Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4291993

deadlock in javax.swing.UIDefaults.get()

    XMLWordPrintable

Details

    Description

      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)

      Attachments

        Issue Links

          Activity

            People

              svioletsunw Scott Violet (Inactive)
              elatkinsunw Eugene Latkin (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: