-
Bug
-
Resolution: Duplicate
-
P4
-
None
-
1.3.0
-
generic
-
generic
The method javax.swing.UIDefaults.get(Object) may cause deadlock
in multi-thread application.
There are two 'synchronized' blocks in the method's code (displayed
below). I particular, those blocks of code process the case if 'key'
is assigned to 'LazyValue' which currently has 'PENDING' status:
- the 1st block: waits while the 'PENDING' value be prepared
by some other Java-thread just exploring it,
- and the 2nd block: prepares 'PENDING' value and notifies other
threads when the value is ready.
However, deadlock is possible between these two blocks of code:
Suppose, that some thread (let's denote it thread 'A') executes the
line (marked below):
super.set(key,PENDING)
and quits from the 1st synchronized block.
At that moment, other thread (the thread 'B') may bring into the 1st
syncronized block for the same UIDefaults instance and the same key.
Since the status of the corresponding value is 'PENDING', that thread
will never leave the synchronized block.
The thread 'B' will prevent the thread 'A' from getting into the 2nd
syncronized block, so that the thread 'A' will never have a chance to
process that LazyValue and remove its 'PENDING' status.
At that time, many other threads may hang awaiting for permission
to get into the 1st synchronized block.
/**
* Returns the value for key. If the value is a
* <code>UIDefaults.LazyValue</code> then the real
* value is computed with <code>LazyValue.createValue()</code>,
* the table entry is replaced, and the real value is returned.
* If the value is an <code>UIDefaults.ActiveValue</code>
* the table entry is not replaced - the value is computed
* with ActiveValue.createValue() for each get() call.
*
* @see LazyValue
* @see ActiveValue
* @see java.util.Hashtable#get
*/
public Object get(Object key)
{
/* Quickly handle the common case, without grabbing
* a lock.
*/
Object value = super.get(key);
if ((value != PENDING) &&
!(value instanceof ActiveValue) &&
!(value instanceof LazyValue)) {
return value;
}
/* If the LazyValue for key is being constructed by another
* thread then wait and then return the new value, otherwise drop
* the lock and construct the ActiveValue or the LazyValue.
* We use the special value PENDING to mark LazyValues that
* are being constructed.
*/
+----> synchronized(this) {
| value = super.get(key);
| if (value == PENDING) {
| do {
| try {
| this.wait();
| }
| catch (InterruptedException e) {
| }
| value = super.get(key);
| }
| while(value == PENDING);
| return value;
| }
| else if (value instanceof LazyValue) {
+---- super.put(key, PENDING);
| }
| else if (!(value instanceof ActiveValue)) {
| return value;
| }
| }
|
| /* At this point we know that the value of key was
| * a LazyValue or an ActiveValue.
| */
| if (value instanceof LazyValue) {
| try {
| /* If an exception is thrown we'll just put the LazyValue
| * back in the table.
| */
| value = ((LazyValue)value).createValue(this);
| }
| finally {
+-------> synchronized(this) {
if (value == null) {
super.remove(key);
}
else {
super.put(key, value);
}
this.notify();
}
}
}
else {
value = ((ActiveValue)value).createValue(this);
}
return value;
}
Indeed, probability of scenario situation seems low; but it increases
dramatically if there are many threads exploring UIDefaults.
In particular, the JCK-stress test 'jck12a017' oftenly hangs due
to this scenario. (This test tries to execute 405 of JCK 1.2a
API/swing tests simultaneously in concurent threads.)
The stress test 'jck12a017' belongs to the 'testbase_nsk' testbase, and its
sources could be found in the directory:
/net/sqesvr/vsn/testbase/testbase_nsk/src/nsk/stress/jck12a/jck12a017
To execute this test, please follow these instructions:
1. Create some temporary directory, to say C:\TEMP\jck12a017,
and make this directory your current directory:
cd C:\TEMP\jck12a017
2. Copy the test's sources into this directory. You need to copy
only the file 'jck12a017.java', and the files 'jck12a017.cfg'
and 'jck12a017.README' are optional.
3. Also copy into the current directory the stress-wrapper needed to
execute the test. The wrapper is the 'StressTest.java' file found
in the directory:
/net/sqesvr/vsn/testbase/testbase_nsk/src/stress/share
4. Create 'classes' subdirectory for the wrapper's class-files you will
need to compile:
mkdir classes
5. Mount to the drive L: the NFS directory:
/usr/local/java
(it is available as \\grinder\local-java for NT machines)
6. Setup the CLASSPATH to provide the test with JavaTest 2.0 tool
and JCK 1.2a pre-compiled API tests:
set CLASSPATH=.;.\classes;L:\sqe-tools2.0\javatest.jar;L:\jck1.2\JCK-runtime-api-12a\classes
7. Compile the wrapper with JDK 1.2, JDK 1.2.2 or JDK 1.3 compiler:
javac -d .\classes StressTest.java
8. Compile the test with JDK 1.2, JDK 1.2.2 or JDK 1.3 compiler:
javac jck12a017.java
9. Execute the test under debugger:
jdb -J-classic jck12a017
>run
(Please, turn off HotSpot, because it crashes under this test: see bug #4288475)
10. When the test hangs after 3-5 minutes, see list of deadlocked threads
and where they are hanged, e.g.:
>threads
Group main:
(java.lang.Thread)0x1
(javasoft.sqe.stresstest.StressTest$TestThread)0x384
(javasoft.sqe.stresstest.StressTest$TestThread)0x389
(javasoft.sqe.stresstest.StressTest$TestThread)0x45e
(javasoft.sqe.stresstest.StressTest$TestThread)0x491
(javasoft.sqe.stresstest.StressTest$TestThread)0x492
(javasoft.sqe.stresstest.StressTest$TestThread)0x499
(javasoft.sqe.stresstest.StressTest$TestThread)0x49c
(javasoft.sqe.stresstest.StressTest$TestThread)0x4da
(javasoft.sqe.stresstest.StressTest$TestThread)0x4dc
(javasoft.sqe.stresstest.StressTest$TestThread)0x4f0
(javasoft.sqe.stresstest.StressTest$TestThread)0x4f1
(javasoft.sqe.stresstest.StressTest$TestThread)0x503
(javasoft.sqe.stresstest.StressTest$TestThread)0x505
(java.awt.EventDispatchThread)0x6b5
(sun.awt.PostEventQueue)0x6b8
(java.lang.Thread)0x6bd
(java.lang.Thread)0x75d
(javasoft.sqe.tests.api.javax.swing.text.AbstractDocument.MyThread)0xa2d
(sun.awt.ScreenUpdater)0x978
>suspend
>where 0x384
0x384:
[1] java.lang.Object.wait (native method)
[2] java.lang.Object.wait (Object.java:424)
[3] javax.swing.UIDefaults.get (UIDefaults.java:119) -- this.wait() -- 1st syncronized block --
[4] javax.swing.MultiUIDefaults.get (MultiUIDefaults.java:50)
[5] javax.swing.UIDefaults.getBorder (UIDefaults.java:245)
[6] javax.swing.UIManager.getBorder (UIManager.java:490)
[7] javax.swing.LookAndFeel.installBorder (LookAndFeel.java:111)
[8] javax.swing.plaf.basic.BasicButtonUI.installDefaults (BasicButtonUI.java:132)
[9] javax.swing.plaf.metal.MetalButtonUI.installDefaults (MetalButtonUI.java:58)
[10] javax.swing.plaf.basic.BasicButtonUI.installUI (BasicButtonUI.java:67)
[11] javax.swing.JComponent.setUI (JComponent.java:326)
[12] javax.swing.AbstractButton.setUI (AbstractButton.java:1147)
[13] javax.swing.JButton.updateUI (JButton.java:122)
[14] javax.swing.AbstractButton.init (AbstractButton.java:1423)
[15] javax.swing.JButton.<init> (JButton.java:112)
[16] javax.swing.JButton.<init> (JButton.java:87)
[17] javasoft.sqe.tests.api.javax.swing.border.BevelBorder.getXXXTests.BevelBorder2006 (getXXXTests.java:91)
[18] java.lang.reflect.Method.invoke (native method)
[19] javasoft.sqe.jck.lib.MultiTest.run (MultiTest.java:137)
[20] javasoft.sqe.stresstest.StressTest$TestThread.run (StressTest.java:723)
in multi-thread application.
There are two 'synchronized' blocks in the method's code (displayed
below). I particular, those blocks of code process the case if 'key'
is assigned to 'LazyValue' which currently has 'PENDING' status:
- the 1st block: waits while the 'PENDING' value be prepared
by some other Java-thread just exploring it,
- and the 2nd block: prepares 'PENDING' value and notifies other
threads when the value is ready.
However, deadlock is possible between these two blocks of code:
Suppose, that some thread (let's denote it thread 'A') executes the
line (marked below):
super.set(key,PENDING)
and quits from the 1st synchronized block.
At that moment, other thread (the thread 'B') may bring into the 1st
syncronized block for the same UIDefaults instance and the same key.
Since the status of the corresponding value is 'PENDING', that thread
will never leave the synchronized block.
The thread 'B' will prevent the thread 'A' from getting into the 2nd
syncronized block, so that the thread 'A' will never have a chance to
process that LazyValue and remove its 'PENDING' status.
At that time, many other threads may hang awaiting for permission
to get into the 1st synchronized block.
/**
* Returns the value for key. If the value is a
* <code>UIDefaults.LazyValue</code> then the real
* value is computed with <code>LazyValue.createValue()</code>,
* the table entry is replaced, and the real value is returned.
* If the value is an <code>UIDefaults.ActiveValue</code>
* the table entry is not replaced - the value is computed
* with ActiveValue.createValue() for each get() call.
*
* @see LazyValue
* @see ActiveValue
* @see java.util.Hashtable#get
*/
public Object get(Object key)
{
/* Quickly handle the common case, without grabbing
* a lock.
*/
Object value = super.get(key);
if ((value != PENDING) &&
!(value instanceof ActiveValue) &&
!(value instanceof LazyValue)) {
return value;
}
/* If the LazyValue for key is being constructed by another
* thread then wait and then return the new value, otherwise drop
* the lock and construct the ActiveValue or the LazyValue.
* We use the special value PENDING to mark LazyValues that
* are being constructed.
*/
+----> synchronized(this) {
| value = super.get(key);
| if (value == PENDING) {
| do {
| try {
| this.wait();
| }
| catch (InterruptedException e) {
| }
| value = super.get(key);
| }
| while(value == PENDING);
| return value;
| }
| else if (value instanceof LazyValue) {
+---- super.put(key, PENDING);
| }
| else if (!(value instanceof ActiveValue)) {
| return value;
| }
| }
|
| /* At this point we know that the value of key was
| * a LazyValue or an ActiveValue.
| */
| if (value instanceof LazyValue) {
| try {
| /* If an exception is thrown we'll just put the LazyValue
| * back in the table.
| */
| value = ((LazyValue)value).createValue(this);
| }
| finally {
+-------> synchronized(this) {
if (value == null) {
super.remove(key);
}
else {
super.put(key, value);
}
this.notify();
}
}
}
else {
value = ((ActiveValue)value).createValue(this);
}
return value;
}
Indeed, probability of scenario situation seems low; but it increases
dramatically if there are many threads exploring UIDefaults.
In particular, the JCK-stress test 'jck12a017' oftenly hangs due
to this scenario. (This test tries to execute 405 of JCK 1.2a
API/swing tests simultaneously in concurent threads.)
The stress test 'jck12a017' belongs to the 'testbase_nsk' testbase, and its
sources could be found in the directory:
/net/sqesvr/vsn/testbase/testbase_nsk/src/nsk/stress/jck12a/jck12a017
To execute this test, please follow these instructions:
1. Create some temporary directory, to say C:\TEMP\jck12a017,
and make this directory your current directory:
cd C:\TEMP\jck12a017
2. Copy the test's sources into this directory. You need to copy
only the file 'jck12a017.java', and the files 'jck12a017.cfg'
and 'jck12a017.README' are optional.
3. Also copy into the current directory the stress-wrapper needed to
execute the test. The wrapper is the 'StressTest.java' file found
in the directory:
/net/sqesvr/vsn/testbase/testbase_nsk/src/stress/share
4. Create 'classes' subdirectory for the wrapper's class-files you will
need to compile:
mkdir classes
5. Mount to the drive L: the NFS directory:
/usr/local/java
(it is available as \\grinder\local-java for NT machines)
6. Setup the CLASSPATH to provide the test with JavaTest 2.0 tool
and JCK 1.2a pre-compiled API tests:
set CLASSPATH=.;.\classes;L:\sqe-tools2.0\javatest.jar;L:\jck1.2\JCK-runtime-api-12a\classes
7. Compile the wrapper with JDK 1.2, JDK 1.2.2 or JDK 1.3 compiler:
javac -d .\classes StressTest.java
8. Compile the test with JDK 1.2, JDK 1.2.2 or JDK 1.3 compiler:
javac jck12a017.java
9. Execute the test under debugger:
jdb -J-classic jck12a017
>run
(Please, turn off HotSpot, because it crashes under this test: see bug #4288475)
10. When the test hangs after 3-5 minutes, see list of deadlocked threads
and where they are hanged, e.g.:
>threads
Group main:
(java.lang.Thread)0x1
(javasoft.sqe.stresstest.StressTest$TestThread)0x384
(javasoft.sqe.stresstest.StressTest$TestThread)0x389
(javasoft.sqe.stresstest.StressTest$TestThread)0x45e
(javasoft.sqe.stresstest.StressTest$TestThread)0x491
(javasoft.sqe.stresstest.StressTest$TestThread)0x492
(javasoft.sqe.stresstest.StressTest$TestThread)0x499
(javasoft.sqe.stresstest.StressTest$TestThread)0x49c
(javasoft.sqe.stresstest.StressTest$TestThread)0x4da
(javasoft.sqe.stresstest.StressTest$TestThread)0x4dc
(javasoft.sqe.stresstest.StressTest$TestThread)0x4f0
(javasoft.sqe.stresstest.StressTest$TestThread)0x4f1
(javasoft.sqe.stresstest.StressTest$TestThread)0x503
(javasoft.sqe.stresstest.StressTest$TestThread)0x505
(java.awt.EventDispatchThread)0x6b5
(sun.awt.PostEventQueue)0x6b8
(java.lang.Thread)0x6bd
(java.lang.Thread)0x75d
(javasoft.sqe.tests.api.javax.swing.text.AbstractDocument.MyThread)0xa2d
(sun.awt.ScreenUpdater)0x978
>suspend
>where 0x384
0x384:
[1] java.lang.Object.wait (native method)
[2] java.lang.Object.wait (Object.java:424)
[3] javax.swing.UIDefaults.get (UIDefaults.java:119) -- this.wait() -- 1st syncronized block --
[4] javax.swing.MultiUIDefaults.get (MultiUIDefaults.java:50)
[5] javax.swing.UIDefaults.getBorder (UIDefaults.java:245)
[6] javax.swing.UIManager.getBorder (UIManager.java:490)
[7] javax.swing.LookAndFeel.installBorder (LookAndFeel.java:111)
[8] javax.swing.plaf.basic.BasicButtonUI.installDefaults (BasicButtonUI.java:132)
[9] javax.swing.plaf.metal.MetalButtonUI.installDefaults (MetalButtonUI.java:58)
[10] javax.swing.plaf.basic.BasicButtonUI.installUI (BasicButtonUI.java:67)
[11] javax.swing.JComponent.setUI (JComponent.java:326)
[12] javax.swing.AbstractButton.setUI (AbstractButton.java:1147)
[13] javax.swing.JButton.updateUI (JButton.java:122)
[14] javax.swing.AbstractButton.init (AbstractButton.java:1423)
[15] javax.swing.JButton.<init> (JButton.java:112)
[16] javax.swing.JButton.<init> (JButton.java:87)
[17] javasoft.sqe.tests.api.javax.swing.border.BevelBorder.getXXXTests.BevelBorder2006 (getXXXTests.java:91)
[18] java.lang.reflect.Method.invoke (native method)
[19] javasoft.sqe.jck.lib.MultiTest.run (MultiTest.java:137)
[20] javasoft.sqe.stresstest.StressTest$TestThread.run (StressTest.java:723)
- duplicates
-
JDK-4327067 Potential hang in multithreaded calls to UIDefaults.get()
-
- Resolved
-
- relates to
-
JDK-4288475 assert(test != 0, "Unexpected object header as 0"); -Xcomp
-
- Resolved
-