JDK-6927486 : Hashtable.writeObject() synchronization risks serialization deadlock
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:collections
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2010-02-18
  • Updated: 2021-03-03
  • Resolved: 2011-04-06
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b132Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
NOTE
----
This issue was previously investigated under CR 5085747, but it was closed
due to a lack of sufficient information or testcase.  A Licensee has provided
us with with a testcase in hopes of us picking up on a fresh investigation.
Please cross-refernce the related bug id's to supplement this new investigation.

OPERATING SYSTEM
----------------
All

FULL JDK VERSION
----------------
All current JDKs, including 7-ea-b83, 6u18, 5.0u23, 1.4.2_25

DESCRIPTION
-----------
See CR 5085747 Description, comments, eval. 

REPRODUCTION INSTRUCTIONS
-------------------------
1. Compile and run attached testcase, HTProblem.java.
2. Wait until "Deadlock" message is printed.
3. Gather a thread dump using CTRL-BREAK or "kill -3".
4. Observe the deadlock below:

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x0aab8654 (object 0x029d35b0, a java.util.Hashtable),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x0aaf5a24 (object 0x029d3610, a java.util.Hashtable),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at java.util.Hashtable.writeObject(Hashtable.java:814)
        - waiting to lock <0x029d35b0> (a java.util.Hashtable)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
        at MyObject.writeObject(HTProblem.java:48)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
        at java.util.Hashtable.writeObject(Hashtable.java:824)
        - locked <0x029d3610> (a java.util.Hashtable)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
        at MyThread.run(HTProblem.java:68)
"Thread-0":
        at java.util.Hashtable.writeObject(Hashtable.java:814)
        - waiting to lock <0x029d3610> (a java.util.Hashtable)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
        at MyObject.writeObject(HTProblem.java:48)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
        at java.util.Hashtable.writeObject(Hashtable.java:824)
        - locked <0x029d35b0> (a java.util.Hashtable)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
        at MyThread.run(HTProblem.java:68)

Found 1 deadlock.


TESTCASE SOURCE
---------------
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.FileOutputStream;
import java.util.Hashtable;

public class HTProblem {
 public static void main(String args[]) {
	 MyObject mo1=new MyObject();
	 MyObject mo2=new MyObject();
	 Hashtable ht1=new Hashtable();
	 Hashtable ht2=new Hashtable();
	 mo1.ht=ht2;
	 mo1.sleepTime=100;
	 mo2.ht=ht1;
	 mo2.sleepTime=100;
	 ht1.put("key",mo1);
	 ht2.put("key",mo2);
	 MyThread t1=new MyThread(ht1,"file1");
	 MyThread t2=new MyThread(ht2,"file2");
	 t1.start();
	 t2.start();
	try {
		Thread.sleep(5000);
	} catch (Exception e) {
		e.printStackTrace();
	}
	if(t1.isAlive() && t2.isAlive()) {
		System.out.println("Deadlock");
	}
}
 
}

class MyObject implements Serializable{
	public transient Hashtable ht;
	public transient long sleepTime;
	
    private void writeObject(java.io.ObjectOutputStream s)
    throws IOException
	{
		try {
			Thread.sleep(sleepTime);
		} catch (Exception e) {
		}
		s.writeObject(ht);
}

    private void readObject(java.io.ObjectInputStream s)
    throws IOException, ClassNotFoundException
	{
		ht = (Hashtable)s.readObject();
	}
}

class MyThread extends Thread {
	Hashtable ht;
	String file;
	public MyThread(Hashtable ht,String file){
		this.ht=ht;
		this.file=file;
	}
	public void run() {
		try {
			ObjectOutput oo=new ObjectOutputStream(new FileOutputStream(file));
			oo.writeObject(ht);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

Comments
PUBLIC COMMENTS Fix contributed by Neil Richards of IBM UK
15-02-2011

SUGGESTED FIX see webrev provided by Neil Richards of IBM: http://cr.openjdk.java.net/~mduigou/6927486.1/webrev/
11-01-2011

EVALUATION Hashtable.writeObject() is synchronized. When two threads are serializing two hashtables (ht1 and ht2 respectively), both threads are holding the monitor of ht1 and ht2 respectively. If an element in one hashtable (ht1) contains a reference to another hashtable (ht2), serializing ht1 will call ht2.writeObject() which attempts to acquire ht2 while serializing ht2 will call ht1.writeObject() which attempts to acquire ht1, that results in a deadlock. See also 6934356.
28-07-2010