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

Runtime abruptly terminates after passing api/java_awt/Toolkit/index.html#Cursor

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P3 P3
    • None
    • 6
    • client-libs
    • x86
    • windows_xp

      Test Challenger Name and Company:

         Vitaly Mikheev, Excelsior LLC

      Specification Name and Version:

         The Java Language Specification, 3rd Edition; Java Platform Standard Ed 6.0

      Test Suite Name and Version:

         JCK 6a (29-May-2007, build b14)

      TestName:
         api/java_awt/Toolkit/index.html#Cursor

      COMPLAINT:

      From time to time, the Runtime abruptly terminates after passing this test, that is, after it reported that all the test cases were successfully passed.

      HOW TO REPRODUCE ON THE RI

      It's hard to reproduce the problem on the Sun RI because its appearance strongly depends on timing effects and thread scheduling pictute.

      ROOT CAUSE ANALYSIS:

      Due to lack of proper synchronization during AWT resource cleanup upon application exit,
      the destructor ~AwtCursor() may be invoked twice on the same C++ object.

      It provokes access violation and Runtime crash.

      DETAILS:

      Upon test exit, a Java Runtime cleans up the native resources (OS hadles, C++ objects, C memory and the like) associated with AWT Java objects.

      Event loop thread
      -------------------------

      After exiting the event loop in the method

          Java_sun_awt_windows_WToolkit_eventLoop

      the cleanup is done by the Dispose() method

          Java_sun_awt_windows_WToolkit_eventLoop(JNIEnv *env, jobject self) {
             ....
             AwtToolkit::GetInstance().Dispose();
             ....
          }

      which eventually invokes

      void AwtObjectList::Cleanup()
      {
          CriticalSection::Lock l(theAwtObjectList.m_lock);

          CriticalSection &syncCS = AwtToolkit::GetInstance().GetSyncCS();
          BOOL entered = syncCS.TryEnter();
          if (entered) {
              AwtObjectListItem* item = theAwtObjectList.m_head;
              while (item != NULL) {
                  AwtObjectListItem* next = item->next;
                  delete item->obj; //delete #1
                  item = next;
              }
              theAwtObjectList.m_head = NULL;
              syncCS.Leave();
          } else {
              ....
          }
      }

      Disposer thread (sun.java2d.Disposer.run)
      -------------------------------------------------------------

      At the same time, clean up is done in the disposer thread.

      The class java.awt.Cursor has a disposer record associated with the class instances.

      public class Cursor .... {
          static class CursorDisposer implements sun.java2d.DisposerRecord {
              long pData;
              public void dispose() {
                  finalizeImpl(pData);
              }
          }

      The dispose() method invokes finalizeImpl:

      Java_java_awt_Cursor_finalizeImpl(JNIEnv *env, jclass clazz, jlong pData)
      {
          ....
          AwtToolkit::GetInstance().SyncCall(AwtCursor::_Dispose, jlong_to_ptr(pData));
          ....
      }

      that invokes this callback method

      void AwtCursor::_Dispose(void *param) {
          AwtCursor *c = (AwtCursor *)param;
          if (c != NULL) {
              JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

              jobject localObj = env->NewLocalRef(c->jCursor);
              if (localObj != NULL) {
                  setPData(localObj, ptr_to_jlong(NULL));
                  env->DeleteLocalRef(localObj);
              }

              delete c; //delete #2
          }
      }

      wrapped into a synchronization function.

      void *AwtToolkit::SyncCall(void *(*ftn)(void *), void *param) {
          JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
          if (!IsMainThread()) {
              CriticalSection::Lock l(GetSyncCS());
              return (*ftn)(param);
          } else {
              return (*ftn)(param);
          }
      }

      -------------------

      The same C++ object (instance of AwtCursor) is placed in both:

         - AwtObjectListItem processed in the event loop
         - the disposer record associated with a java.awt.Cursor instance

      -------------------

      The thread scheduling picture that causes the double delete of the same C++ object is as follows.

      1) AwtObjectList::Cleanup() invoked in the event loop thread enters in the critical section

         CriticalSection &syncCS = AwtToolkit::GetInstance().GetSyncCS();
          BOOL entered = syncCS.TryEnter();

         and starts the cleanup of the AwtObjectListItem

      2) At the same time, the disposer of a java.awt.Cursor instance is invoked in the disposer thread

         The disposer is blocked on the critical section owned by AwtObjectList::Cleanup()

      3) AwtObjectList::Cleanup() finishes the cleanup and releases the critical section

      4) AwtCursor::_Dispose continues execution and tries to delete the object passed to it as "param" despite the object has been already deleted in AwtObjectList::Cleanup()

            Unassigned Unassigned
            mbykov Misha Bykov (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: