/src/kilim/Fiber.java
http://github.com/kilim/kilim · Java · 386 lines · 194 code · 49 blank · 143 comment · 32 complexity · 457e534eef1073e748e1f52b91ad1a61 MD5 · raw file
- /* 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; ii++) stateStack[ii] = null;
- iStack = -1;
- isPausing = false;
- isDone = false;
- }
-
- /** yield cooperatively to the next task waiting to use the thread */
- public static void yield() throws Pausable {
- Task.errNotWoven();
- }
- /** yield cooperatively to the next task waiting to use the thread */
- public static void yield(Fiber f) {
- f.togglePause();
- }
- /*
- * The user calls pause(), but the weaver changes the
- * call to pause(Fiber), which alternates between
- * pausing and not pausing in successive invocations.
- * @param f
- */
- public static void pause(Fiber f) {
- f.togglePause();
- }
-
- /*
- * Indication from the caller that the invoked function has returned.
- *
- * If it is a normal return, it needs to read the state into curState
- * and nullify it in stateStack. This accomplishes the equivalent of:
- * move up
- * get state()
- * restore from state
- * delete state()
- * If it is pausing, it merely needs to return a combined status
- *
- * @return a combined status of PAUSING/NOT_PAUSING and HAS_STATE/NO_STATE.
- */
-
- public int up() {
- int d = iStack;
- iStack = --d;
- if (isPausing) {
- // if (debug) System.out.println("\nup(pausing)" + this);;
- // if (debug) ds();
- return (stateStack[d] == null) ? PAUSING__NO_STATE
- : PAUSING__HAS_STATE;
- // not setting curState because the generated code is only
- // interested in knowing whether we have state or not.
- } else {
- // move up to caller's level
- State[] stack = stateStack;
- State cs = curState = stack[d];
- if (cs == null) {
- pc = 0;
- // if (debug) System.out.println("\nup(not pausing)" + this);;
- // if (debug) ds();
- return NOT_PAUSING__NO_STATE;
- } else {
- stack[d] = null; // clean up
- pc = cs.pc;
- // if (debug) System.out.println("\nup(not pausing)" + this);;
- // if (debug) ds();
- return NOT_PAUSING__HAS_STATE;
- }
- }
- }
-
-
- public final Fiber begin() {
- return down();
- }
-
- /**
- * end() is the last up(). returns true if the fiber is not pausing.
- */
- public final boolean end() {
- assert iStack == 0 : "Reset: Expected iStack == 0, not " + iStack + "\n" + this;
- boolean isDone = !isPausing;
-
- if (isDone) {
- // clean up callee's state
- stateStack[0] = null;
- }
- // reset pausing for next round.
- isPausing = false;
- iStack = -1;
- // if (debug) System.out.println("lastUp() " + this);
- // if (debug) ds();
- return isDone;
- }
- /*
- * Called by the generated code to indicate that it is moving on to the next
- * pausable method in the hierarchy Adjust iStack, set curState and pc for
- * convenience
- *
- * @return this Fiber.
- */
- public Fiber down() {
- int d = ++iStack;
- if (d >= 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
- * <pre>
- * 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)
- * </pre>
- * 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;
- }
- }