New Synchronized Block Instrumentor

I just finished changing the instrumentor for synchronized blocks. Now it generates bytecode that pretty much exactly matches hand-written Java code. This synchronized block

1
2
3
synchronized(this) {
    foo();
}

with the bytecode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
aload\_0 // load this
dup
astore\_1 // store in local 1
monitorenter // lock this
aload\_0 // load this
invokevirtual (foo) // call foo()
aload\_1 // restore this from local 1
monitorexit // unlock this
goto 18 // jump to return

// handler for any exception (lines 5-6)
astore\_2 // store exception in local 2
aload\_1 // restore this from local 1
monitorexit // unlock this
aload\_2 // restore exception from local 2
athrow // rethrow

return

gets turned into the bytecode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
aload\_0 // load this
invokestatic (SynchronizedMonitor.tryEnter)
aload\_0 // load this
dup
astore\_1 // store in local 1
monitorenter // lock this
aload\_0 // load this
invokestatic (SynchronizedMonitor.enter)
aload\_0 // load this
invokevirtual (foo) // call foo()
aload\_0 // load this
invokestatic (SynchronizedMonitor.leave)
aload\_1 // restore this from local 1
monitorexit // unlock this
goto 26 // jump to return

// handler for any exception (lines 9-10)
astore\_2 // store exception in local 2
aload\_0 // load this
invokestatic (SynchronizedMonitor.leave)
aload\_1 // restore this from local 1
monitorexit // unlock this
aload\_2 // restore exception from local 2
athrow // rethrow

return

which is really close to this bytecode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
aload\_0 // load this
invokestatic (SynchronizedMonitor.tryEnter)
aload\_0 // load this
dup
astore\_1 // store this in local 1
monitorenter // lock this
aload\_0 // load this
invokestatic (SynchronizedMonitor.enter)
aload\_0 // load this
invokevirtual (foo) // call foo()
aload\_0 // load this
invokestatic  (SynchronizedMonitor.leave)
goto 22 // jump beyond athrow

// exception handler for all exceptions (lines 10-11, 17-18)
astore\_2 // store exception in local 2
aload\_0 // load this
invokestatic (SynchronizedMonitor.leave)
aload\_2 // restore exception from local 2
athrow // rethrow

aload\_1 // restore this from local 1
monitorexit // unlock this
goto 33 // jump to return

// exception handler for all exceptions (lines 8-24, 28-30)
astore\_3 // store exception in local 3
aload\_1 // restore this from local 1
monitorexit // unlick this
aload\_3 // restore exception from local 3
athrow // rethrow

return

which is what javac generates from this Java code:

1
2
3
4
5
6
7
8
9
10
SynchronizedMonitor.tryEnter(this);
synchronized(this) {
    SynchronizedMonitor.enter(this);
    try {
        foo();
    }
    finally {
        SynchronizedMonitor.leave(this);
    }
}

Assuming the monitor methods whose calls have been inserted don’t throw exceptions, I think the instrumented version and the javac version are equivalent. But do I really have to make it this complicated? Is matching the javac output worth it?

I’ve also noticed that javac doesn’t seem to do any optimizations. I’m a bit shocked, but with a JIT compiler it probably makes sense. I actually hope this observation is correct and will remain valid. If javac does make optimizations, then this whole matching idea will immediately go out the door.

I’ll still have to make the synchronized methods to blocks instrumentor work again, and modify all the others… and of course I haven’t tested at all. More work to come, for sure. Good night.

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