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
35import java.io.ByteArrayOutputStream;
36import java.io.IOException;
37import java.io.ObjectOutputStream;
38import java.io.PrintWriter;
39import java.io.Serializable;
40import java.io.StringWriter;
41import java.util.ArrayList;
42import java.util.Hashtable;
43import java.util.List;
44import java.util.concurrent.CyclicBarrier;
45
46public class SerializationDeadlock {
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 {
80        public TestBarrier(final int count) {
81            super(count);
82        }
83
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
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
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
127        private static synchronized void addException(final Exception exception) {
128            exceptions.add(exception);
129        }
130
131        public static synchronized void handleExceptions() {
132            if (false == exceptions.isEmpty()) {
133                throw new RuntimeException(getErrorText(exceptions));
134            }
135        }
136
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