kilim.Fiber Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kilim Show documentation
Show all versions of kilim Show documentation
Coroutines, continuations, fibers, actors and message passing for the JVM
/* Copyright (c) 2006, Sriram Srinivasan
*
* You may distribute this software under the terms of the license
* specified in the file "License"
*/
package kilim;
import java.lang.reflect.Field;
/**
* This class serves as a context to manage and store the continuation stack.
* The actual capture of the closure is done in the Weaver-transformed code.
*/
public final class Fiber {
// public boolean debug = false;
/**
* The current frame's state (local vars and elements of the operand stack
* that will be needed when the Fiber is resumed. It is always kept equal
* to stateStack[iStack] if iStack is in the (0..stateStack.length-1) range,
* and null otherwise. This is used by the generated code to avoid
* having to manipulate stateStack in the generated code, and to isolate
* all stack manipulations to up() and down().
*/
public State curState;
/**
* The "program counter", kept equal to stateStack[iStack].pc and is used to
* jump to the appropriate place in the code while rewinding the code, and
* also to inform the weaved code inside an exception handler which pausable
* method (if at all) was being invoked when that exception was thrown. The
* value 0 refers to normal operation; control transfers to the beginning of
* the original (pre-weaved) code. A value of n indicates a direct jump into
* the nth pausable method (after restoring the appropriate state).
* Accessed by generated code (hence public).
*/
public int pc;
/*
* One State object for each activation frame in the call hierarchy.
*/
private State[] stateStack = new State[10];
/*
* Index into stateStack and equal to depth of call hierarchy - 1
*/
private int iStack = -1;
boolean isPausing;
boolean isDone;
/*
* The task to which this Fiber belongs
*/
public Task task;
/*
* Special marker state used by pause
*/
private static final State PAUSE_STATE = new State();
/*
* Status indicators returned by down()
*
* normal return, nothing to restore
*/
public static final int NOT_PAUSING__NO_STATE = 0;
/*
* Normal return, have saved state to restore before resuming
*/
public static final int NOT_PAUSING__HAS_STATE = 1;
/*
* Pausing, and need to save state before returning
*/
public static final int PAUSING__NO_STATE = 2;
/*
* Pausing, and have saved state from an earlier invocation,
* so nothing left to do.
*/
public static final int PAUSING__HAS_STATE = 3;
static {
PAUSE_STATE.pc = 1;
}
public static class MethodRef {
String classname, methodname;
public MethodRef(String cn,String mn) { classname = cn; methodname = mn; }
}
public Fiber(Task t) {
task = t;
}
public Task task() {
return task;
}
public boolean isDone() {
return isDone;
}
public static void pause() throws Pausable {
throw new IllegalStateException("pause() called without weaving");
}
public void reset() {
curState = null;
pc = 0;
for (int ii=0; ii= stateStack.length) {
// System.out.println("size == " + d);
ensureSize(d * 2);
pc = 0;
curState = null;
} else {
State s = stateStack[d];
curState = s;
pc = (s == null) ? 0 : s.pc;
}
// if (debug) System.out.println("down:\n" + this);
// if (debug) ds();
return this;
}
static void ds() {
for (StackTraceElement ste: new Exception().getStackTrace()) {
String cl = ste.getClassName();
String meth = ste.getMethodName();
if (cl.startsWith("kilim.Worker") || meth.equals("go") || meth.equals("ds")) continue;
String line = ste.getLineNumber() < 0 ? "" : ":" + ste.getLineNumber();
System.out.println('\t' + cl + '.' + ste.getMethodName() +
'(' + ste.getFileName() + line + ')');
}
}
/**
* In the normal (non-exception) scheme of things, the iStack is incremented
* by down() on the way down and decremented by a corresponding up() when returning
* or pausing. If, however, an exception is thrown, we lose track of where we
* are in the hierarchy. We recalibrate iStack by creating a dummy exception
* and comparing it to the stack depth of an exception taken earlier.
* This is done in scheduler.getStackDepth();
* A sample stack trace of the dummy exception looks as follows
*
* at kilim.Fiber.upEx(Fiber.java:250)
* at kilim.test.ex.ExCatch.normalCatch(ExCatch.java)
* at kilim.test.ex.ExCatch.test(ExCatch.java)
* at kilim.test.ex.ExCatch.execute(ExCatch.java)
* at kilim.Task.runExecute(Task.java)
* at kilim.WorkerThread.run(WorkerThread.java:11)
*
* We have to figure out the stack depth (iStack) of the method
* that caught the exception and called upEx ("normalCatch" here).
* The call stack below runExecute may be owned by the scheduler, which
* may permit more than one task to build up on the stack. For this reason,
* we let the scheduler tell us the depth of upEx below the task's execute().
* @return Fiber.pc (note: in contrast up() returns status)
*/
public int upEx() {
// compute new iStack.
int is = Task.getStackDepth(task) - 2; // remove upEx and convert to 0-based index.
State cs = stateStack[is];
for (int i = iStack; i >= is; i--) {
stateStack[i] = null; // release state
}
iStack = is;
curState = cs;
return (cs == null) ? 0 : cs.pc;
}
/**
* Called by the weaved code while rewinding the stack. If we are about to
* call a virtual pausable method, we need an object reference on which to
* call that method. The next state has that information in state.self
*/
public Object getCallee() {
assert stateStack[iStack] != PAUSE_STATE : "No callee: this state is the pause state";
assert stateStack[iStack] != null : "Callee is null";
return stateStack[iStack + 1].self;
}
public void setCallee(Object callee) {
if (isPausing) {
stateStack[iStack].self = callee;
}
}
private State[] ensureSize(int newsize) {
// System.out.println("ENSURE SIZE = " + newsize);
State[] newStack = new State[newsize];
System.arraycopy(stateStack, 0, newStack, 0, stateStack.length);
stateStack = newStack;
return newStack;
}
/**
* Called by the generated code before pausing and unwinding its stack
* frame.
*
* @param state
*/
public void setState(State state) {
stateStack[iStack] = state;
isPausing = true;
// System.out.println("setState[" + + iStack + "] = " + this);
}
public State getState() {
return stateStack[iStack];
}
void togglePause() {
// The client code would have called fiber.down()
// before calling Task.pause. curStatus would be
// upto date.
if (curState == null) {
setState(PAUSE_STATE);
} else {
assert curState == PAUSE_STATE : "togglePause: Expected PAUSE_STATE, instead got: iStack == " + iStack + ", state = " + curState;
stateStack[iStack] = null;
isPausing = false;
}
}
public String toString() {
StringBuilder sb = new StringBuilder(40);
sb.append("iStack = ").append(iStack).append(", pc = ").append(pc);
if (isPausing) {
sb.append(" pausing");
}
sb.append('\n');
for (int i = 0; i < stateStack.length; i++) {
State st = stateStack[i];
if (st != null) {
sb.append(st.getClass().getName()).append('[').append(i).append("]: ");
stateToString(sb, stateStack[i]);
}
}
return sb.toString();
}
public void wrongPC() {
throw new IllegalStateException("Wrong pc: " + pc);
}
static private void stateToString(StringBuilder sb, State s) {
if (s == PAUSE_STATE) {
sb.append("PAUSE\n");
return;
}
Field[] fs = s.getClass().getFields();
for (int i = 0; i < fs.length; i++) {
Field f = fs[i];
sb.append(f.getName()).append(" = ");
Object v;
try {
v = f.get(s);
} catch (IllegalAccessException iae) {
v = "?";
}
sb.append(' ').append(v).append(' ');
}
sb.append('\n');
}
void clearPausing() {
isPausing = false;
}
public interface Worker {
public void execute() throws Pausable, Exception;
public void execute(kilim.Fiber fiber) throws Exception;
}
}