JDK-4880633 : XMLEncoder not thread safe
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 1.4.0,1.4.1
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux,windows_xp
  • CPU: x86
  • Submitted: 2003-06-18
  • Updated: 2003-09-26
  • Resolved: 2003-09-26
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.
Other
1.4.2_09Fixed
Related Reports
Relates :  
Description

Name: gm110360			Date: 06/18/2003


FULL PRODUCT VERSION :
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows XP [Version 5.1.2600]

EXTRA RELEVANT SYSTEM CONFIGURATION :
Dual processor Machine

A DESCRIPTION OF THE PROBLEM :
When execution XMLEncoder.writeObject(Object .. )
with multiple threads on a dual processor box a lock
occures.

Diffrent version of the object is also written.
(Warning NullPointerException written randomly)even single
processor machine with multiple threads

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.Compile example files(2)
2.Execute example
3.Run few times till lock occurs.
4.Do thread dump
5.Lock occurs in class java.beans.Statement when calling
static variable methodCache (HashMap) that is not
synchronized.

EXPECTED VERSUS ACTUAL BEHAVIOR :
Application should exit normally but 1 or more threads lock.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Example thread dump:


Full thread dump Java HotSpot(TM) Client VM (1.4.0_03-b04 mixed mode):

"Thread-3" prio=5 tid=0x000344D8 nid=0xc44 waiting on monitor [0..7fb14]

"Bug Thread1" prio=5 tid=0x00A423B0 nid=0x7f4 runnable [ae9f000..ae9fdb4]
        at java.util.HashMap.get(HashMap.java:316)
        at java.beans.Statement.getMethod(Statement.java:360)
        at java.beans.Statement.invoke(Statement.java:464)
        at java.beans.Statement.execute(Statement.java:119)
        at java.beans.Encoder.execute(Encoder.java:94)
        at java.beans.Encoder.writeStatement(Encoder.java:238)
        at java.beans.XMLEncoder.writeStatement(XMLEncoder.java:320)
        at java.beans.DefaultPersistenceDelegate.invokeStatement
(DefaultPersistenceDelegate.java:242)
        at java.beans.DefaultPersistenceDelegate.doProperty
(DefaultPersistenceDelegate.java:236)
        at java.beans.DefaultPersistenceDelegate.initBean
(DefaultPersistenceDelegate.java:254)
        at java.beans.DefaultPersistenceDelegate.initialize
(DefaultPersistenceDelegate.java:395)
        at java.beans.PersistenceDelegate.writeObject
(PersistenceDelegate.java:103)
        at java.beans.Encoder.writeObject(Encoder.java:55)
        at java.beans.XMLEncoder.writeObject(XMLEncoder.java:250)
        at java.beans.Encoder.writeExpression(Encoder.java:260)
        at java.beans.XMLEncoder.writeExpression(XMLEncoder.java:351)
        at java.beans.PersistenceDelegate.writeObject
(PersistenceDelegate.java:100)
        at java.beans.Encoder.writeObject(Encoder.java:55)
        at java.beans.XMLEncoder.writeObject(XMLEncoder.java:250)
        at java.beans.Encoder.writeObject1(Encoder.java:192)
        at java.beans.Encoder.cloneStatement(Encoder.java:205)
        at java.beans.Encoder.writeStatement(Encoder.java:236)
        at java.beans.XMLEncoder.writeStatement(XMLEncoder.java:320)
        at java.beans.XMLEncoder.writeObject(XMLEncoder.java:253)
        at XMLEncoderBug$OwnEncoder.run(XMLEncoderBug.java:180)

"Bug Thread0" prio=5 tid=0x00A42E98 nid=0xda8 runnable [ae5f000..ae5fdb4]
        at java.util.HashMap.get(HashMap.java:316)
        at java.beans.Statement.getMethod(Statement.java:360)
        at java.beans.Statement.invoke(Statement.java:464)
        at java.beans.Statement.execute(Statement.java:119)
        at java.beans.Encoder.execute(Encoder.java:94)
        at java.beans.Encoder.writeStatement(Encoder.java:238)
        at java.beans.XMLEncoder.writeStatement(XMLEncoder.java:320)
        at java.beans.DefaultPersistenceDelegate.invokeStatement
(DefaultPersistenceDelegate.java:242)
        at java.beans.DefaultPersistenceDelegate.doProperty
(DefaultPersistenceDelegate.java:236)
        at java.beans.DefaultPersistenceDelegate.initBean
(DefaultPersistenceDelegate.java:254)
        at java.beans.DefaultPersistenceDelegate.initialize
(DefaultPersistenceDelegate.java:395)
        at java.beans.PersistenceDelegate.writeObject
(PersistenceDelegate.java:103)
        at java.beans.Encoder.writeObject(Encoder.java:55)
        at java.beans.XMLEncoder.writeObject(XMLEncoder.java:250)
        at java.beans.Encoder.writeExpression(Encoder.java:260)
        at java.beans.XMLEncoder.writeExpression(XMLEncoder.java:351)
        at java.beans.PersistenceDelegate.writeObject
(PersistenceDelegate.java:100)
        at java.beans.Encoder.writeObject(Encoder.java:55)
        at java.beans.XMLEncoder.writeObject(XMLEncoder.java:250)
        at java.beans.Encoder.writeObject1(Encoder.java:192)
        at java.beans.Encoder.cloneStatement(Encoder.java:205)
        at java.beans.Encoder.writeStatement(Encoder.java:236)
        at java.beans.XMLEncoder.writeStatement(XMLEncoder.java:320)
        at java.beans.XMLEncoder.writeObject(XMLEncoder.java:253)
        at XMLEncoderBug$OwnEncoder.run(XMLEncoderBug.java:180)

"Signal Dispatcher" daemon prio=10 tid=0x009A5358 nid=0x70c waiting on monitor
[0..0]

"Finalizer" daemon prio=9 tid=0x0AA60EB8 nid=0xa54 waiting on monitor
[ad1f000..ad1fdb4]
        at java.lang.Object.wait(Native Method)
        - waiting on <02F63CF8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:111)
        - locked <02F63CF8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:127)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)

"Reference Handler" daemon prio=10 tid=0x0AA602A0 nid=0x6f4 waiting on monitor
[acdf000..acdfdb4]
        at java.lang.Object.wait(Native Method)
        - waiting on <02F63A70> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:426)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:113)
        - locked <02F63A70> (a java.lang.ref.Reference$Lock)

"VM Thread" prio=5 tid=0x009D7A98 nid=0xcd4 runnable

"VM Periodic Task Thread" prio=10 tid=0x009A4028 nid=0xe40 waiting on monitor
"Suspend Checker Thread" prio=10 tid=0x009A48F8 nid=0x680 runnable


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
Test case in comments section...
---------- END SOURCE ----------
(Review ID: 181175) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger FIXED IN: tiger INTEGRATED IN: tiger tiger-b22
14-06-2004

SUGGESTED FIX The fix for that bug is in the XMLEncoder class in which it uses a per instance NameGenerator, internal persistence delegate access in Meta data is syncronized and the biggest fix is in the new ReflectionUtils class - most of the methods and structures are from Statement. Syncronized access is done to the method cache and it will be collected when memory becomes low. There is also a considerable amount of refactoring: - Consolidation of 4 implementations of "capitalize" into 1 in NameGenerator. - Replaced package private IdentityHashtable with java.util.IdentityHashMap. - Consolidated reflection utilities into ReflectionUtils - Removed a lot of one line methods. There is only one issue: now that Statement.instanceName doesn't use the name generator, debugging output will not provide as much detail upon failure. This is a small issue and only really affects annonymous inner classes. I spent the entire day trying to figure this one out using ThreadLocal and other means and I couldn't find a thead safe way to resolve this. Since this is mostly for debugging, a better solution would be to implement logging. ###@###.### 2003-09-19
19-09-2003

EVALUATION I ran the test case on a dual processor machine and I'm getting the deadlock. I modified Statement.getMethod() to be synchronized and it seemed to have fixed the problem but I'm sure it has affected performance. Since multi-threaded access to the XMLEncoder is an unconventional way of using the encoder I don't think that this is the proper solution since it may penalize other more conventional users of this API. Perhaps the best way to address this problem is to provide a function that will enable thread safety using a flag. I still haven't investigated the NPE. ###@###.### 2003-06-25 The NPE is still part of the same synchronization bug. This problem went away when the getMethod, setCaching and isCaching methods are synchronized. Will fix for Tiger. ###@###.### 2003-08-07 AFter discussing this issue with the original architect, we should get rid of the setCaching private method and leave caching on all the time. ###@###.### 2003-09-09 Implemented the solution with a syncronized getMethod and the test case will exectute correctly - most of the time. 1/5 it will fail because of an IllegalAccessException: Caused by: java.lang.IllegalAccessException: Can not call newInstance() on the Class for java.lang.Class at java.lang.Class.newInstance0(Class.java:273) at java.lang.Class.newInstance(Class.java:261) 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:324) at java.beans.Statement.invoke(Statement.java:441) at java.beans.Expression.getValue(Expression.java:101) at java.beans.Encoder.getValue(Encoder.java:84) The scope of this bug will broaden to fix all multi-threaded issues with the encoder rather than the one presented in the test case. Spectifically, NameGenerator.clear and instanceName is not thread safe. ###@###.### 2003-09-12
12-09-2003