org.lightwolf
Class Flow

java.lang.Object
  extended by org.lightwolf.Flow
All Implemented Interfaces:
Serializable

public final class Flow
extends Object
implements Serializable

An execution context similar to that of a thread, but with more capabilities.

Compared to normal threads, flows simplifies the implementation of concurrent algorithms and scalable applications. The following list summarizes the capabilities of a flow:

There are some important concepts regarding flows.

Flow-method: A flow-method is a method marked with the FlowMethod annotation. Whenever a flow-method is executing, there will be an associated flow instance, which can be obtained by invoking current() (such flow is automatically instantiated as described below). If a flow-method invokes itself or another flow-method, both methods use the same flow instance. If a flow-method invokes a normal (non-flow) method, the flow is kept active and can be queried, but some flow utilities will disabled until the normal method returns to the invoker flow-method.

Flow-creator: Whenever a flow-method is invoked by a normal (non-flow) method, it is called the flow-creator method, because it triggers the creation of a new flow (this is done automatically). The flow ends when the flow-creator completes normally or by exception. If the flow-creator calls itself or another flow-method, no new flow is created, as described above.

Flow-controller: The flow-controller is a normal (non-flow) method that invokes a flow-method. The flow-controller receives signals sent by some flow operations, and must handle those signals accordingly. There are standard implementations of flow-controllers, such as execute(Callable) and SimpleFlowManager. If it is known that a flow-method will never send signals, the flow-controller can be any ordinary method calling a flow-method. Many utilities do not send any signal, such as split(int) and returnAndContinue().

According to the above specification, when a flow-method A invokes a normal method B, and then B invokes a flow-method C, a nested flow is created. Regarding the nested flow, B will be the flow-controller and C the flow-creator. If C invokes a flow utility such as fork(int), the effect is applied only to the nested flow. The outer flow, on which A is running, is not affected by the fork. When the nested flow ends or is suspended, the outer flow becomes active again. The number of nesting levels is limited only by memory. Nested flows are uncommon because usually flow-methods are designed to call other flow-methods, which does not cause the creation of new flow, as mentioned above. Nevertheless, nested flows are allowed as an orthogonality feature.

If a flow A belongs to a Process, then every flow B derived from A will automatically belong to the same process. Derived flows are result of shallow copies. Many utilities perform shallow copies, including but not limited to fork(int), returnAndContinue() and Continuation.

Author:
Fernando Colombo
See Also:
FlowMethod, Process, Serialized Form

Field Summary
static int ACTIVE
          A constant indicating that the flow is active.
static int ENDED
          A constant indicating that the flow is ended.
static int PASSIVE
          A constant indicating that the flow is passive.
static int SUSPENDED
          A constant indicating that the flow is suspended.
 
Method Summary
 Future<?> activate()
          Equivalent to activate(null).
 Future<?> activate(Object signalResult)
          Schedules this flow to resume in another thread.
 Future<?> activateThrowing(Throwable exception)
          Schedules this flow to resume in another thread, and throws an exception upon resuming.
 Flow copy()
          Performs a shallow copy on this flow.
static Flow current()
          Returns the current flow.
static void end()
          Ends the current flow.
static void endFork()
          Ends the current fork without waiting for any branch.
static Object execute(Callable<?> callable)
           
static void execute(Runnable runnable)
           
static void forgetFork()
          Forgets the current fork without waiting for any branch.
static boolean forgetProcess()
          Removes the current flow from its current process.
static int fork(int n)
          Starts a fork on the invoker.
 FlowManager getManager()
           
 Flow getPrevious()
           
 Object getResult()
           
 int getState()
          This flow's state, which will be ACTIVE, SUSPENDED or ENDED.
 boolean isActive()
           
 boolean isEnded()
           
 boolean isSuspended()
           
 Object join()
           
static void joinProcess(Process process)
          Adds the current flow to the informed process.
static void leaveProcess()
          Removes the current flow from its current process.
