Wednesday, March 10, 2010

Coroutines for Java: Status Update

I'm currently working on bringing Coroutines to the Hotspot Java VM (as part of the MLVM project). I'm happy to announce that I have pushed a version of my code to the mlvm mercurial repository. The patches are named "coro.patch".

If you want to test the new coroutine features you can either compile your own binaries (see the wiki for details) or use a recent openjdk build (from here) together with one of my precompiled binaries:


  • Linux x86 binaries:
    Place the desired binaries (debug or product) in the jre/lib/i386 folder

  • Windows x86 binaries:
    Place the desired binaries (debug or product) in the jre\bin folder

  • Java classes:
    These classes have to be inserted before the boot classpath because some classes in rt.jar are replaced. This can be achieved via prepending them to the boot classpath:
    "-Xbootclasspath/p:target_folder/coroutine_classes.jar"


If everything is configured correctly you shuold be able to run the following example program (source):

This program produces the following output:
main start
Coroutine.run
main end


The Coroutine is created simply by creating a new Coroutine instance. Analogous to Thread the code that will be executed within the coroutine can be defined either by overriding the run method or by passing a Runnable to the constructor. The Coroutine framework keeps all active coroutines in a doubly-linked ring, which defines an execution order for the coroutines and makes sure that no coroutine is "lost". (which is more important than you might think!)

These symmetric coroutines are great to allow, for example, periodic scheduling of agents and the like. But another way of using coroutines are asymmetric coroutines.
These are good at inverting the way a method behaves: Imagine a method that somehow generates a stream of values. Normally one would have to think about where to put and what to do with these values. But using coroutines we can write the method as if we had an infinite buffer for our values.

An example I like to bring up for this is the SAX parser: using asymmetric coroutines a (callback-based) SAX parser can be turned into a generator that returns one value at a time (source):

The extended for loop here is short for:
while(!parser.isFinished()) {
String element = coCall(parser);


The coroutine in this case extends CoGenerator. For other use cases there are two more classes: CoConsumer and CoExpression
A CoConsumer receives a value whenever it is called, but doesn't return anything, and a CoExpression both receives and returns a value.


The interface of the Coroutine classes looks like this:


The performance of this implementation is quite good - it takes ~15 ns per context switch on my machine in Ubuntu x86.

Have fun!
cheers,
Lukas

hsdis-i386.dll

Sometimes seeing the actual assembly code that hotspot creates can be very helpful. "-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly" should do the trick.
But you will probably get the following message:

Could not load hsdis-i386.dll; library not loadable; PrintAssembly is disabled

This happens when the library that does the actual disassembly is missing.
Building this library can be really tricky, especially on Windows. Here is a precompiled binary: hsdis-i386.zip (Windows x86)