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 6927486
  28  * @summary Serializing Hashtable 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.Hashtable;
  40 import java.util.List;
  41 import java.util.concurrent.CyclicBarrier;
  42 
  43 public class SerializationDeadlock {
  44     public static void main(final String[] args) throws Exception {
  45         // Test for Hashtable serialization deadlock
  46         final Hashtable<Object, Object> h1 = new Hashtable<>();
  47         final Hashtable<Object, Object> h2 = new Hashtable<>();
  48         final TestBarrier testStart = new TestBarrier(3);
  49 
  50         // Populate the hashtables so that they refer to each other
  51         h1.put(testStart, h2);
  52         h2.put(testStart, h1);
  53 
  54         final CyclicBarrier testEnd = new CyclicBarrier(3);
  55         final TestThread t1 = new TestThread(h1, testEnd);
  56         final TestThread t2 = new TestThread(h2, testEnd);
  57 
  58         t1.start();
  59         t2.start();
  60 
  61         // Wait for both test threads to have initiated serialization
  62         // of the 'testStart' object (and hence of both 'h1' and 'h2') 
  63         testStart.await();
  64 
  65         // Wait for both test threads to successfully finish serialization
  66         // of 'h1' and 'h2'.
  67         System.out.println("Waiting for Hashtable serialization to complete ...");
  68         System.out.println("(This test will hang if serialization deadlocks)");
  69         testEnd.await();
  70         System.out.println("Test PASSED: serialization completed successfully");
  71 
  72         TestThread.handleExceptions();
  73     }
  74 
  75     static final class TestBarrier extends CyclicBarrier 
  76     implements Serializable {
  77         public TestBarrier(final int count) {
  78             super(count);
  79         }
  80 
  81         private void writeObject(final ObjectOutputStream oos) 
  82         throws IOException {
  83             oos.defaultWriteObject();
  84             // Wait until all test threads have started serializing data
  85             try {
  86                 await();
  87             } catch (final Exception e) {
  88                 throw new IOException("Test ERROR: Unexpected exception caught", e);
  89             }
  90         }
  91     }
  92 
  93     static final class TestThread extends Thread {
  94         private static final List<Exception> exceptions = new ArrayList<>();
  95 
  96         private final Hashtable<Object, Object> hashtable;
  97         private final CyclicBarrier testEnd;
  98 
  99         public TestThread(final Hashtable<Object, Object> hashtable,
 100                 final CyclicBarrier testEnd) {
 101             this.hashtable = hashtable;
 102             this.testEnd = testEnd;
 103             setDaemon(true);
 104         }
 105 
 106         public void run() {
 107             try {
 108                 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
 109                 final ObjectOutputStream oos = new ObjectOutputStream(baos);
 110 
 111                 oos.writeObject(hashtable);
 112                 oos.close();
 113             } catch (final IOException ioe) {
 114                 addException(ioe);
 115             } finally {
 116                 try {
 117                     testEnd.await();
 118                 } catch (Exception e) {
 119                     addException(e);
 120                 }
 121             }
 122         }
 123 
 124         private static synchronized void addException(final Exception exception) {
 125             exceptions.add(exception);
 126         }
 127 
 128         public static synchronized void handleExceptions() {
 129             if (false == exceptions.isEmpty()) {
 130                 throw new RuntimeException(getErrorText(exceptions));
 131             }
 132         }
 133 
 134         private static String getErrorText(final List<Exception> exceptions) {
 135             final StringWriter sw = new StringWriter();
 136             final PrintWriter pw = new PrintWriter(sw);
 137 
 138             pw.println("Test ERROR: Unexpected exceptions thrown on test threads:");
 139             for (Exception exception : exceptions) {
 140                 pw.print("\t");
 141                 pw.println(exception);
 142                 for (StackTraceElement element : exception.getStackTrace()) {
 143                     pw.print("\t\tat ");
 144                     pw.println(element);
 145                 }
 146             }
 147 
 148             pw.close();
 149             return sw.toString();
 150         }
 151     }
 152 }
 153