static void log(String msg)
           
static void merge()
          Ends a fork by restoring single-threaded execution.
static boolean merge(long timeout, TimeUnit unit)
          Attempts to end a fork, or give-up after a timeout elapses.
static Flow newFlow()
          Creates and returns a new flow.
static Process process()
          The process to which this flow belongs.
 Object resume()
          Equivalent to resume(null).
 Object resume(Object signalResult)
          Resumes this flow.
 Object resumeThrowing(Throwable exception)
          Resumes this flow throwing an exception.
static void returnAndContinue()
          Performs return while continuing asynchronously.
static void returnAndContinue(boolean v)
          Performs return boolean-value while continuing asynchronously.
static void returnAndContinue(byte v)
          Performs return byte-value while continuing asynchronously.
static void returnAndContinue(char v)
          Performs return char-value while continuing asynchronously.
static void returnAndContinue(double v)
          Performs return double-value while continuing asynchronously.
static void returnAndContinue(float v)
          Performs return float-value while continuing asynchronously.
static void returnAndContinue(int v)
          Causes a method to return an int while continuing asynchronously.
static void returnAndContinue(long v)
          Performs return long-value while continuing asynchronously.
static void returnAndContinue(Object v)
          Performs return reference-value while continuing asynchronously.
static void returnAndContinue(short v)
          Performs return short-value while continuing asynchronously.
static Flow safeCurrent()
          Returns the current flow, or throws an exception if there is no current flow.
static Object signal(FlowSignal signal)
          Suspends the flow and sends a signal to the flow-controller.
static Flow snapshot()
           
static int split(int n)
          Initiates concurrent execution on the invoker.
 Flow streamedCopy()
           
static Flow submit(Callable<?> callable)
           
static Flow submit(Runnable runnable)
           
static Object suspend()
          Equivalent to suspend(null).
static Object suspend(Object argument)
          Suspends the current flow, allowing the current thread to be released.
 Object waitNotRunning()
           
 Object waitSuspended()
           
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

ACTIVE

public static final int ACTIVE
A constant indicating that the flow is active. An active flow is running, which means that it is consuming a Thread. When a flow method calls a non-flow method, the flow will remain active. A flow will move out from active state when the flow-creator returns or when the flow is suspended.

See Also:
getState(), Constant Field Values

SUSPENDED

public static final int SUSPENDED
A constant indicating that the flow is suspended. A suspended flow is not running, which means that it is not consuming any Thread. A suspended flow is always stopped at the point of invocation of signal(FlowSignal) or higher level methods such as suspend(Object) or Continuation.checkpoint(). This means it can be resumed with one of the resume() methods.

See Also:
getState(), Constant Field Values

PASSIVE

public static final int PASSIVE
A constant indicating that the flow is passive. A passive flow is not running and its data is stored on its process. Except for that, a passive flow is identical to a SUSPENDED flow.

See Also:
getState(), Constant Field Values

ENDED

public static final int ENDED
A constant indicating that the flow is ended. A flow is ended when the flow-creator returns normally or by exception, or when the method end() is called. An ended flow cannot be resumed, but it can be passed for the method Continuation.placeOnCheckpoint(Flow).

See Also:
getState(), Constant Field Values
Method Detail

newFlow

public static Flow newFlow()
Creates and returns a new flow. The new flow will be in ENDED state, which is suitable to be passed as argument to methods such as Continuation.resume(Flow).

Returns:
A newly created flow instance, in ENDED state.

execute

public static void execute(Runnable runnable)

execute

public static Object execute(Callable<?> callable)

submit

public static Flow submit(Runnable runnable)

submit

public static Flow submit(Callable<?> callable)

process

public static Process process()
The process to which this flow belongs.

Returns:
An instance of Process, or null if this flow does not belong to any process.
See Also:
joinProcess(Process), leaveProcess()

joinProcess

