Writing my own compiler for a Java-like language, I am having trouble compiling synchronized blocks
. I come up with the following idea to simplify them to try-finally
blocks:
synchonized (obj) {
statements...
}
Can be replaced with
Object _lock = obj
_monitorEnter(lock)
try {
statements...
}
finally {
_monitorExit(lock)
}
Where _monitorEnter
and _monitorExit
represent the MONITORENTER
and MONITOREXIT
instructions.
Am I correct with this assumption of how synchronized
is compiled, or am I missing something?
EDIT
My implementation previously had some special handling for return
and throw
statements within the body. Basically, it would manually load all lock
variables and MONITOREXIT
them before each *RETURN
or THROW
instruction. Is this handled by the finally
block, or do I still need these checks?
Your assumptions are correct. The synchronized
block in the Java language is implemented with monitorenter
and monitorexit
instructions. You can review the JVM specification details here.
Synchronization in the Java Virtual Machine is implemented by monitor entry and exit, either explicitly (by use of the monitorenter and monitorexit instructions) or implicitly (by the method invocation and return instructions).
The compiler generates bytecode that will handle all exceptions thrown inside a synchronized
body, so your try-finally approach will work fine here.
Specification of finally
statement does not tell anything about releasing monitors. Example provided in the first link shows bytecode for a simple method wrapped in synchronized
block. As you can see, any possible exception is handled to ensure monitorexit instruction execution. You should implement same behaviour in your compiler (write code that will release monitor inside finally statement).
void onlyMe(Foo f) {
synchronized(f) {
doSomething();
}
}
Method void onlyMe(Foo)
0 aload_1 // Push f
1 dup // Duplicate it on the stack
2 astore_2 // Store duplicate in local variable 2
3 monitorenter // Enter the monitor associated with f
4 aload_0 // Holding the monitor, pass this and...
5 invokevirtual #5 // ...call Example.doSomething()V
8 aload_2 // Push local variable 2 (f)
9 monitorexit // Exit the monitor associated with f
10 goto 18 // Complete the method normally
13 astore_3 // In case of any throw, end up here
14 aload_2 // Push local variable 2 (f)
15 monitorexit // Be sure to exit the monitor!
16 aload_3 // Push thrown value...
17 athrow // ...and rethrow value to the invoker
18 return // Return in the normal case
Exception table:
From To Target Type
4 10 13 any
13 16 13 any