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