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