Sunbug 6934356: Vector.writeObject() synchronization risks serialization deadlock
Neil Richards
neil.richards at ngmr.net
Mon Dec 13 17:59:57 UTC 2010
Hello.
I have a fix and testcase for problem 6934356 in the Java bug database
- "Vector.writeObject() synchronization risks serialization deadlock".
I've included the 'hg diff -g' output below.
I'm new to OpenJDK - though not to Java SE implementation development
- so hope that this is correct mailing list to ask for the code to be
reviewed.
(Please point me towards a better list if this is not so).
Also, given that this is a reported bug in the Java bug database, I'm
a little confused as to whether I need to additionally raise it in
https://bugs.openjdk.java.net/
And, indeed, whether it is better for me to include the 'hg diff -g'
output inline, as I have done below, or as an attachment, or in some
other fashion.
Basically, any guidance will be gratefully received :-)
Thanks,
Neil
--
Unless stated above:
IBM United Kingdom Limited - Registered in England and Wales with number 741598.
Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU
--
diff --git a/src/share/classes/java/util/Vector.java
b/src/share/classes/java/util/Vector.java
--- a/src/share/classes/java/util/Vector.java
+++ b/src/share/classes/java/util/Vector.java
@@ -1053,10 +1053,20 @@
* is, serialize it). This method is present merely for synchronization.
* It just calls the default writeObject method.
*/
- private synchronized void writeObject(java.io.ObjectOutputStream s)
+ private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException
{
- s.defaultWriteObject();
+ final java.io.ObjectOutputStream.PutField fields = s.putFields();
+ Object[] data = null;
+ synchronized (this) {
+ fields.put("capacityIncrement", capacityIncrement);
+ fields.put("elementCount", elementCount);
+ final int dataLength = elementData.length;
+ data = new Object[dataLength];
+ System.arraycopy(elementData, 0, data, 0, dataLength);
+ }
+ fields.put("elementData", data);
+ s.writeFields();
}
/**
diff --git a/test/java/util/Vector/SerializationDeadlock.java
b/test/java/util/Vector/SerializationDeadlock.java
new file mode 100644
--- /dev/null
+++ b/test/java/util/Vector/SerializationDeadlock.java
@@ -0,0 +1,111 @@
+/*
+ * @test
+ * @bug 6934356
+ * @summary Serializing Vector objects which refer to each other
should not be able to deadlock.
+ * @author Neil Richards <neil.richards at ngmr.net>
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Vector;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+
+public class SerializationDeadlock {
+ public static void main(final String[] args) {
+ // Test for Vector serialization deadlock
+ final Vector<Object> v1 = new Vector<Object>();
+ final Vector<Object> v2 = new Vector<Object>();
+ final TestBarrier testStart = new TestBarrier(3);
+
+ v1.add(testStart);
+ v1.add(v2);
+ v2.add(testStart);
+ v2.add(v1);
+
+ final CyclicBarrier testEnd = new CyclicBarrier(3);
+ final TestThread t1 = new TestThread(v1, testEnd);
+ final TestThread t2 = new TestThread(v2, testEnd);
+
+ t1.start();
+ t2.start();
+
+ try {
+ testStart.await();
+ testEnd.await(1, TimeUnit.SECONDS);
+ } catch (TimeoutException te) {
+ throw new RuntimeException("Test FAILED: Probable serialization
deadlock detected");
+ } catch (Exception e) {
+ throw new RuntimeException("Test ERROR: Unexpected exception caught", e);
+ }
+
+ TestThread.handleExceptions();
+ }
+
+ static final class TestBarrier extends CyclicBarrier
+ implements Serializable {
+ public TestBarrier(final int count) {
+ super(count);
+ }
+
+ private void writeObject(final ObjectOutputStream oos)
+ throws IOException {
+ oos.defaultWriteObject();
+ // Wait until all TestThreads have started serializing data
+ try {
+ await();
+ } catch (final Exception e) {
+ throw new IOException("Test ERROR: Unexpected
exception caught", e);
+ }
+ }
+ }
+
+ static final class TestThread extends Thread {
+ private static final ArrayList<Exception> exceptions = new
ArrayList<Exception>();
+
+ private final Vector vector;
+ private final CyclicBarrier testEnd;
+
+ public TestThread(final Vector vector, final CyclicBarrier testEnd) {
+ this.vector = vector;
+ this.testEnd = testEnd;
+ setDaemon(true);
+ }
+
+ public void run() {
+ try {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final ObjectOutputStream oos = new ObjectOutputStream(baos);
+
+ oos.writeObject(vector);
+ oos.close();
+ } catch (final IOException ioe) {
+ addException(ioe);
+ } finally {
+ try {
+ testEnd.await();
+ } catch (Exception e) {
+ addException(e);
+ }
+ }
+ }
+
+ private static synchronized void addException(final Exception
exception) {
+ exceptions.add(exception);
+ }
+
+ public static synchronized void handleExceptions() {
+ if (false == exceptions.isEmpty()) {
+ for (Exception exception : exceptions) {
+ exception.printStackTrace();
+ }
+ throw new RuntimeException("Test ERROR: Unexpected exceptions
thrown on test threads - see stderr for details");
+ }
+ }
+ }
+}
+
More information about the core-libs-dev
mailing list