1 /*
   2  * Copyright 2010, 2011 IBM Corporation.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is licensed by Oracle to you under the terms of the GNU
   6  * General Public License version 2 only.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 /*
  26  * @test
  27  * @bug 6934356
  28  * @summary Serializing Vector objects which refer to each other should not be able to deadlock.
  29  * @author Neil Richards <neil.richards@ngmr.net>, <neil_richards@uk.ibm.com>
  30  */
  31 
  32 import java.io.ByteArrayOutputStream;
  33 import java.io.IOException;
  34 import java.io.ObjectOutputStream;
  35 import java.io.PrintWriter;
  36 import java.io.Serializable;
  37 import java.io.StringWriter;
  38 import java.util.ArrayList;
  39 import java.util.List;
  40 import java.util.Vector;
  41 import java.util.concurrent.CyclicBarrier;
  42 
  43 public class SerializationDeadlock {
  44     public static void main(final String[] args) throws Exception {
  45         // Test for Vector serialization deadlock
  46         final Vector<Object> v1 = new Vector<>();
  47         final Vector<Object> v2 = new Vector<>();
  48         final TestBarrier testStart = new TestBarrier(3);
  49 
  50         // Populate the vectors so that they refer to each other
  51         v1.add(testStart);
  52         v1.add(v2);
  53         v2.add(testStart);
  54         v2.add(v1);
  55 
  56         final CyclicBarrier testEnd = new CyclicBarrier(3);
  57         final TestThread t1 = new TestThread(v1, testEnd);
  58         final TestThread t2 = new TestThread(v2, testEnd);
  59 
  60         t1.start();
  61         t2.start();
  62 
  63         // Wait for both test threads to have initiated serialization
  64         // of the 'testStart' object (and hence of both 'v1' and 'v2') 
  65         testStart.await();
  66 
  67         // Wait for both test threads to successfully finish serialization
  68         // of 'v1' and 'v2'.
  69         System.out.println("Waiting for Vector serialization to complete ...");
  70         System.out.println("(This test will hang if serialization deadlocks)");
  71         testEnd.await();
  72         System.out.println("Test PASSED: serialization completed successfully");
  73 
  74         TestThread.handleExceptions();
  75     }
  76 
  77     static final class TestBarrier extends CyclicBarrier 
  78             implements Serializable {
  79         public TestBarrier(final int count) {
  80             super(count);
  81         }
  82 
  83         private void writeObject(final ObjectOutputStream oos) 
  84                 throws IOException {
  85             oos.defaultWriteObject();
  86             // Wait until all test threads have started serializing data
  87             try {
  88                 await();
  89             } catch (final Exception e) {
  90                 throw new IOException("Test ERROR: Unexpected exception caught", e);
  91             }
  92         }
  93     }
  94 
  95     static final class TestThread extends Thread {
  96         private static final List<Exception> exceptions = new ArrayList<>();
  97 
  98         private final Vector vector;
  99         private final CyclicBarrier testEnd;
 100 
 101         public TestThread(final Vector vector, final CyclicBarrier testEnd) {
 102             this.vector = vector;
 103             this.testEnd = testEnd;
 104             setDaemon(true);
 105         }
 106 
 107         public void run() {
 108             try {
 109                 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
 110                 final ObjectOutputStream oos = new ObjectOutputStream(baos);
 111 
 112                 oos.writeObject(vector);
 113                 oos.close();
 114             } catch (final IOException ioe) {
 115                 addException(ioe);
 116             } finally {
 117                 try {
 118                     testEnd.await();
 119                 } catch (Exception e) {
 120                     addException(e);
 121                 }
 122             }
 123         }
 124 
 125         private static synchronized void addException(final Exception exception) {
 126             exceptions.add(exception);
 127         }
 128 
 129         public static synchronized void handleExceptions() {
 130             if (false == exceptions.isEmpty()) {
 131                 throw new RuntimeException(getErrorText(exceptions));
 132             }
 133         }
 134 
 135         private static String getErrorText(final List<Exception> exceptions) {
 136             final StringWriter sw = new StringWriter();
 137             final PrintWriter pw = new PrintWriter(sw);
 138 
 139             pw.println("Test ERROR: Unexpected exceptions thrown on test threads:");
 140             for (Exception exception : exceptions) {
 141                 pw.print("\t");
 142                 pw.println(exception);
 143                 for (StackTraceElement element : exception.getStackTrace()) {
 144                     pw.print("\t\tat ");
 145                     pw.println(element);
 146                 }
 147             }
 148 
 149             pw.close();
 150             return sw.toString();
 151         }
 152     }
 153 }
 154