Invalid Assumptions Suck

I just found out I made invalid assumptions when I rewrote the synchronized block instrumentation code, and I hadn’t discovered my mistake until now, when I ran more general tests.

I assumed the structure of the monitorenter code always looked like this:

aload\_0 // load something
dup
astore\_1 // store in local
monitorenter // lock

I assumed that the instruction immediately before the dup would be sufficient by itself to put the object getting locked on the stack. However, that’s not the case, for example when a non-local variable is accessed:

aload\_0// load this
getstatic 0004 // load field
dup
astore\_1 // store in local
monitorenter // lock

Now, the two instructions before the dup are necessary. It gets worse: If the object put on the stack is the result of a function call, making that call twice may produce different results:

synchronized(someFunc()) {
...
}

or in bytecode:

aload\_0// load this
invokevirtual 0018 // call some method
dup
astore\_1 // store in local
monitorenter // lock

Even if I were to duplicate aload_0; invokevirtual 0018, the code would not do what I want. So apparently the real equivalent of what I want to do is this:

final Object temp = someFunc();
SynchronizedMonitor.tryEnterBlock(temp);
synchronized(temp) {
SynchronizedMonitor.enterBlock(temp);
try {
...
}
finally {
SynchronizedMonitor.leave(temp);
}
}

That boils down to bytecode like this (except more complicated because of javac’s ways):

aload\_0// load this
invokevirtual 0018 // call some method
astore\_1 // store return value in local temp
aload\_1 // restore from local temp
invokestatic (SynchronizedMonitor.tryEnter)
aload\_1 // restore from local temp
dup
astore\_2 // store in local 2
monitorenter // lock
aload\_1 // restore from local temp
invokestatic (SynchronizedMonitor.tryEnter)
...
aload\_2 // restore this from local 2
monitorexit // unlock
goto 26 // jump over finally handler

// handler for any exception (line 12)
astore\_3 // store exception in local 3
aload\_1 // restore from local temp
invokestatic (SynchronizedMonitor.leave)
aload\_2 // restore this from local 1
monitorexit // unlock
aload\_3 // restore exception from local 3
athrow // rethrow

...

So I have to introduce an additional local variable (which brings up the naming/equivalence issues again), store to it and reload from it several times. With several synchronized blocks in one method, it becomes more difficult to remember which local variable goes with which block. One thing that I probably can do is reuse the local variable already used by javac. In that case, local variables 1 and 2 would be replaced by a single local variable, or I could reorder the instructions and move the astore up, optimizing an unnecessary astore away. Both cases would not generate a one-to-one correspondence with javac, but it should be identical except for naming/equivalence again.

Now, if I weren’t trying to duplicate javac’s code, it would be as simple as this:

aload\_0// load this
invokevirtual 0018 // call some method
dup
astore\_2 // store in local 2
* dup
* dup
* invokestatic (SynchronizedMonitor.tryEnter)
monitorenter // lock
* invokestatic (SynchronizedMonitor.tryEnter)
...
aload\_2 // restore this from local 2
* dup
* invokestatic (SynchronizedMonitor.leave)
monitorexit // unlock
goto 26 // jump over finally handler

// handler for any exception (line 10)
astore\_3 // store exception in local 3
aload\_2 // restore this from local 1
* dup
* invokestatic (SynchronizedMonitor.leave)
monitorexit // unlock
aload\_3 // restore exception from local 3
athrow // rethrow

...

The beauty here is that the code is very tightly centered around monitorenter and monitorexit, and no new finally handler has to be introduced. The inserted instructions have been marked with a *.

Actually… now that I think of it, it’s likely that I don’t have to introduce a new code>finally handler anyway, because javac needs to make sure there is a hidden code>finally handler anyway to unlock and rethrow. But again, I wouldn’t get what javac generates, I wouldn’t get Java code that corresponds to it.

All of this is solvable, but I’m seriously beginning to doubt the value.

Share

About Mathias

Software development engineer. Principal developer of DrJava. Recent Ph.D. graduate from the Department of Computer Science at Rice University.
This entry was posted in Concurrent Unit Testing. Bookmark the permalink.

Leave a Reply