Continuations, here I come

Rémi Forax forax at univ-mlv.fr
Fri Jul 31 12:55:44 PDT 2009


Comments inlined:

Le 31/07/2009 14:40, Lukas Stadler a écrit :
> Hi everybody!
>
> It's been a while since I posted the last update about the continuations
> work:
>
> As Christian already mentioned we wrote a paper about the implementation
> of the continuation capture/resume functionality. The paper isn't
> published yet but I can send private copies to anyone who's interested.
> I'll also attend the JVM Language Summit in September.
>
> Apart from the finishing the paper I've been working hard on making the
> continuations patch more stable and I've by now created a small
> junit-suite which allows me to perform all the refactorings I wanted to
> do for such a long time...
> The current API is quite simple:
>
> Continuation c = new Continuation();
> Object ret = c.save();
> if (ret == Continuation.CAPTURED) {
>     c.resume(null);
> }
> // ret is null at this point
>    

What's happen if i call resume() without calling save() ??

> The next thing on my agenda is delimited continuations. (which also
> allows for portable, serializable, etc. continuations, you know, all the
> fun stuff...) The challenge here isn't the technical implementation (as
> this is already laid out by the underlying implementation) but the
> interface. I'd be more than happy about any input you can give me!
>
> As I see it the basic problem is how one can specify the point at which
> the continuation is bisected. There are a few options:
> * use a (native or non-native) method that takes a Runnable as the border
> * specify a method at which to cut the delimited continuation
> * allow the application to walk the stackframes of the continuation and
> decide where to cut
> * use the "difference" between two continuations
> * and possibly some more...
>
> For performance reasons there is one restriction: The delimited
> continuation should not be cut within a stackframe. This might not seem
> like a problem but remember that inline can collapse multiple java
> stackframes into a single native one.
>
> So I'd propose the following:
>
> interface DelimitedRunnable {
>     public void run(Continuation cont);
> }
>
> /* Executes runnable and passes the continuation that can be used to
> return to return as a parameter. Every continuation stored below this
> stackframe is a delimited continuation as soon as control returns to the
> delimited method. */
> Object delimited(DelimitedRunnable runnable);
>
> /* Appends the delimited continuation to the current stack and starts
> execution. The Continuation.copy method returns the continuation that
> can be used to return. */
> Object delimited(Continuation cont);
>    

Using overloading for methods with different semantics
is a way to create puzzlers.

> usage would look like this:
>
> class Enumerable implements DelimitedRunnable {
>     private Continuation cont = new Continuation();
>     public void run(Continuation ret) {
>       int i = 0;
>       while (true) {
>         Object o = cont.save();
>         if (o == Continuation.CAPTURED)
>           ret.resume(i);
>         else
>           ret = o;
>         i++;
>       }
>     }
>
>     public void next() {
>       if (cont.isEmpty())
>         Continuation.delimited(this);
>       else
>         Continuation.delimited(cont);
>     }
> }
>
> Security: I don't think that delimited continuations make any difference
> security-wise.
> The current status: I could push the patch into the patch-repository,
> but I'm kind of a mercurial-newbie... Do you perhaps have any pointers
> to a description of how that works? :-)
>
> - Lukas
>    

I like the Fiber API of Ruby 1.9,
translated in Java:

public class Fiber {
   public Fiber(Runnable r) {
      ....
   }

   // or save, suspend, passivate, etc
   public static void yield(Object value) {
      ...
   }

    public Object resume() {
       ...
    }
}

I think it's important to use a Runnable without argument
because it better fit with current API but there is a mismatch:
Runnable/Callable are used for asynchronous call (Thread or executor),
I don't think it's a good idea to reuse the same interface for synchronous
and asynchronous block of code.

A first call to resume() run the runnable and it ends when resume()
returns a sentinel. I think it's better than to have two different methods.

I have declared yield (equivalent of save())
static because it avoid the user to call yield on another continuation.
I don't know if with the current continuation is something that can
be implemented easily or not.

And the Fibonacci example (you can't post something a little bit
serious without a Fibonacci example :)

Fiber f = new Fiber(new RunnableOrAnotherInterface() {
   public void run() {
      int f = 1;
      int oldf =0;
      for(;;) {
         int fib = oldf + f;
         Fiber.yield(fib);
         oldf = f;
         f = fib;
      }
  }
});
...
for(int i=0;i <10; i++) {
    System.out.println(f.resume());
}

cheers,
Rémi



More information about the mlvm-dev mailing list