public static void joinProcess(Process process)
Adds the current flow to the informed process. This method sets the current flow's process to the informed process. This allows usage of process utilities from the current flow.

Parameters:
process - The process to which the current flow will be added. Must not be null.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
NullPointerException - If the informed process is a null reference.
See Also:
Process, leaveProcess(), forgetProcess()

leaveProcess

public static void leaveProcess()
Removes the current flow from its current process. This method sets the current flow's process to null, or throws an exception if the current flow does not belong to any process.

Throws:
IllegalStateException - If the invoker is not a FlowMethod, or if the current flow does not belong to any process.
See Also:
joinProcess(Process), forgetProcess()

forgetProcess

public static boolean forgetProcess()
Removes the current flow from its current process. This method sets the current flow's process to null, or does nothing if the current flow does not belong to any process.

Throws:
IllegalStateException - If the invoker is not a FlowMethod.
See Also:
joinProcess(Process), leaveProcess()

snapshot

public static Flow snapshot()

split

public static int split(int n)
Initiates concurrent execution on the invoker.

This method creates n shallow copies of the current flow, then executes each copy concurrently and starting from the invocation point. In other words, this method is invoked once, but returns 1+n times: 1 time for the invoker, and n times for new flows. Notice that split(0) is a no-effect operation.

The returned value is an int that identifies the flow to which the method returned. It will be 0 for the invoker, and a distinct positive integer for new flows, ranging from 1 to n.

The following example illustrates this behavior:

    void example() {
        System.out.println("Before doFlow()");
        int i = doFlow();
        System.out.printf("doFlow(): %d\n", i);
    }
 
    @FlowMethod
    int doFlow() {
        System.out.println("Before performSplit()");
        int i = performSplit();
        System.out.printf("performSplit(): %d\n", i);
        return i;
    }
 
    @FlowMethod
    int performSplit() {
        System.out.println("Before split(2)");
        int i = Flow.split(2);
        System.out.printf("Split result: %d\n", i);
        return i;
    }
 
The above snippet prints the following (under fair scheduling conditions):
     Before doFlow()
     Before performSplit()
     Before split(2)
     Split result: 0
     Split result: 1
     Split result: 2
     performSplit(): 0
     performSplit(): 1
     performSplit(): 2
     doFlow(): 0
 

Each of the created flow runs on a possibly different thread (the actual thread is defined by the flow manager), and will be independent from all other flows, including the invoker. As defined by the shallow copy behavior, all flows share heap objects referenced by the invoker stack frames at the time of invocation.

If there is an active fork at the time of invocation, the invoker will remain on such fork, hence methods such as merge() and forgetFork() are still valid for the invoker. On the other hand, the newly created flows will be outside any fork, which means that invoking merge() and forgetFork() before an explicit fork on such flows will cause an exception to be thrown.

Parameters:
n - The number of flows to create. Must be non-negative.
Returns:
The flow identifier. Zero for the invoker, and a distinct integer ranging from 1 to n for each new branch.
Throws:
IllegalArgumentException - If n is negative.
IllegalStateException - If the invoker is not a FlowMethod.
See Also:
fork(int)

fork

public static int fork(int n)
Starts a fork on the invoker. A fork is a temporary work executed by concurrent flows.

This method is similar to split(int). In addition to create new flows, it makes the invoker and each of the created flows to run in the context of a fork. The invoker will stay in the fork until it explicitly merges or forgets. As a convention in this documentation, new flows created by this method are called branches, and the flow that invokes this method is called the fork-creator.

If this method is invoked during a previous fork, a nested fork is established. This method changes the behavior of merge() and other fork-ending methods, which always applies to the innermost fork. Because of this, it is recommended to use the structured fork/merge style, which use try/finally blocks as in the following example:

    ... // Single-threaded execution.
    int branch = Flow.fork(n);
    try {
        ... // Concurrent execution by n threads.
    } finally {
        Flow.merge();
    }
    ... // Single-threaded execution.
 

