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.
217 function _return_value (x) {
221 function co_return (x) {
222 return new _return_value(x);
225 const CONTINUATION = { toString: function () "[object CONTINUATION]" };
226 const SUSPEND = { toString: function () "[object SUSPEND]" };
229 * Returns true if the `obj' is a generator object. Returns false
230 * otherwise. It is assumed that only generator objects that are
231 * actually `prepared coroutines' (see above) will be used with this
234 function is_coroutine (obj) {
235 return obj != null &&
236 typeof(obj) == "object" &&
237 typeof(obj.next) == "function" &&
238 typeof(obj.send) == "function";
241 function _do_call (f) {
243 /* Suspend immediately so that co_call can pass us the continuation object. */
247 * Stack of (partially-run) prepared coroutines/generator objects
248 * that specifies the call-chain of the current coroutine function
249 * `f'. Conceptually, `f' is at the top of the stack.
254 * `y' and `throw_value' together specify how execution of `f' will be resumed.
255 * If `throw_value' is false, f.send(y) is called to resume execution normally.
256 * If `throw_value' is true, f.throw(y) is called to throw the exception `y' in `f'.
258 * Because `f' is initially a just-created prepared coroutine that has not run any
259 * code yet, we must start it by calling f.send(undefined) (which is equivalent to
263 var throw_value = false;
265 try { // We must capture any exception thrown by `f'
268 * If `f' yields again after being resumed, the value
269 * passed to yield will be stored in `x'.
275 x = f.throw(y); // f.throw returns the next value passed to yield
277 x = f.send(y); // f.send also returns the next value passed to yield
279 if (x === CONTINUATION) {
281 * The coroutine (`f') asked us to pass it a reference
282 * to the current continuation. We don't need to make
283 * any adjustments to the call stack.
291 * The coroutine `f' asked us to suspend execution of
292 * the current thread. We do this by calling yield ourself.
295 /* our execution will be suspended until send or throw is called on our generator object */
299 * Since no exception was thrown, user must have requested that we resume
300 * normally using cc(value); we simply pass that value back to `f', which
301 * asked us to suspend in the first place.
306 * User requested that we resume by throwing an exception, so we re-throw
307 * the exception in `f'.
315 if (is_coroutine(x)) {
316 // `f' wants to synchronously call the coroutine `x'
317 stack[stack.length] = f; // push the current coroutine `f' onto the call stack
318 f = x; // make `x' the new top of the stack
319 y = undefined; // `x` is a new coroutine so we must start it by passing `undefined'
323 if (x instanceof _return_value) {
324 // `f' wants to return a value
326 if (stack.length == 0) {
328 * `f' doesn't have a caller, so we simply ignore
329 * the return value and terminate the thread.
333 // Pop the caller of `f' off the top of the stack
334 f = stack[stack.length - 1];
336 // Pass the return value to the caller, which is now the current coroutine
342 * `f' yielded to us a value without any special
343 * interpretation. Most likely, this is due to `f' calling
344 * a normal function as if it were a coroutine, in which
345 * case `x' simply contains the return value of that
346 * normal function. Just return the value back to `f'.
351 * `f' threw an exception. If `e' is a StopIteration
352 * exception, then `f' exited without returning a value
353 * (equivalent to returning a value of
354 * `undefined'). Otherwise, `f' threw or propagted a real
357 if (stack.length == 0) {
359 * `f' doesn't have a caller, so regardless of whether
360 * `f' exited normally or threw an exception, we
361 * simply terminate the thread.
365 // Pop the caller of `f' off the top of the stack
366 f = stack[stack.length - 1];
368 if (e instanceof StopIteration)
369 y = undefined; // propagate a return value of `undefined' to the caller
371 // propagate the exception to the caller
380 * Invokes the specified coroutine. If the argument is not a (already
381 * prepared) coroutine, this function simply returns, doing
382 * nothing. Thus, the syntax:
384 * co_call(foo(a,b,c))
386 * can be used to call the function foo with the arguments [a,b,c]
387 * regardless of whether foo is a normal function or a coroutine
388 * function. Note, though, that using this syntax, if foo is a normal
389 * function and throws an exception, it will be propagated, while if
390 * foo is a coroutine, any exceptions it throws will be ignored.
393 function co_call (f) {
394 if (!is_coroutine(f))
397 /** The _do_call helper function is called to actually do all of
398 * the work. `_do_call' is written as a generator function for
399 * implementation convenience.
403 var cc = function (x) {
405 // We resume execution of the thread by calling send on
406 // the generator object corresponding to our invocation of
409 } catch (e if e instanceof StopIteration) {}
411 // Dump this error, because it indicates a programming error
415 cc.throw = function (x) {
418 } catch (e if e instanceof StopIteration) {}
420 // Dump this error, because it indicates a programming error
426 } catch (e if e instanceof StopIteration) {}
428 // Dump this error, because it indicates a programming error
434 provide("coroutine");