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