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