isearch-backspace, isearch-done: docstrings
[conkeror.git] / modules / coroutine.js
blobd0c5339185b0ade6fa493355e5bc663fd944afee
1 /**
2  * (C) Copyright 2008 Jeremy Maitin-Shepard
3  *
4  * Use, modification, and distribution are subject to the terms specified in the
5  * COPYING file.
6 **/
8 /**
9  * Coroutine (i.e. cooperative multithreading) implementation in
10  * JavaScript based on Mozilla JavaScript 1.7 generators.
11  *
12  * Before trying to understand this file, first read about generators
13  * as described here:
14  * https://developer.mozilla.org/en/New_in_JavaScript_1.7
15  *
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/
20  *
21  * === Introduction ===
22  *
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).
26  *
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.:
30  *
31  * foo(a,b,c)
32  *
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.
39  *
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)
54  * behavior may occur.
55  *
56  * === Requirements for a coroutine ===
57  *
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.
62  *
63  * --- Return values ---
64  *
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:
72  *
73  * yield co_return(<expr>);
74  *
75  * must be used in place of the normal syntax:
76  *
77  * return <expr>;
78  *
79  * --- Invoking another coroutine function synchronously ---
80  *
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:
88  *
89  * yield <prepared-coroutine-expr>
90  *
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
94  *
95  * yield foo(a,b,c)
96  *
97  * or
98  *
99  * yield obj.foo(a,b,c)
101  * in the case that "foo" is a coroutine method of some object
102  * `obj'.
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
110  * be stored in `x'.
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
121  * yield foo(a,b,c)
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
133  * called.)
135  * --- Current continutation/"thread" handle ---
137  * The special syntax
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
147  * operation:
149  * yield SUSPEND;
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:
156  * cc(value)
158  * or
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
164  * syntax:
166  * cc.throw(e)
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,
185  * may not be run.
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>)
197  * or, for example,
199  * co_call(foo(a,b,c))
201  * or
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.
215  **/
217 function _return_value (x) {
218     this.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
232  * library.
233  **/
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. */
244     var cc = yield;
246     /**
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.
250      **/
251     var stack = [];
253     /**
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'.
257      *
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
260      * calling f.next()).
261      **/
262     var y = undefined;
263     var throw_value = false;
264     while (true) {
265         try { // We must capture any exception thrown by `f'
267             /**
268              * If `f' yields again after being resumed, the value
269              * passed to yield will be stored in `x'.
270              **/
271             let x;
273             if (throw_value) {
274                 throw_value = false;
275                 x = f.throw(y); // f.throw returns the next value passed to yield
276             } else
277                 x = f.send(y); // f.send also returns the next value passed to yield
279             if (x === CONTINUATION) {
280                 /**
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.
284                  **/
285                 y = cc;
286                 continue;
287             }
289             if (x === SUSPEND) {
290                 /**
291                  * The coroutine `f' asked us to suspend execution of
292                  * the current thread.  We do this by calling yield ourself.
293                  **/
294                 try {
295                     /* our execution will be suspended until send or throw is called on our generator object */
296                     x = yield;
298                     /**
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.
302                      **/
303                     y = x;
304                 } catch (e) {
305                     /**
306                      * User requested that we resume by throwing an exception, so we re-throw
307                      * the exception in `f'.
308                      **/
309                     throw_value = true;
310                     y = e;
311                 }
312                 continue;
313             }
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'
320                 continue;
321             }
323             if (x instanceof _return_value) {
324                 // `f' wants to return a value
325                 f.close();
326                 if (stack.length == 0) {
327                     /**
328                      * `f' doesn't have a caller, so we simply ignore
329                      * the return value and terminate the thread.
330                      */
331                     return;
332                 }
333                 // Pop the caller of `f' off the top of the stack
334                 f = stack[stack.length - 1];
335                 stack.length--;
336                 // Pass the return value to the caller, which is now the current coroutine
337                 y = x.value;
338                 continue;
339             }
341             /**
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'.
347              **/
348             y = x;
349         } catch (e) {
350             /**
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
355              * exception.
356              **/
357             if (stack.length == 0) {
358                 /**
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.
362                  */
363                 return;
364             }
365             // Pop the caller of `f' off the top of the stack
366             f = stack[stack.length - 1];
367             stack.length--;
368             if (e instanceof StopIteration)
369                 y = undefined; // propagate a return value of `undefined' to the caller
370             else {
371                 // propagate the exception to the caller
372                 y = e;
373                 throw_value = true;
374             }
375         }
376     }
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.
392  **/
393 function co_call (f) {
394     if (!is_coroutine(f))
395         return;
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.
400      **/
401     var g = _do_call(f);
402     g.next();
403     var cc = function (x) {
404         try {
405             // We resume execution of the thread by calling send on
406             // the generator object corresponding to our invocation of
407             // _do_call.
408             g.send(x);
409         } catch (e if e instanceof StopIteration) {}
410         catch (e) {
411             // Dump this error, because it indicates a programming error
412             dump_error(e);
413         }
414     };
415     cc.throw = function (x) {
416         try {
417             g.throw(x);
418         } catch (e if e instanceof StopIteration) {}
419         catch (e) {
420             // Dump this error, because it indicates a programming error
421             dump_error(e);
422         }
423     };
424     try {
425         g.send(cc);
426     } catch (e if e instanceof StopIteration) {}
427     catch (e) {
428         // Dump this error, because it indicates a programming error
429         dump_error(e);
430     }
431     return cc;
434 provide("coroutine");