The returned value is an int that identifies the branch to which the method returned. It will be 0 for the invoker, and a distinct positive integer for new branches, ranging from 1 to n.

If parameter n is zero, no new flow will be instantiated and the invoker will continue execution in single-threaded mode. Still, the next fork-ending operation will apply to such fork.

Parameters:
n - The number of branches to create. Must be non-negative.
Returns:
The branch identifier. Zero for the invoker, and a distinct integer ranging from 1 to n for each new branch.
Throws:
IllegalArgumentException - If n is negative.
IllegalStateException - If the invoker is not a FlowMethod.
See Also:
merge(long, TimeUnit), forgetFork()

merge

public static void merge()
                  throws InterruptedException
Ends a fork by restoring single-threaded execution. This method causes the invoker to exit the current fork. Depending on who is the invoker, the behavior will be different.

If the invoker is the fork-creator: This method blocks until each of the corresponding branches ends (see below), or until this thread is interrupted, whichever comes first. If the merge is successful (all branches have ended), the invoker exits the fork, restoring the next outer fork, if any. Otherwise (the thread is interrupted while at least one branch is active), the invoker will stay in the fork. If all other branches have ended before invocation of this method, it will return immediately without checking the thread's interrupt flag.

If the invoker is a branch: This method does not return. It ends the flow immediately, as if end() were invoked. Notice that even finally blocks will not execute.

This method considers only branches created by the last invocation of fork(int).

A branch will be active (that is, it will not end) until one of the following happens:

A suspended branch is considered to be active. Hence, this method will block the fork-creator even if all other branches are suspended. If this happens on a real scenario, another thread must resume the branches, which will allow them to end normally, so this method can return to the fork-creator.

Whenever this method returns normally, it is guaranteed that the invoker was the fork-creator and no branch will be active.

Throws:
InterruptedException - If the current thread was interrupted while there was at least one active branch.
IllegalStateException - If the invoker is not a FlowMethod.
See Also:
fork(int), merge(long, TimeUnit), forgetFork()

merge

public static boolean merge(long timeout,
                            TimeUnit unit)
                     throws InterruptedException
Attempts to end a fork, or give-up after a timeout elapses. This method is the time-constrained version of merge(). Depending on who is the invoker, the behavior will be different.

If the invoker is the fork-creator: This method blocks until each of the corresponding branches ends (see merge()), or until the given timeout expires, or until this thread is interrupted, whichever comes first. If the merge is successful (all branches have ended before the timeout expire), the invoker exits the fork, restoring the next outer fork, if any, and this method returns true. If the timeout expires while at least on branch is active, the invoker will stay in the fork, and this method returns false. Otherwise (the thread is interrupted while at least one branch is active), the invoker will stay in the fork. If all other branches have ended before invocation of this method, it will return immediately without checking the thread's interrupt flag.

If the invoker is a branch: This method does not return. It ends the flow immediately, as if end() were invoked. Notice that even finally blocks will not execute.

Whenever this method returns normally, it is guaranteed that the invoker will be the fork-creator.

Returns:
true if all other branches have ended, false if there was at least one active branch when the timeout elapsed.
Throws:
InterruptedException - If the current thread was interrupted during the wait for branches to end.
IllegalStateException - If the invoker is not a FlowMethod.
NullPointerException - If unit is null.
See Also:
fork(int), merge(), forgetFork()

forgetFork

public static void forgetFork()
Forgets the current fork without waiting for any branch. This method is used to exit the current fork without waiting for, nor causing, any branch to end.

If the invoker is the fork-creator, this method exits the fork and restores the next outer fork, if any, and then immediately returns. If the invoker is a branch, the flow will continue to run on the same thread, but "detached" from the fork, as if created by split(int). Therefore the fork-creator will consider this branch as ended, which means that it might unblock an ongoing call to merge() in the fork-creator.

This method does not cause the current flow to end, even if the invoker is a branch. If you need this, use endFork() instead.

