diff --git a/classpath/avian/Continuations.java b/classpath/avian/Continuations.java index 0953995320..3aaf9c17c1 100644 --- a/classpath/avian/Continuations.java +++ b/classpath/avian/Continuations.java @@ -12,10 +12,159 @@ package avian; import java.util.concurrent.Callable; +/** + * This class provides access to the Avian VM's continuation support. + * + *
A continuation is a snapshot of a thread's call stack which can
+ * be captured via callWithCurrentContinuation
and later
+ * restored any number of times. We restore this snapshot by either
+ * feeding it a result (to be returned by
+ * callWithCurrentContinuation
) or feeding it an
+ * exception (to be thrown by
+ * callWithCurrentContinuation
). Continuations may be
+ * used to implement features such as coroutines, generators, and
+ * cooperative multitasking.
+ *
+ *
This class provides two static methods -
+ * callWithCurrentContinuation
and
+ * dynamicWind
- with similar semantics to the Scheme
+ * methods call-with-current-continuation
and
+ * dynamic-wind
, respectively. Additionally, we define
+ * how continuations work with respect to native code, exceptions,
+ * try/finally blocks, synchronized blocks, and multithreading.
+ *
+ *
A continuation can be thought of as a singly-linked list of
+ * stack frames representing the call trace, where the head of the
+ * list is the frame of the method most recently called (i.e. the top
+ * of the stack). However, this trace only extends as far as the most
+ * recent chain of Java frames - it ends just prior to the most recent
+ * native frame in the stack. The reason for this is that the VM
+ * cannot, in general, safely capture and restore native frames.
+ * Therefore, each call from native code to Java (including the
+ * original invocation of main
or
+ * Thread.run
) represents a new continuation context in
+ * which continuations may be captured, and these will only contain
+ * frames from within that context.
+ *
+ *
Calling a continuation (i.e. feeding it a result or exception) + * causes the current continuation to be replaced with the calling + * continuation. When the last method in this new continuation + * returns, it returns to the native frame which created the current + * context, which may not be the same as the context in which that + * continuation was created. + * + *
We define the return type of a continuation context as the + * return type of the least-recently called method in the call trace + * (i.e. the end of the list). A continuation may be called from a + * different continuation context than the one in which it was created + * provided the return type of the new context is compatible with the + * original context. + * + *
Given a thread executing in continuation context "A" which wants + * to call a continuation created in context "B", the following rules + * apply: + * + *
void
, the return
+ * type of "B" may be anything, including void
A thread may call a continuation created by a different thread
+ * provided the return types are compatible. Multiple threads may
+ * safely call the same continuation simultaneously without
+ * synchronization. Any attempt to call a continuation from a context
+ * with an incompatible return type will result in an
+ * Traditionally, Java provides one way to wind the execution stack
+ * (recursive method calls) and two ways to unwind it (normal returns
+ * and exception unwinding). With continuations, we add a new way to
+ * (re)wind the stack and a new way to unwind it.
+ *
+ * The call stack of a continuation may share frames with other
+ * continuations - in which case they share a common history. When
+ * calling a continuation "B" from the current continuation "A", we
+ * must unwind past any frames which are in "A" but not in "B" and
+ * rewind past any frames in "B" but not in "A". During this
+ * unwinding and rewinding, we may pass through synchronized and
+ * try/finally blocks while going down the old stack and up the new
+ * stack.
+ *
+ * However, unlike the traditional processes of winding and
+ * unwinding, the VM will ignore these blocks - monitors will not be
+ * released or acquired and finally blocks will not execute. This is
+ * by design. The purpose of such a block is to acquire a resource
+ * before executing a task and release when that task is done. With
+ * continuations, we may wish to yield control temporarily to another
+ * continuation while executing such a task, and we might do so
+ * several times. In such a case we would only want to acquire the
+ * resource when we start the task and only release it when we're
+ * finished, regardless of how often we unwound to reach other
+ * continuations or rewound to return to the next step.
+ *
+ * Conversely, we may wish to acquire and release a resource each
+ * time we (re)wind to or unwind from a continuation, respectively.
+ * In this case, we use This method will either return the result returned by
+ * This method first calls If If avian/IncompatibleContinuationException
being thrown.
+ *
+ *
Winding, Unwinding, and Rewinding
+ *
+ * dynamicWind
to register functions
+ * which will run every time that frame is passed, regardless of how
+ * the stack is wound or unwound.
+ */
public abstract class Continuations {
+ /**
+ * Captures the current continuation, passing a reference to the
+ * specified receiver.
+ *
+ * receiver.receive(Callback)
, propagate the exception
+ * thrown by that method, return the result passed to the
+ * handleResult(T) method of the continuation, or throw the
+ * exception passed to the handleException(Throwable) of the
+ * continuation.
+ */
public static native before.run()
, then
+ * thunk.call()
, and finally after.run()
,
+ * returning the result of the second call. If
+ * before.run()
does not return normally, the second
+ * and third calls will not happen. If thunk.call()
+ * throws an exception, after.run()
, will be called
+ * before the exception is propagated.
+ *
+ * thunk.call()
calls a continuation (directly or
+ * via a subroutine) which does not include the current call to
+ * dynamicWind
,
after.run()
will be called
+ * before control passes to that continuation. If this call throws
+ * an exception, the exception will propagate to the current caller
+ * of dynamicWind
.
+ *
+ * thunk.call()
creates a continuation which is
+ * later called from a continuation which does not include the
+ * current call to dynamicWind
,
+ * before.run()
will be called before control passes to
+ * that continuation. As above, if this call throws an exception,
+ * the exception will propagate to the current caller of
+ * dynamicWind
.
+ */
public static