I think I found a way how to fulfill both requirements, but so far only in pure Java bytecode. In bytecode, acquiring and releasing monitors isn’t bound to any scoping and thus doesn’t have to happen in a nested, non-overlapping fashion.
If compactWait
is written like this (Java/Java byte code blend):
public static void compactWait(long code, long tid) {
...
// beginning of main synchronized block
monitorenter _waitAlgoObj
...
// beginning of waitObj synchronized block
monitorenter waitObj
// end of main synchronized block
monitorexit _waitAlgoObj
// wait for notification
waitObj.wait()
// end of waitObj synchronized blocm
monitorexit waitObj
...
}
and compactThreadExit
in a similar way:
public static void compactThreadExit(long tid) {
...
// beginning of main synchronized block
monitorenter _waitAlgoObj
...
// beginning of _waitArray[index] synchronized block
monitorenter _waitArray[index]
// end of main synchronized block
monitorexit _waitAlgoObj
// notify sleeping thread
_waitArray[index].notify()
// end of _waitArray[index] synchronized block
monitorexit _waitArray[index]
...
}
then the regions in which the two monitors are held overlap, and this prevents compactThreadExit
from calling notify()
before compactWait
had a chance to call wait()
.
Let’s look at a more detailed trace from the last posting:
- Thread 1 does
monitorenter _waitAlgoObj
- …
- Thread 1 wakes thread 2 up.
- Thread 2 wakes up.
- Thread 2 wants the
_waitAlgoObj
monitor… - Thread 1 scans for its sync point and inserts a new wait object into
_waitArray[index]
. - Thread 1 does
monitorenter _waitArray[index]
- Thread 1 does
monitorexit _waitAlgoObj
- Thread 2 does
monitorenter _waitAlgoObj
- Thread 2 advances the index.
- Thread 2 does
monitorexit _waitAlgoObj
- Thread 2 exits
compactWait
and executes its code. - Thread 2 enters
compactThreadExit
. - Thread 2 does
monitorenter _waitAlgoObj
- Thread 2 wants the
_waitArray[index]
monitor… - Thread 1 calls
wait()
on that wait object. - Thread 2 does
monitorenter _waitArray[index]
- Thread 2 checks if there is an object in the wait array and, because there is, calls
notify()
on the wait object, waking thread 1 up. - Thread 1 wakes up.
- Thread 2 does
monitorexit _waitArray[index]
- Thread 2 exits
compactThreadExit
. - Thread 2 terminates.
- Thread 1 does
monitorexit _waitArray[index]
- Thread 1 exits
compactWait
and executes its code. - …
Whenever a thread wants to call Object.wait()
or Object.notify()
, the thread first has to own that object’s monitor. By acquiring that monitor in compactWait
before the monitor for the entire algorithm is released, we can guarantee that wait()
is called before notify()
can be called in compactThreadExit
.
I’m just not sure what the easiest way to test this is…