Throws:
IllegalStateException - If the invoker is not a FlowMethod.
See Also:
fork(int), merge(long, TimeUnit), endFork()

endFork

public static void endFork()
Ends the current fork without waiting for any branch. This method is used to exit the current fork without waiting for any branch to end.

If the invoker is the fork-creator, this method exits the fork and restores the next outer fork, if any, and then immediately returns. If the invoker is a branch, it will end the flow immediately, as if end() were invoked. This means that it might unblock an ongoing call to merge() in the fork-creator.

Throws:
IllegalStateException - If the invoker is not a FlowMethod.
See Also:
fork(int), merge(long, TimeUnit), forgetFork()

end

public static void end()
Ends the current flow. This method does not return. It causes execution to continue after the flow-creator. The following sample illustrates this behavior:
    void example() {
        System.out.println("Before doFlow()");
        int i = doFlow();
        System.out.printf("doFlow(): %d\n", i);
    }
 
    @FlowMethod
    int doFlow() {
        System.out.println("Before end()");
        Flow.end();
        System.out.println("After end()");
        return 5;
    }
 
 
The above snippet prints the following:
     Before doFlow()
     Before end()
     doFlow(): 0
 

This method causes the flow-creator to return a "zero" value corresponding to its return type:

If the current flow was created by an utility such as split(int) or returnAndContinue(), this method never returns and simply releases the current thread.

Throws:
IllegalStateException - If the invoker is not a FlowMethod.

returnAndContinue

public static void returnAndContinue()
Performs return while continuing asynchronously.

This method is used to perform a local split operation. In the current flow, it works as if return were executed. In a newly created flow, the invoker is resumed from the point of invocation. The new flow starts from the invoker method, and not from the flow-creator as it would be if split(int) were used. The following example illustrates this behavior:

    @FlowMethod
    void example() {
        System.out.println("Before doFlow()");
        doFlow();
        System.out.println("After doFlow()");
        Thread.sleep(50); // Schedule the new flow's thread. 
        System.out.println("Done");
    }
 
    @FlowMethod
    void doFlow() {
        System.out.println("Before returnAndContinue()");
        Flow.returnAndContinue();
        System.out.println("After returnAndContinue()");
    }
 
 
 

The above example prints the following:

     Before doFlow()
     Before returnAndContinue()
     After doFlow()
     After returnAndContinue()
     Done
 

Notice that the new flow ends when the method doFlow() ends. Calling this method more than once in the same method works, but is redundant because in the second and subsequent calls, the work doesn't need to be done in a new flow.

This method must be called only by a void method. There is one version of this method for each possible return value.

Throws:
IllegalStateException - If the invoker is not a FlowMethod.
IllegalReturnValueException - If the invoker is not a void method.

returnAndContinue

public static void returnAndContinue(boolean v)
Performs return boolean-value while continuing asynchronously. This method is similar to returnAndContinue(int) , except for that it must be invoked for a boolean method.

Parameters:
v - The value to return.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
IllegalReturnValueException - If the invoker is not a boolean method.

returnAndContinue

public static void returnAndContinue(char v)
Performs return char-value while continuing asynchronously. This method is similar to returnAndContinue(int) , except for that it must be invoked for a char method.

Parameters:
v - The value to return.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
IllegalReturnValueException - If the invoker is not a char method.

returnAndContinue

public static void returnAndContinue(byte v)
Performs return byte-value while continuing asynchronously. This method is similar to returnAndContinue(int) , except for that it must be invoked for a byte method.

Parameters:
v - The value to return.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
IllegalReturnValueException - If the invoker is not a byte method.

returnAndContinue

public static void returnAndContinue(short v)
Performs return short-value while continuing asynchronously. This method is similar to returnAndContinue(int) , except for that it must be invoked for a short method.

Parameters:
v - The value to return.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
IllegalReturnValueException - If the invoker is not a short method.

returnAndContinue

public static void returnAndContinue(int v)
Causes a method to return an int while continuing asynchronously.

This method is used to perform a local split operation. In the current flow, it works as if return v were executed. In a newly created flow, the invoker is resumed from the point of invocation. The new flow starts from the invoker method, and not from the flow-creator as it would be if split(int) were used. The following example illustrates this behavior:

    @FlowMethod
    void example() {
        System.out.println("Before doFlow()");
        int i = doFlow();
        System.out.printf("doFlow(): %d\n", i);
        Thread.sleep(50); // Schedule the new flow's thread. 
        System.out.println("Done");
    }
 
    @FlowMethod
    int doFlow() {
        System.out.println("Before returnAndContinue()");
        Flow.returnAndContinue(123);
        System.out.println("After returnAndContinue()");
        return 456;
    }
 
The above example prints the following:
     Before doFlow()
     Before returnAndContinue()
     doFlow(): 123
     After returnAndContinue()
     Done
 

Notice that the new flow ends when the method doFlow() ends. Hence the value of the actual return statement ( 456 in the example) is discarded. Calling this method more than once in the same method works, but is redundant because in the second and subsequent calls, the work doesn't need to be done in a new flow.

This method must be called only by an int method. There is one version of this method for each possible return value.

Parameters:
v - The value to return.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
IllegalReturnValueException - If the invoker is not an int method.

returnAndContinue

public static void returnAndContinue(long v)
Performs return long-value while continuing asynchronously. This method is similar to returnAndContinue(int) , except for that it must be invoked for a long method.

Parameters:
v - The value to return.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
IllegalReturnValueException - If the invoker is not a long method.

returnAndContinue

public static void returnAndContinue(float v)
Performs return float-value while continuing asynchronously. This method is similar to returnAndContinue(int) , except for that it must be invoked for a float method.

Parameters:
v - The value to return.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
IllegalReturnValueException - If the invoker is not a float method.

returnAndContinue

public static void returnAndContinue(double v)
Performs return double-value while continuing asynchronously. This method is similar to returnAndContinue(int) , except for that it must be invoked for a double method.

Parameters:
v - The value to return.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
IllegalReturnValueException - If the invoker is not a double method.

returnAndContinue

public static void returnAndContinue(Object v)
Performs return reference-value while continuing asynchronously. This method is similar to returnAndContinue(int) , except for that it must be invoked for an reference method.

Parameters:
v - The value to return.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
IllegalReturnValueException - If the invoker is not a reference method.
ClassCastException - If the returned object is not assignable to the invoker's return type.

suspend

public static Object suspend()
Equivalent to suspend(null).


suspend

public static Object suspend(Object argument)
Suspends the current flow, allowing the current thread to be released. This method simply sends a SuspendSignal to the flow-controller, which by default does nothing. This can be used to release the current thread to other tasks.

The informed argument is simply passed to the constructor of SuspendSignal, and can be later obtained, usually in the flow-controller, by invoking SuspendSignal.getResult(). The informed argument is usually used to tell the reason of suspension. That is, its usually a merely informative value, used for debugging and tracing purposes.

When a flow chooses to suspend itself, it usually must provide means to be resumed when some event occurs, and not rely on the flow-controller for this task. For example, if a flow chooses to suspend itself while waiting for user input, it should store itself on the user's session storage before suspending, so that when user input happens, the event handler can invoke resume(Object) on the suspended flow.

This method returns the value passed to resume(Object), and might throw an exception if resumeThrowing(Throwable) were instead used. This method can return multiple times and/or in different flows, at the discretion of whoever uses the suspended flow.

This method behaves exactly as the expression signal(new SuspendSignal(argument)).

Parameters:
argument - The argument to be associated with SuspendSignal.
Returns:
The object passed to resume(Object).
See Also:
signal(FlowSignal), suspend(), SuspendSignal, resume(), resume(Object)

signal

public static Object signal(FlowSignal signal)
Suspends the flow and sends a signal to the flow-controller.

This method first suspends the current flow at the point of invocation. Then it sends the specified signal to the flow-controller, as if the signal were thrown by the flow-creator (see below). After this, the next actions are fully determined by the flow-controller, which typically uses the signal to decide.

This method returns only when the flow-controller invokes a resume method (see below) on this flow or on a copy. Notice that this method may complete on a different flow and it may also complete more than once, at the discretion of the flow-controller.

Although the signal is an exception, this method doesn't throw the signal on the current flow. The signal is thrown by the subsystem after the flow have been suspended. To the flow-controller, the signal appears to have been thrown by the flow-creator. This mechanism is illustrated in the following snippet:

    void example() { // This is the flow-controller.
        try {
            doFlow();
            System.out.println("doFlow() returned");
        } catch (FlowSignal signal) {
            System.out.println("Caught by the flow-controller");
            System.out.println("Calling Flow.resume()");
            signal.getFlow().resume();
            System.out.println("Flow ended");
        }
    }
 
    @FlowMethod
    void doFlow() { // This is the flow-creator.
        try {
            doSignal();
            System.out.println("doSignal() returned");
        } catch (FlowSignal signal) {
            System.out.println("Caught by the flow-creator");
        }
    }
 
    @FlowMethod
    void doSignal() { // This is an ordinary flow method.
        try {
            System.out.println("Sending signal");
            FlowSignal signal = new MyFlowSignal();
            Flow.signal(signal);
            System.out.println("Returned from signal (resumed)");
        } catch (FlowSignal signal) {
            System.out.println("Caught by doSignal()");
        }
    }
 
 
 
The above snippet prints the following:
     Sending signal
     Caught by the flow-controller
     Calling Flow.resume()
     Returned from signal (resumed)
     doSignal() returned
     Flow ended
 
The flow-controller can, among other actions, create a shallow copy of this flow, serialize this flow and deserialize in another machine instance, ignore the signal and never resume, or wait for some specific condition before resuming. To provide satisfactory usability, the flow-controller must proceed as specified by the received signal object. There are some well-known signal classes such as DelayedCallSignal and SuspendSignal, but developers are free to create new signals, as long as they provide support for them in the flow-controller.

The completion of this method depends on the chosen resume method:

This method is used to implement utilities such as suspend(Object) and ThreadFreeLock. It is the lowest level API for implementing features such as releasing a pooled thread before completion and serializing thread state for long running processes.

Parameters:
signal - The signal to be sent to the flow-controller.
Returns:
The object passed to resume(Object) method.
Throws:
IllegalStateException - If the invoker is not a FlowMethod.
NullPointerException - If signal is null.
ResumeException - If the flow is resuming through resumeThrowing(Throwable).

current

public static Flow current()
Returns the current flow. This method returns non-null if the current thread is executing a flow method. Otherwise it returns null. The current flow will always be ACTIVE .

Returns:
The current flow, or null no flow method is running on the current thread.

safeCurrent

public static Flow safeCurrent()
Returns the current flow, or throws an exception if there is no current flow. This method is like current(), except in that it never returns null. In the absence of a current flow, it will throw an exception.

Returns:
The current flow.
Throws:
IllegalStateException - If there is no current flow.

log

public static void log(String msg)

getManager

public FlowManager getManager()

getPrevious

public Flow getPrevious()

getState

public int getState()
This flow's state, which will be ACTIVE, SUSPENDED or ENDED.

See Also:
waitSuspended(), waitNotRunning()

isActive

public boolean isActive()

isSuspended

public boolean isSuspended()

isEnded

public boolean isEnded()

resume

public Object resume()
Equivalent to resume(null).


resumeThrowing

public Object resumeThrowing(Throwable exception)
Resumes this flow throwing an exception. This method causes the last invocation of signal(FlowSignal) inside this flow to throw a ResumeException whose cause is the informed exception.

In all other aspects, this method behaves as resume(Object). That is, the invoker will block until the flow completes, will be the new flow-controller, and might need to handle signals.

Parameters:
exception - The exception that will be the cause of the ResumeException to be throwed.
Returns:
The value returned by the flow-creator, or null if the flow-creator is a void method.
Throws:
NullPointerException - If exception is null.
IllegalStateException - If this flow is not suspended.
See Also:
signal(FlowSignal), resume(Object), activateThrowing(Throwable)

resume

public Object resume(Object signalResult)
Resumes this flow. This method causes the last invocation of signal(FlowSignal) inside this flow to return. The flow resumes execution directly on the current thread, and therefore this method only returns when the flow finishes. This means that the invoker of this method will be the new flow-controller, and might need to handle signals. To resume this flow on another thread, use activate(Object).

The informed parameter is simply returned to the flow as a normal result of signal(FlowSignal).

The completion of this method is defined as follows:

In other words, the invoker of resume(Object) will behave as an ordinary flow-controller.

Parameters:
signalResult - The value that signal(FlowSignal) must return to its invoker.
Returns:
The value returned by the flow-creator, or null if the flow-creator is a void method.
Throws:
IllegalStateException - If this flow is not suspended.
See Also:
signal(FlowSignal), resume(), resumeThrowing(Throwable), activate(), activate(Object)

activate

public Future<?> activate()
Equivalent to activate(null).


activateThrowing

public Future<?> activateThrowing(Throwable exception)
Schedules this flow to resume in another thread, and throws an exception upon resuming. This method is identical to activate(Object), except in that resuming is done with resumeThrowing(Throwable) instead of resume(Object).

See Also:
resumeThrowing(Throwable), activate(), activate(Object)

activate

public Future<?> activate(Object signalResult)
Schedules this flow to resume in another thread. The method invokes the flow's manager FlowManager.submit(Flow, Object) method, which will assign resume execution to some thread. The thread's run is a simple flow-controller. It can be roughly defined by this pseudo-code:
     public void run() {
         try {
             flow.resume(signalResult);
         } catch (FlowSignal signal) {
             signal.defaultAction(); // Always call the default action.
         } catch (Throwable e) {
             log(e); // Logs the exception.
         }
     }
 
The returned Future can be use to query execution status. Notice that if such Future is done, it does not necessarily means that the flow finished. Future.isDone() will return true even when the flow was just suspended.

Parameters:
signalResult - The argument to be passed to resume(Object) method. This argument will be returned to the flow by the signal(FlowSignal) that causes suspension.
Returns:
A Future that can be used to query execution status.
See Also:
resume(Object), activate(), activateThrowing(Throwable)

copy

public Flow copy()
Performs a shallow copy on this flow. A shallow copy is a simple copy of stack frames, with all local and stack variables, starting from the flow-creator method, and until the current position in the flow's execution. The value of all variables are copied by simple assignment, including references. Because no heap object is ever cloned or serialized by this method, this flow and the copy will share heap objects referenced by the copied stack variables.

If this flow is suspended, the returned copy can be resumed and it will run just after the suspend invocation, as if it were suspended by itself. This process does not affect the current flow, which was only the source in a copy operation. Many copies can be created from a single flow, which allows implementation of utilities such as split(int) and EventPicker.

The target flow must be either suspended or ended, and hence this method cannot be used to copy a running flow. To get the state of a running flow, use Continuation.

Returns:
A flow whose execution context is identical to this flow, sharing all heap objects referenced by stack frames.
Throws:
IllegalStateException - If this flow is not suspended nor ended.

streamedCopy

public Flow streamedCopy()

join

public Object join()
            throws InterruptedException
Throws:
InterruptedException

waitSuspended

public Object waitSuspended()
                     throws InterruptedException
Throws:
InterruptedException

waitNotRunning

public Object waitNotRunning()
                      throws InterruptedException
Throws:
InterruptedException

getResult

public Object getResult()