I’ve written a mock-up test of the algorithm with the interleaved synchronized blocks, using a short, inefficient Monitor
class that immitates the behavior of Java monitors:
class Monitor {
private AtomicBoolean _b = new AtomicBoolean(false);
private Queue
Using this class, I’ve tried to test the implementation, and it seems to work. Now I just have to find a way to easily write this with Java’s regular monitors and the monitorenter
and monitorexit
instructions. I don’t feel like writing the two methods entirely in Java bytecode, so I’ll probably write two empty methods, public static void monitorEnter(final Object o)
and public static void monitorExit(final Object o)
, and then write an instrumentation strategy that inserts the bytecode aload\_0; monitorenter; return
and aload\_0; monitorexit; return
, respectively, as bodies.
When interleaving synchronized blocks this way, there is a greater risk of forgetting to release ownership of a monitor somewhere, producing a deadlock. I think it might be a good idea to also implement this algorithm in Promela and use SPIN to verify it.
I’ve found another mistake in the algorithm, by the way: In compactThreadExit
, I also have to check if I’ve reached the end of the schedule, and if that is the case, turn off scheduled replay and notify all threads waiting for a new buffer. Here’s the new, corrected algorithm:
compactWait
:
- Repeat this… (“RETRY” loop)
monitorenter _waitAlgoObj
(beginning of main synchronized block)- If the buffer has never been loaded, or if the index is at the end of the buffer…
- Load the buffer and reset the indices.
- Wake up all threads waiting for a buffer update.
- If the current sync point marks the end of the schedule…
- Disable scheduled replay.
- Wake up all threads waiting for a buffer update.
monitorexit _waitAlgoObj
- Break out of the “RETRY” loop.
- If there’s a thread waiting for this wait array entry…
- Notify it
- Set a flag to scan ahead for the current sync point (“SCAN”).
- Do not advance indices.
- Otherwise…
- If the current sync point in the array is the right one…
- Advance indices.
- Allow the thread to exit the “RETRY” loop.
- Otherwise…
- Set a flag to scan ahead (“SCAN”).
- If the current sync point in the array is the right one…
- If the “SCAN” flag is set (i.e. either a thread was woken up or the current sync point did not match)…
- Look for the sync points in the remaining part of the array.
- If it could be found…
- Insert a
new Object()
into corresponding slot of the wait array. - Set that
Object()
as “wait object” for this method. - Allow the thread to exit the “RETRY” loop.
- Insert a
- Otherwise…
- Set the new buffer wait object as “wait object” for this method.
- Do not allow the thread to exit the “RETRY” loop.
monitorenter waitObj
(beginning of wait object synchronized block)
monitorexit _waitAlgoObj
(end of main synchronized block)
- If the buffer has never been loaded, or if the index is at the end of the buffer…
- If a “wait object” has been set for this method…
- Call
wait()
on that object (make sure to continue waiting if interrupted). monitorexit waitObj
(end of wait object synchronized block)- If scheduled replay is still enabled and the thread is allowed to exit the “RETRY” loop (i.e. it was not a “new buffer” wait)…
monitorenter _waitAlgoObj
(beginning of main synchronized block)- Advance the indices.
monitorexit _waitAlgoObj
(end of main synchronized block)
- Call
- …while the thread is not allowed to exit the loop and scheduled replay is enabled (end “RETRY” loop).
compactThreadExit
:
monitorenter _waitAlgoObj
(beginning of main synchronized block)- If the index is at the end of the buffer…
- Load the buffer and reset the indices.
- Wake up all threads waiting for a buffer update.
- Otherwise…
monitorenter _waitArray[index]
(beginning of wait object synchronized block)- If there’s a thread waiting for this wait array entry…
- Notify it.
- Otherwise, if the current sync point marks the end of the schedule…
- Disable scheduled replay.
- Wake up all threads waiting for a buffer update.
- Otherwise…
- Do nothing, because there’s still a thread awake that will reach this sync point.
monitorexit _waitArray[index]
(end of wait object synchronized block)
- If the index is at the end of the buffer…
monitorexit _waitAlgoObj
(end of main synchronized block)