-
Bug
-
Resolution: Duplicate
-
P3
-
None
-
6
-
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()
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()
- duplicates
-
JDK-6507549 jvm crash while performing cursors finalizer
- Closed