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

AWT deadlocking with WComponentPeer and WMenuItemPeer finalize methods

XMLWordPrintable

    • b01
    • x86
    • windows_nt
    • Not verified

      Here is a problem that we've been experiencing and we've finally tracked down the source of
      it. Unfortunately it (again) requires a fairly complicated testcase and doesn't always
      occur - you'll see why when we explain why it appears to be occurring.

      If you need us to provide the testcase for this (after you've read the logical explanation)
      then please feel free to contact us.

      The short of it is that it looks like you'll be needing to modify WComponentPeer.java and
      WMenuItemPeer.java so that their finalize methods don't deal with the Hashtable (since
      they should have been disposed elsewhere) - we haven't tested this yet...


      Short Problem Description:
      --------------------------

      The Windows AWT implementation, and potentially others based upon
      SunToolkit.java, have a high potential to become deadlocked with
      the Finalizer thread once it becomes active.

      This occurs because:

      (1) the AWT Peers implement finalize methods that make calls to
          [potentially locked] syncronized [shared] objects

      (2) the Finalizer maintains an internal Finalizer lock (_finalmeq_lock)
          to stop certain portions of the finalizer running concurrently AND

      (3) the potentially locked object makes allocation calls against the
          heap while locked. (as Hashtable does) AND

      (4) due to a memory allocation failure in (3) the finalizer is run
          synchronously.

      If the Finalizer attempts to finalize an AWT Peer object, whilst another
      [AWT Peer] is trying to be allocated then a deadlock can occur.


      Scope of problem:
      -----------------

      It's possible that any application that makes extensive use of the AWT
      could run into this problem.

      The likelyhood of hitting this problem increases as the application
      becomes more dynamic in creating and destroying it's AWT Components.

      In short, it affects the scalability of AWT Applications, since it only
      starts to occur once the finalizer starts to get involved - which is
      mostly during high Application stress periods.

      This occurs more often that you'd think...


      Problem Details:
      ----------------

      The class sun.awt.SunToolkit maintains a single Hashtable (peerMap) of
      currently active component/peer objects. Whenever a peer is created,
      through one of the createXXXX methods, a Component/Peer mapping entry
      is added to the Hashtable. When a peer is disposed, it must remove it's
      mapping entry from the Hashtable.

      Subclasses of sun.awt.SunToolkit, such as sun.awt.windows.WToolkit,
      implement various createXXXX methods to create assorted peers, each
      of which is added to the single Hashtable (peerMap).

      This code typically looks like this:

          public MenuItemPeer createMenuItem(MenuItem target)
          {
              MenuItemPeer = new WMenuItemPeer(target);
              peerMap.put(target, peer);
              return peer;
          }

      In turn, each of these peer objects must "report" that it is going away,
      by calling the method targetDisposedPeer (made public in WToolkit) so that
      it's mapping entry can be removed from the Hashtable.

          protected void finalize() throws Throwable
          {
              dispose();
              super.finalize();
          }

          public void dispose()
          {
              WToolkit.targetDisposedPeer(target, this);
              _dispose();
          }

      This code is typically called when the peer object is dispose()'d. In the
      case of the Windows Toolkit, this call is implemented in:

          sun.awt.windows.WComponentPeer AND
          sun.awt.windows.WMenuItemPeer

      (In our case it's MenuItems that cause the problem we are seeing)

      When any Object is being finalized, by the Finalizer thread, it's
      finalize method is invoked. The default implementation of the
      AWT Peer finalize method is to call dispose. This in turn makes
      [synchronized] calls to the [single] hashtable in WToolkit.


      Now we have some general background, I'll illustrate one of the
      situations under which the deadlock can occur - the stack traces
      shown are from an real application where this problem is occuring
      fairly frequently (many times per day).

      Consider an Application where:

      (1) Thread A creates a PopupMenu Item and attaches it to a component,
          the call stack is as follows:

              java.util.Hashtable.put
              sun.awt.windows.WToolkit.createMenuItem
              java.awt.MenuItem.addNotify
              java.awt.Menu.addNotify
              java.awt.Menu.addNotify
              java.awt.PopupMenu.addNotify
              java.awt.Component.add
              oracle...
              ...<Thread A>...

      (2) At pretty much the same time, the Finalizer decides to wake up and
          do a little house cleaning. It locates an old AWT Component peer,
          that's not referenced any more, and decides to finalize it. The call
          stack is as follows:

              java.util.Hashtable.get
              sun.awt.windows.WToolkit.targetDisposedPeer
              sun.awt.windows.WMenuItemPeer.dispose
              sun.awt.windows.WMenuItemPeer.finalize
              ...<Finalizer Thread>...

          At this time it blocks since Thread A (from 1 above) "owns" the lock
          on the single Hashtable object (SunToolkit.peerMap).

      (3) Thread A, in processing the Hashtable's put request, attempts to
          allocate a new HashtableEntry. In doing this, Java internals
          realize that there isn't enough memory/resources in the current
          pool, for whatever reason, and forces an internal *in thread*
          memory cleanup by executing part of the Finalizer code.

          This occurs when the Hashtable does the following:
              ...
              HashtableEntry e = new HashtableEntry();
              ...

          (code extracted from the implementation of the Hashtable.put method):


      It is at this point that the deadlock occurs, since Thread A now needs
      the Finalizer internal lock object, and has the Hashtable lock, and
      the Finalizer has the Finalizer internal lock, and needs the Hashtable lock.

      In other words:

           "Thread A" OWNS SunToolkit.peerMap lock, WANTS Finalizer lock.
           "Finalizer" OWNS Finalizer lock, WANTS SunToolkit.peerMap lock.


      Point to note:
      --------------

      Code in the finalize() method, or called as a direct result of it,
      of *any* object should be extremely careful when making calls that
      may result in Obtaining locks on other objects.

            jlockesunw Jonathan Locke (Inactive)
            rschiavisunw Richard Schiavi (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: