EVALUATION
Here is what's going. The crash always happens on application shutdown, in particular when AWT toolkit is destroyed. In AwtToolkit::Dispose() we call AwtObjectList::Cleanup() which deletes all the AWT native objects including cursors. After this some of these cursors are finalized by Java2D Disposer. The code for this finalization is:
static class CursorDisposer implements sun.java2d.DisposerRecord {
long pData;
public void dispose() {
finalizeImpl(pData);
}
}
'finalizeImpl' is a native method that checks if pData is not null, converts it to 'AwtCursor *' and deletes. As the cursor has been already deleted, this leads to crash.
The general problem is that AWT native objects may be deleted from two distinct places: from AwtToolkit disposal code and from Java2D Disposer. I'm not sure if any one of these places is enough, so I suggest to fix the problem another way.
The only thing we need is to ensure that no AwtObject is deleted twice, from both places. This means:
1: If an object is deleted by Java2D Disposer, it shouldn't be deleted from AwtToolkit::Dispose()
2: If an object is deleted from AwtToolkit::Dispose(), it shouldn't be deleted by Java2D Disposer.
1 is now already true: when an object is being deleted, it is removed from the AwtObjectList, so it is not deleted in AwtToolkit::Dispose(). Thus, the only thing to fix is 2. As there is no way to remove the disposer record from Java2D Disposer, I suggest to set cursor disposer's pData to 0 when the corresponding record is deleted, and modify the code above to check if pData is not equal to 0. See suggested fix for details.
|
SUGGESTED FIX
--- Cursor.java 2006-12-28 12:50:32.000000000 +0300
***************
*** 190,209 ****
static class CursorDisposer implements sun.java2d.DisposerRecord {
long pData;
public void dispose() {
! finalizeImpl(pData);
}
}
transient CursorDisposer disposer;
private void setPData(long pData) {
this.pData = pData;
! if (!GraphicsEnvironment.isHeadless() && pData != 0) {
! if (disposer == null) {
! // anchor is null after deserialization
! if (anchor == null) {
! anchor = new Object();
}
- disposer = new CursorDisposer();
- sun.java2d.Disposer.addRecord(anchor, disposer);
}
disposer.pData = pData;
}
--- 190,213 ----
static class CursorDisposer implements sun.java2d.DisposerRecord {
long pData;
public void dispose() {
! if (pData != 0) {
! finalizeImpl(pData);
! }
}
}
transient CursorDisposer disposer;
private void setPData(long pData) {
this.pData = pData;
! if (!GraphicsEnvironment.isHeadless()) {
! if (pData != 0) {
! if (disposer == null) {
! // anchor is null after deserialization
! if (anchor == null) {
! anchor = new Object();
! }
! disposer = new CursorDisposer();
! sun.java2d.Disposer.addRecord(anchor, disposer);
}
}
disposer.pData = pData;
}
|