All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kilim.Fiber Maven / Gradle / Ivy

Go to download

Coroutines, continuations, fibers, actors and message passing for the JVM

There is a newer version: 2.0.2-jdk7
Show newest version
/* 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy