PageRenderTime 33ms CodeModel.GetById 13ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/kilim/Fiber.java

http://github.com/kilim/kilim
Java | 386 lines | 194 code | 49 blank | 143 comment | 32 complexity | 457e534eef1073e748e1f52b91ad1a61 MD5 | raw file
  1/* Copyright (c) 2006, Sriram Srinivasan
  2 *
  3 * You may distribute this software under the terms of the license 
  4 * specified in the file "License"
  5 */
  6
  7package kilim;
  8
  9import java.lang.reflect.Field;
 10
 11/**
 12 * This class serves as a context to manage and store the continuation stack.
 13 * The actual capture of the closure is done in the Weaver-transformed code.
 14 */
 15
 16public final class Fiber {
 17
 18//    public boolean debug = false;
 19    /**
 20     * The current frame's state (local vars  and elements of the operand stack 
 21     * that will be needed when the Fiber is resumed. It is always kept equal 
 22     * to stateStack[iStack] if iStack is in the (0..stateStack.length-1) range, 
 23     * and null otherwise. This is used by the generated code to avoid 
 24     * having to manipulate stateStack in the generated code, and to isolate
 25     * all stack manipulations to up() and down().
 26     */
 27    public State               curState;
 28
 29    /**
 30     * The "program counter", kept equal to stateStack[iStack].pc and is used to
 31     * jump to the appropriate place in the code while rewinding the code, and
 32     * also to inform the weaved code inside an exception handler which pausable
 33     * method (if at all) was being invoked when that exception was thrown. The
 34     * value 0 refers to normal operation; control transfers to the beginning of
 35     * the original (pre-weaved) code. A value of n indicates a direct jump into 
 36     * the nth pausable method (after restoring the appropriate state).
 37     * Accessed by generated code (hence public).
 38     */
 39    public int                 pc;
 40
 41    /*
 42     * One State object for each activation frame in the call hierarchy.
 43     */
 44    private State[]            stateStack              = new State[10];
 45
 46    /*
 47     * Index into stateStack and equal to depth of call hierarchy - 1
 48     */
 49    private int                iStack                  = -1;
 50
 51    boolean                    isPausing;
 52    
 53    boolean                    isDone;
 54
 55    /*
 56     * The task to which this Fiber belongs
 57     */
 58    public Task                      task;
 59
 60    /*
 61     * Special marker state used by pause
 62     */
 63    private static final State PAUSE_STATE             = new State();
 64
 65    /*
 66     * Status indicators returned by down()
 67     *
 68     * normal return, nothing to restore
 69     */
 70    public static final int   NOT_PAUSING__NO_STATE  = 0;
 71    
 72    /*
 73     * Normal return, have saved state to restore before resuming
 74     */
 75    public static final int   NOT_PAUSING__HAS_STATE = 1;
 76    
 77    /*
 78     * Pausing, and need to save state before returning
 79     */
 80    public static final int   PAUSING__NO_STATE      = 2;
 81    
 82    /*
 83     * Pausing, and have saved state from an earlier invocation,
 84     * so nothing left to do.
 85     */
 86    public static final int   PAUSING__HAS_STATE     = 3;
 87
 88    static {
 89        PAUSE_STATE.pc = 1;
 90    }
 91
 92    public static class MethodRef {
 93        String classname, methodname;
 94        public MethodRef(String cn,String mn) { classname = cn; methodname = mn; }
 95    }
 96    
 97    public Fiber(Task t) {
 98        task = t;
 99    }
100
101    public Task task() {
102        return task;
103    }
104
105    public boolean isDone() {
106        return isDone;
107    }
108    
109    public static void pause() throws Pausable {
110        throw new IllegalStateException("pause() called without weaving");
111    }
112
113
114    public void reset() {
115        curState = null;
116        pc = 0;
117        for (int ii=0; ii<stateStack.length; ii++) stateStack[ii] = null;
118        iStack = -1;
119        isPausing = false;
120        isDone = false;
121    }
122    
123    /** yield cooperatively to the next task waiting to use the thread */
124    public static void yield() throws Pausable {
125        Task.errNotWoven();
126    }
127
128    /** yield cooperatively to the next task waiting to use the thread */
129    public static void yield(Fiber f) {
130        f.togglePause();
131    }
132
133    /*
134     * The user calls pause(), but the weaver changes the
135     * call to pause(Fiber), which alternates between
136     * pausing and not pausing in successive invocations.
137     * @param f
138     */
139    public static void pause(Fiber f) {
140        f.togglePause();
141    }
142    
143    /*
144     * Indication from the caller that the invoked function has returned.
145     * 
146     * If it is a normal return, it needs to read the state into curState
147     * and nullify it in stateStack. This accomplishes the equivalent of:
148     *      move up
149     *      get state()
150     *      restore from state
151     *      delete state()
152
153     * If it is pausing, it merely needs to return a combined status
154     * 
155     * @return a combined status of PAUSING/NOT_PAUSING and HAS_STATE/NO_STATE.
156     */
157    
158    public int up() {
159        int d = iStack;
160        iStack = --d;
161        if (isPausing) {
162//            if (debug) System.out.println("\nup(pausing)" + this);;
163//            if (debug) ds();
164            return (stateStack[d] == null) ? PAUSING__NO_STATE
165                    : PAUSING__HAS_STATE;
166            // not setting curState because the generated code is only
167            // interested in knowing whether we have state or not.
168        } else {
169            // move up to caller's level
170            State[] stack = stateStack;
171            State cs = curState = stack[d];
172            if (cs == null) {
173                pc = 0;
174//                if (debug) System.out.println("\nup(not pausing)" + this);;
175//                if (debug) ds();
176                return NOT_PAUSING__NO_STATE;
177            } else {
178                stack[d] = null; // clean up
179                pc = cs.pc;
180//                if (debug) System.out.println("\nup(not pausing)" + this);;
181//                if (debug) ds();
182                return NOT_PAUSING__HAS_STATE;
183              }
184        }
185    }
186    
187    
188    public final Fiber begin() {
189        return down();
190    }
191    
192    /**
193     * end() is the last up(). returns true if the fiber is not pausing.
194     */
195    public final boolean end() {
196        assert iStack == 0 : "Reset: Expected iStack == 0, not " + iStack + "\n" + this;
197        boolean isDone = !isPausing;
198        
199        if (isDone) {
200            // clean up callee's state
201            stateStack[0] = null;
202        }
203        // reset pausing for next round.
204        isPausing = false;
205        iStack = -1;
206//        if (debug) System.out.println("lastUp() " + this);
207//        if (debug) ds();
208        return isDone;
209    }
210
211    /*
212     * Called by the generated code to indicate that it is moving on to the next
213     * pausable method in the hierarchy Adjust iStack, set curState and pc for
214     * convenience
215     * 
216     * @return this Fiber.
217     */
218    public Fiber down() {
219        int d = ++iStack;
220        if (d >= stateStack.length) {
221//            System.out.println("size == " + d);
222            ensureSize(d * 2);
223            pc = 0;
224            curState = null;
225        } else {
226            State s = stateStack[d];
227            curState = s;
228            pc = (s == null) ? 0 : s.pc;
229        }
230//        if (debug) System.out.println("down:\n" + this);
231//        if (debug) ds();
232        return this;
233    }
234    
235    static void ds() {
236        for (StackTraceElement ste: new Exception().getStackTrace()) {
237            String cl = ste.getClassName();
238            String meth = ste.getMethodName();
239            if (cl.startsWith("kilim.Worker") || meth.equals("go") || meth.equals("ds")) continue;
240            String line = ste.getLineNumber() < 0 ? ""  : ":" + ste.getLineNumber();
241            System.out.println('\t' + cl + '.' + ste.getMethodName() + 
242                    '(' + ste.getFileName() + line + ')');
243        }
244    }
245
246    /**
247     * In the normal (non-exception) scheme of things, the iStack is incremented
248     * by down() on the way down and decremented by a corresponding up() when returning 
249     * or pausing. If, however, an exception is thrown, we lose track of where we
250     * are in the hierarchy. We recalibrate iStack by creating a dummy exception
251     * and comparing it to the stack depth of an exception taken earlier.
252     * This is done in scheduler.getStackDepth();
253     * A sample stack trace of the dummy exception looks as follows
254     * <pre>
255     *   at kilim.Fiber.upEx(Fiber.java:250)
256     *   at kilim.test.ex.ExCatch.normalCatch(ExCatch.java)
257     *   at kilim.test.ex.ExCatch.test(ExCatch.java)
258     *   at kilim.test.ex.ExCatch.execute(ExCatch.java)
259     *   at kilim.Task.runExecute(Task.java)
260     *   at kilim.WorkerThread.run(WorkerThread.java:11)
261     * </pre>
262     * We have to figure out the stack depth (iStack) of the method
263     * that caught the exception and called upEx ("normalCatch" here).
264     * The call stack below runExecute may be owned by the scheduler, which
265     * may permit more than one task to build up on the stack. For this reason,
266     * we let the scheduler tell us the depth of upEx below the task's execute().
267     * @return Fiber.pc (note: in contrast up() returns status)
268     */
269    public int upEx() {
270        // compute new iStack. 
271        int is = Task.getStackDepth(task) - 2; // remove upEx and convert to 0-based index. 
272        State cs = stateStack[is];
273
274        for (int i = iStack; i >= is; i--) {
275            stateStack[i] = null; // release state
276        }
277
278        iStack = is;
279        curState = cs;
280        return (cs == null) ? 0 : cs.pc;
281    }
282    
283    /**
284     * Called by the weaved code while rewinding the stack. If we are about to
285     * call a virtual pausable method, we need an object reference on which to
286     * call that method. The next state has that information in state.self
287     */
288    public Object getCallee() {
289        assert stateStack[iStack] != PAUSE_STATE : "No callee: this state is the pause state";
290        assert stateStack[iStack] != null : "Callee is null";
291        return stateStack[iStack + 1].self;
292    }
293    
294    public void setCallee(Object callee) {
295        if (isPausing) {
296            stateStack[iStack].self = callee;
297        }
298    }
299
300    private State[] ensureSize(int newsize) {
301//        System.out.println("ENSURE SIZE = " + newsize);
302        State[] newStack = new State[newsize];
303        System.arraycopy(stateStack, 0, newStack, 0, stateStack.length);
304        stateStack = newStack;
305        return newStack;
306    }
307
308    /**
309     * Called by the generated code before pausing and unwinding its stack
310     * frame.
311     * 
312     * @param state
313     */
314    public void setState(State state) {
315        stateStack[iStack] = state;
316        isPausing = true;
317//        System.out.println("setState[" + + iStack + "] = " + this);
318    }
319    
320    public State getState() {
321      return stateStack[iStack];
322    }
323
324    void togglePause() {
325        // The client code would have called fiber.down()
326        // before calling Task.pause. curStatus would be
327        // upto date.
328        
329        if (curState == null) {
330            setState(PAUSE_STATE);
331        } else {
332            assert curState == PAUSE_STATE : "togglePause: Expected PAUSE_STATE, instead got: iStack == " + iStack + ", state = " + curState;
333            stateStack[iStack] = null;
334            isPausing = false;
335        }
336    }
337
338    public String toString() {
339        StringBuilder sb = new StringBuilder(40);
340        sb.append("iStack = ").append(iStack).append(", pc = ").append(pc);
341        if (isPausing) {
342            sb.append(" pausing");
343        }
344        sb.append('\n');
345        for (int i = 0; i < stateStack.length; i++) {
346            State st = stateStack[i];
347            if (st != null) {
348                sb.append(st.getClass().getName()).append('[').append(i).append("]: ");
349                stateToString(sb, stateStack[i]);
350            }
351        }
352        return sb.toString();
353    }
354    
355    public void wrongPC() {
356        throw new IllegalStateException("Wrong pc: " + pc);
357    }
358
359    static private void stateToString(StringBuilder sb, State s) {
360        if (s == PAUSE_STATE) {
361            sb.append("PAUSE\n");
362            return;
363        }
364        Field[] fs = s.getClass().getFields();
365        for (int i = 0; i < fs.length; i++) {
366            Field f = fs[i];
367            sb.append(f.getName()).append(" = ");
368            Object v;
369            try {
370                v = f.get(s);
371            } catch (IllegalAccessException iae) {
372                v = "?";
373            }
374            sb.append(' ').append(v).append(' ');
375        }
376        sb.append('\n');
377    }
378
379    void clearPausing() {
380        isPausing = false;
381    }
382    public interface Worker {
383        public void execute() throws Pausable, Exception;
384        public void execute(kilim.Fiber fiber) throws Exception;
385    }
386}