2 * (C) Copyright 2008 Jeremy Maitin-Shepard
4 * Use, modification, and distribution are subject to the terms specified in the
9 * Coroutine (i.e. cooperative multithreading) implementation in
10 * JavaScript based on Mozilla JavaScript 1.7 generators.
12 * Before trying to understand this file, first read about generators
14 * https://developer.mozilla.org/en/New_in_JavaScript_1.7
16 * Additionally, here is a document describing another implementation
17 * of coroutines/cooperative multithreading in JavaScript based on
18 * generators that may be of interest:
19 * http://www.neilmix.com/2007/02/07/threading-in-javascript-17/
21 * === Introduction ===
23 * For the purposes of Conkeror, a coroutine is a generator function
24 * (i.e. a function that uses the yield keyword) that adheres to
25 * certain practices (described later in this file).
27 * As described in the "New in JavaScript 1.7" document, although a
28 * generator function `foo' can be invoked using the same syntax as
29 * any other function, i.e.:
33 * this "function call" merely serves to bind the arguments (including
34 * the special `this' argument) without actually running any of the
35 * code specified in the defintion of foo, and return a special
36 * generator object. The generator object has three methods, `next',
37 * 'send', and 'close', that can be called to actually run code
38 * specified in the definition of the generator function.
40 * In Conkeror, a `coroutine' refers to a generator function that
41 * adheres to the practices described later in this file. In a
42 * JavaScript program, a coroutine (or any generator function)
43 * unfortunately cannot be distinguished from a normal function
44 * without actually invoking it. A `prepared coroutine' refers to a
45 * generator object obtained from calling a coroutine
46 * function. Generally when using this coroutine library, none of the
47 * methods of these generator objects should be called directly. The
48 * `is_coroutine' function can be used to check whether a given value
49 * is a generator object (not a generator function). This library
50 * generally assumes that any generator objects it is passed are
51 * proper prepared coroutines. If a generator function that does not
52 * adhere to the practices required of a coroutine is used with this
53 * library as a coroutine, undefined (and generally undesirable)
56 * === Requirements for a coroutine ===
58 * In most ways, a coroutine function can be written like a normal
59 * function. Arbitrary computation can be done, including arbitrary
60 * calls to normal functions, exceptions can be thrown, and exceptions
61 * can be handled using try-catch-finally blocks.
63 * --- Return values ---
65 * One of the main differences from a normal function is that the
66 * `return' keyword cannot be used to return a value to the caller
67 * (which is necessarily either another coroutine function, or the
68 * coroutine was itself started in a new "thread" in which case the
69 * return value is ignored). The `return' keyword can still be used to
70 * return `undefined' to the caller. In order to return a value,
71 * though, the special syntax:
73 * yield co_return(<expr>);
75 * must be used in place of the normal syntax:
79 * --- Invoking another coroutine function synchronously ---
81 * Another very important operation is calling another coroutine
82 * function synchronously, meaning that control will not return to the
83 * caller (the current coroutine) until the specified coroutine has
84 * either returned or thrown an exception. Conceptually, the specified
85 * coroutine is run in the same "thread" as the current coroutine, as
86 * opposed to being invoked asynchronously, in which case it would be
87 * run in a new "thread". This is done using the syntax:
89 * yield <prepared-coroutine-expr>
91 * where <prepared-coroutine-expr> is some expression that evaluates to
92 * a generator object, most typically a direct call to a coroutine
93 * function in the form of
99 * yield obj.foo(a,b,c)
101 * in the case that "foo" is a coroutine method of some object
104 * If the specified coroutine returns a value normally, the yield
105 * expression evaluates to that value. That is, using the the syntax
107 * var x = yield foo(a,b,c);
109 * if foo is a coroutine and returns a normal value, that value will
112 * Alternatively, if the specified coroutine throws an exception, the
113 * exception will be propagated out of the yield expression, and can
114 * optionally be handled using a try-catch-finally block. If it is not
115 * handled, it will be propagated to the caller of the current
116 * coroutine in the same way.
118 * Note that it is safe to invoke a normal function using `yield' as
119 * well as if it were a coroutine. That is, the syntax
123 * can likewise be used if foo is a normal function, and the same
124 * return value and exception propagation semantics
125 * apply. (Technically, what is actually happenining is that if yield
126 * is passed a value that is not a generator object or one of the
127 * several other types of values that are handled specially like the
128 * return value of co_return, it simply returns the value back
129 * untouched to the coroutine function. Thus, if foo is a normal
130 * function and returns a value, the return value is passed to yield,
131 * which immediately passes it back. If it throws an exception, then
132 * due to the normal exception propagation, yield is never even
135 * --- Current continutation/"thread" handle ---
139 * var cc = yield CONTINUATION;
141 * can be used to obtain a special "continuation" object that serves
142 * as a sort of "handle" to the current thread. Note that while in a
143 * single "thread", even if not in the same coroutine function, the
144 * exact same "continuation" object will always be returned.
146 * The continuation object is used in conjuction with another special
151 * This operation suspends execution of the current "thread" until it
152 * is resumed using a reference to the continuation object for the
153 * "thread". There are two ways to resume executation. To resume
154 * execution normally, the syntax:
160 * cc() (equivalent to cc(undefined))
162 * can be used. This resumes execution and causes the yield SUSPEND
163 * expression to evaluate to the specified value. Alternatively, the
168 * can be used. This causes the specified exception `e' to be thrown
169 * from the yield SUSPEND expression.
171 * It is not valid to use either of these two operations on a
172 * continutation corresponding to a thread that is either currently
173 * running or has already terminated.
175 * Generally, any coroutine function that suspends the current
176 * "thread" should also arrange using some other asynchronous
177 * facility, such as a timer with a callback or an event handler, to
178 * resume the "thread" later. It should also arrange to resume the
179 * "thread" with an exception if an error of some sort occurs in lieu
180 * of simply not resuming the "thread" at all.
182 * It is not technically necessary to resume a "thread" after
183 * suspending it, but it generally should always be done, as otherwise
184 * important error handling code, including code in `finally' blocks,
187 * === Invoking a coroutine asynchronously/spawning a new thread ===
189 * A coroutine function can be called asynchronously from either a
190 * normal function or a coroutine function. Conceptually, this is
191 * equivalent to spawning a new "thread" to run the specified
192 * coroutine function. This operation is done using the co_call
193 * function as follows:
195 * co_call(<prepared-coroutine-expr>)
199 * co_call(foo(a,b,c))
203 * co_call(function (){ yield foo(a,b,c); }())
205 * In the last example, by modifying the anonymous coroutine function,
206 * the return value of the coroutine foo, or an exception it throws,
207 * could be processed, which is not possible in the case that foo is
208 * called directly by co_call.
210 * The function co_call returns the continuation for the new "thread"
211 * created. The return value of the specified continuation is ignored,
212 * as are any exceptions it throws. The call to co_call returns as
213 * soon as the specified coroutine suspends execution for the first
214 * time, or completes.
219 function _return_value (x) {
223 function co_return (x) {
224 return new _return_value(x);
227 const CONTINUATION = { toString: function () "[object CONTINUATION]" };
228 const SUSPEND = { toString: function () "[object SUSPEND]" };
231 * Returns true if the `obj' is a generator object. Returns false
232 * otherwise. It is assumed that only generator objects that are
233 * actually `prepared coroutines' (see above) will be used with this
236 function is_coroutine (obj) {
237 return obj != null &&
238 typeof(obj) == "object" &&
239 typeof(obj.next) == "function" &&
240 typeof(obj.send) == "function";
243 function _do_call (f) {
245 /* Suspend immediately so that co_call can pass us the continuation object. */
249 * Stack of (partially-run) prepared coroutines/generator objects
250 * that specifies the call-chain of the current coroutine function
251 * `f'. Conceptually, `f' is at the top of the stack.
256 * `y' and `throw_value' together specify how execution of `f' will be resumed.
257 * If `throw_value' is false, f.send(y) is called to resume execution normally.
258 * If `throw_value' is true, f.throw(y) is called to throw the exception `y' in `f'.
260 * Because `f' is initially a just-created prepared coroutine that has not run any
261 * code yet, we must start it by calling f.send(undefined) (which is equivalent to
265 var throw_value = false;
267 try { // We must capture any exception thrown by `f'
270 * If `f' yields again after being resumed, the value
271 * passed to yield will be stored in `x'.
277 x = f.throw(y); // f.throw returns the next value passed to yield
279 x = f.send(y); // f.send also returns the next value passed to yield
281 if (x === CONTINUATION) {
283 * The coroutine (`f') asked us to pass it a reference
284 * to the current continuation. We don't need to make
285 * any adjustments to the call stack.
293 * The coroutine `f' asked us to suspend execution of
294 * the current thread. We do this by calling yield ourself.
297 /* our execution will be suspended until send or throw is called on our generator object */
301 * Since no exception was thrown, user must have requested that we resume
302 * normally using cc(value); we simply pass that value back to `f', which
303 * asked us to suspend in the first place.
308 * User requested that we resume by throwing an exception, so we re-throw
309 * the exception in `f'.
317 if (is_coroutine(x)) {
318 // `f' wants to synchronously call the coroutine `x'
319 stack[stack.length] = f; // push the current coroutine `f' onto the call stack
320 f = x; // make `x' the new top of the stack
321 y = undefined; // `x` is a new coroutine so we must start it by passing `undefined'
325 if (x instanceof _return_value) {
326 // `f' wants to return a value
328 if (stack.length == 0) {
330 * `f' doesn't have a caller, so we simply ignore
331 * the return value and terminate the thread.
335 // Pop the caller of `f' off the top of the stack
336 f = stack[stack.length - 1];
338 // Pass the return value to the caller, which is now the current coroutine
344 * `f' yielded to us a value without any special
345 * interpretation. Most likely, this is due to `f' calling
346 * a normal function as if it were a coroutine, in which
347 * case `x' simply contains the return value of that
348 * normal function. Just return the value back to `f'.
353 * `f' threw an exception. If `e' is a StopIteration
354 * exception, then `f' exited without returning a value
355 * (equivalent to returning a value of
356 * `undefined'). Otherwise, `f' threw or propagted a real
359 if (stack.length == 0) {
361 * `f' doesn't have a caller, so regardless of whether
362 * `f' exited normally or threw an exception, we
363 * simply terminate the thread.
367 // Pop the caller of `f' off the top of the stack
368 f = stack[stack.length - 1];
370 if (e instanceof StopIteration)
371 y = undefined; // propagate a return value of `undefined' to the caller
373 // propagate the exception to the caller
382 * Invokes the specified coroutine. If the argument is not a (already
383 * prepared) coroutine, this function simply returns, doing
384 * nothing. Thus, the syntax:
386 * co_call(foo(a,b,c))
388 * can be used to call the function foo with the arguments [a,b,c]
389 * regardless of whether foo is a normal function or a coroutine
390 * function. Note, though, that using this syntax, if foo is a normal
391 * function and throws an exception, it will be propagated, while if
392 * foo is a coroutine, any exceptions it throws will be ignored.
395 function co_call (f) {
396 if (!is_coroutine(f))
399 /** The _do_call helper function is called to actually do all of
400 * the work. `_do_call' is written as a generator function for
401 * implementation convenience.
405 var cc = function (x) {
407 // We resume execution of the thread by calling send on
408 // the generator object corresponding to our invocation of
411 } catch (e if e instanceof StopIteration) {}
413 // Dump this error, because it indicates a programming error
417 cc.throw = function (x) {
420 } catch (e if e instanceof StopIteration) {}
422 // Dump this error, because it indicates a programming error
428 } catch (e if e instanceof StopIteration) {}
430 // Dump this error, because it indicates a programming error
436 provide("coroutine");