Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 432 lines (360 sloc) 12.2 kb
ab566f8 @laverdet npm package
authored
1 fibers(1) -- Fiber support for v8 and Node
2 ==========================================
a9e6241 @laverdet README update
authored
3
ab566f8 @laverdet npm package
authored
4 INSTALLING
5 ----------
a9e6241 @laverdet README update
authored
6
0685a72 @laverdet Documentation updates
authored
7 To install `node-fibers` just use `npm`. It's recommended that you install
8 `node-fibers` globally, as it includes a wrapper script which you must run in
9 lieu of `node`.
f23d4c9 @laverdet Documentation updates
authored
10
11 To install with **npm>=1.0**:
12
13 * `npm install -g fibers`
14 * Ensure `node-fibers` can be found in your $PATH (this should be true unless
15 you symlinked `node` somewhere).
0685a72 @laverdet Documentation updates
authored
16 * Ensure the global `node_modules` directory is in your $NODE_PATH. You might
17 have done this when you installed `npm`, but probably didn't. If this is too
18 hard you can also just do this:
19 ``NODE_ROOT=$(dirname $(dirname $(which node))); [[ $NODE_ROOT ]] && ln -s $NODE_ROOT/lib/node_modules/fibers $NODE_ROOT/lib/node/``
f23d4c9 @laverdet Documentation updates
authored
20
21 To install **without npm**:
22
0685a72 @laverdet Documentation updates
authored
23 * `make`
a9e6241 @laverdet README update
authored
24
25 Only Linux and OS X environments are supported. Windows support is theoretically
26 possible, but not planned.
27
28
ab566f8 @laverdet npm package
authored
29 GETTING STARTED
a9e6241 @laverdet README update
authored
30 ---------------
31
ab566f8 @laverdet npm package
authored
32 If you intend to use fibers, be sure to run `node-fibers` instead of `node`.
33 After that just `require('fibers');` in any Javascript file and you will have
34 fiber support.
35
36
37 EXAMPLES
38 --------
39
0685a72 @laverdet Documentation updates
authored
40 The examples below describe basic use of `Fiber`, but note that it is **not
41 recommended** to use `Fiber` without an abstraction in between your code and
42 fibers. See "FUTURES" below for additional information.
43
5a8946a @laverdet Documentation updates
authored
44 ### Sleep
ab566f8 @laverdet npm package
authored
45 This is a quick example of how you can write sleep() with fibers. Note that
46 while the sleep() call is blocking inside the fiber, node is able to handle
47 other events.
a9e6241 @laverdet README update
authored
48
ac01c73 @laverdet Spaces -> Tabs
authored
49 $ cat sleep.js
50 require('fibers');
a9e6241 @laverdet README update
authored
51
ac01c73 @laverdet Spaces -> Tabs
authored
52 function sleep(ms) {
53 var fiber = Fiber.current;
54 setTimeout(function() {
55 fiber.run();
56 }, ms);
57 yield();
58 }
a9e6241 @laverdet README update
authored
59
ac01c73 @laverdet Spaces -> Tabs
authored
60 Fiber(function() {
5a8946a @laverdet Documentation updates
authored
61 console.log('wait... ' + new Date);
ac01c73 @laverdet Spaces -> Tabs
authored
62 sleep(1000);
5a8946a @laverdet Documentation updates
authored
63 console.log('ok... ' + new Date);
ac01c73 @laverdet Spaces -> Tabs
authored
64 }).run();
5a8946a @laverdet Documentation updates
authored
65 console.log('back in main');
a9e6241 @laverdet README update
authored
66
045363a @laverdet Bump npm version to 0.2.0
authored
67 $ node-fibers sleep.js
ac01c73 @laverdet Spaces -> Tabs
authored
68 wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
69 back in main
70 ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
a9e6241 @laverdet README update
authored
71
ab566f8 @laverdet npm package
authored
72
5a8946a @laverdet Documentation updates
authored
73 ### Incremental Generator
a9e6241 @laverdet README update
authored
74 Yielding execution will resume back in the fiber right where you left off. You
0685a72 @laverdet Documentation updates
authored
75 can also pass values back and forth through yield() and run(). Again, the node
76 event loop is never blocked while this script is running.
a9e6241 @laverdet README update
authored
77
ac01c73 @laverdet Spaces -> Tabs
authored
78 $ cat generator.js
79 require('fibers');
80
81 var inc = Fiber(function(start) {
82 var total = start;
83 while (true) {
84 total += yield(total);
85 }
86 });
87
88 for (var ii = inc.run(1); ii <= 10; ii = inc.run(1)) {
5a8946a @laverdet Documentation updates
authored
89 console.log(ii);
ac01c73 @laverdet Spaces -> Tabs
authored
90 }
91
045363a @laverdet Bump npm version to 0.2.0
authored
92 $ node-fibers generator.js
ac01c73 @laverdet Spaces -> Tabs
authored
93 1
94 2
95 3
96 4
97 5
98 6
99 7
100 8
101 9
102 10
a9e6241 @laverdet README update
authored
103
ab566f8 @laverdet npm package
authored
104
5a8946a @laverdet Documentation updates
authored
105 ### Fibonacci Generator
106 Expanding on the incremental generator above, we can create a generator which
107 returns a new Fibonacci number with each invocation. You can compare this with
108 the [ECMAScript Harmony
109 Generator](http://wiki.ecmascript.org/doku.php?id=harmony:generators) Fibonacci
110 example.
111
112 $ cat fibonacci.js
113 require('fibers');
114
115 // Generator function. Returns a function which returns incrementing
116 // Fibonacci numbers with each call.
117 function Fibonacci() {
118 // Create a new fiber which yields sequential Fibonacci numbers
119 var fiber = Fiber(function() {
120 yield(0); // F(0) -> 0
121 var prev = 0, curr = 1;
122 while (true) {
123 yield(curr);
124 var tmp = prev + curr;
125 prev = curr;
126 curr = tmp;
127 }
128 });
129 // Return a bound handle to `run` on this fiber
130 return fiber.run.bind(fiber);
131 }
132
133 // Initialize a new Fibonacci sequence and iterate up to 1597
134 var seq = Fibonacci();
135 for (var ii = seq(); ii <= 1597; ii = seq()) {
136 console.log(ii);
137 }
138
139 $ node-fibers fibonacci.js
140 0
141 1
142 1
143 2
144 3
145 5
146 8
147 13
148 21
149 34
150 55
151 89
152 144
153 233
154 377
155 610
156 987
157 1597
158
159
160 ### Basic Exceptions
041247c @laverdet Documentation and other inconsequential changes
authored
161 Fibers are exception-safe; exceptions will continue travelling through fiber
162 boundaries:
163
ac01c73 @laverdet Spaces -> Tabs
authored
164 $ cat error.js
165 require('fibers');
166
167 var fn = Fiber(function() {
5a8946a @laverdet Documentation updates
authored
168 console.log('async work here...');
ac01c73 @laverdet Spaces -> Tabs
authored
169 yield();
5a8946a @laverdet Documentation updates
authored
170 console.log('still working...');
ac01c73 @laverdet Spaces -> Tabs
authored
171 yield();
5a8946a @laverdet Documentation updates
authored
172 console.log('just a little bit more...');
ac01c73 @laverdet Spaces -> Tabs
authored
173 yield();
174 throw new Error('oh crap!');
175 });
176
177 try {
178 while (true) {
179 fn.run();
180 }
181 } catch(e) {
5a8946a @laverdet Documentation updates
authored
182 console.log('safely caught that error!');
183 console.log(e.stack);
ac01c73 @laverdet Spaces -> Tabs
authored
184 }
5a8946a @laverdet Documentation updates
authored
185 console.log('done!');
ac01c73 @laverdet Spaces -> Tabs
authored
186
045363a @laverdet Bump npm version to 0.2.0
authored
187 $ node-fibers error.js
ac01c73 @laverdet Spaces -> Tabs
authored
188 async work here...
189 still working...
190 just a little bit more...
191 safely caught that error!
192 Error: oh crap!
193 at error.js:11:9
194 done!
041247c @laverdet Documentation and other inconsequential changes
authored
195
ab566f8 @laverdet npm package
authored
196
0685a72 @laverdet Documentation updates
authored
197 FUTURES
198 -------
ac01c73 @laverdet Spaces -> Tabs
authored
199
0685a72 @laverdet Documentation updates
authored
200 Using the `Fiber` class without an abstraction in between your code and the raw
201 API is **not recommended**. `Fiber` is meant to implement the smallest amount of
202 functionality in order make possible many different programming patterns. This
203 makes the `Fiber` class relatively lousy to work with directly, but extremely
204 powerful when coupled with a decent abstraction. There is no right answer for
205 which abstraction is right for you and your project. Included with `node-fibers`
206 is an implementation of "futures" which is fiber-aware. Usage of this library
207 is documented below. Other externally-maintained options include
208 [0ctave/node-sync](https://github.com/0ctave/node-sync) and
209 [lm1/node-fibers-promise](https://github.com/lm1/node-fibers-promise). However
210 you **should** feel encouraged to be creative with fibers and build a solution
5a8946a @laverdet Documentation updates
authored
211 which works well with your project. For instance, `Future` is not a good
212 abstraction to use if you want to build a generator function (see Fibonacci
213 example above).
ac01c73 @laverdet Spaces -> Tabs
authored
214
0685a72 @laverdet Documentation updates
authored
215 Using `Future` to wrap existing node functions. At no point is the node event
216 loop blocked:
ac01c73 @laverdet Spaces -> Tabs
authored
217
0685a72 @laverdet Documentation updates
authored
218 $ cat ls.js
219 var Future = require('fibers/future'), wait = Future.wait;
220 var fs = require('fs');
ac01c73 @laverdet Spaces -> Tabs
authored
221
0685a72 @laverdet Documentation updates
authored
222 // This wraps existing functions assuming the last argument of the passed
223 // function is a callback. The new functions created immediately return a
224 // future and the future will resolve when the callback is called (which
225 // happens behind the scenes).
226 var readdir = Future.wrap(fs.readdir);
227 var stat = Future.wrap(fs.stat);
ac01c73 @laverdet Spaces -> Tabs
authored
228
229 Fiber(function() {
0685a72 @laverdet Documentation updates
authored
230 // Get a list of files in the directory
231 var fileNames = readdir('.').wait();
232 console.log('Found '+ fileNames.length+ ' files');
233
234 // Stat each file
235 var stats = [];
236 for (var ii = 0; ii < fileNames.length; ++ii) {
237 stats.push(stat(fileNames[ii]));
ac01c73 @laverdet Spaces -> Tabs
authored
238 }
0685a72 @laverdet Documentation updates
authored
239 wait(stats);
ac01c73 @laverdet Spaces -> Tabs
authored
240
0685a72 @laverdet Documentation updates
authored
241 // Print file size
242 for (var ii = 0; ii < fileNames.length; ++ii) {
243 console.log(fileNames[ii]+ ': '+ stats[ii].get().size);
244 }
ac01c73 @laverdet Spaces -> Tabs
authored
245 }).run();
246
0685a72 @laverdet Documentation updates
authored
247 $ node-fibers ls.js
248 Found 11 files
249 bin: 4096
250 fibers.js: 1708
251 .gitignore: 37
252 README.md: 8664
253 future.js: 5833
254 .git: 4096
255 LICENSE: 1054
256 src: 4096
257 ls.js: 860
258 Makefile: 436
259 package.json: 684
260
261
262 The future API is designed to make it easy to move between classic
263 callback-style code and fiber-aware waiting code:
264
265 $ cat sleep.js
266 var Future = require('fibers/future'), wait = Future.wait;
267
268 // This function returns a future which resolves after a timeout. This
269 // demonstrates manually resolving futures.
270 function sleep(ms) {
271 var future = new Future;
272 setTimeout(function() {
273 future.return();
274 }, ms);
275 return future;
276 }
277
278 // You can create functions which automatically run in their own fiber and
279 // return futures that resolve when the fiber returns (this probably sounds
280 // confusing.. just play with it to understand).
281 var calcTimerDelta = function(ms) {
282 var start = new Date;
283 sleep(ms).wait();
284 return new Date - start;
285 }.future(); // <-- important!
286
287 // And futures also include node-friendly callbacks if you don't want to use
288 // wait()
289 calcTimerDelta(2000).resolve(function(err, val) {
290 console.log('Set timer for 2000ms, waited '+ val+ 'ms');
291 });
292
293 $ node-fibers sleep.js
294 Set timer for 2000ms, waited 2009ms
a9e6241 @laverdet README update
authored
295
ab566f8 @laverdet npm package
authored
296
0685a72 @laverdet Documentation updates
authored
297 API DOCUMENTATION
298 -----------------
a9e6241 @laverdet README update
authored
299 Fiber's definition looks something like this:
300
ac01c73 @laverdet Spaces -> Tabs
authored
301 /**
302 * Instantiate a new Fiber. You may invoke this either as a function or as
303 * a constructor; the behavior is the same.
304 *
305 * When run() is called on this fiber for the first time, `fn` will be
306 * invoked as the first frame on a new stack. Execution will continue on
307 * this new stack until `fn` returns, or yield() is called.
308 *
309 * After the function returns the fiber is reset to original state and
310 * may be restarted with another call to run().
311 */
312 function Fiber(fn) {
313 [native code]
314 }
315
316 /**
317 * `Fiber.current` will contain the currently-running Fiber. It will be
318 * `undefined` if there is no fiber (i.e. the main stack of execution).
319 *
320 * See "Garbage Collection" for more information on responsible use of
321 * `Fiber.current`.
322 */
323 Fiber.current = undefined;
324
325 /**
326 * yield() will halt execution of the current fiber and return control back
327 * to original caller of run(). If an argument is supplied to yield, run()
328 * will return that value.
329 *
330 * When run() is called again, yield() will return.
331 *
332 * Note that this function is a global to allow for correct garbage
333 * collection. This results in no loss of functionality because it is only
334 * valid to yield from the currently running fiber anyway.
3913a7e @laverdet Fix fibers in strict mode
authored
335 *
336 * Note also that `yield` is a reserved word in Javascript. This is normally
337 * not an issue, however if using strict mode you will not be able to call
338 * yield() globally. Instead call `Fiber.yield()`.
ac01c73 @laverdet Spaces -> Tabs
authored
339 */
340 function yield(param) {
341 [native code]
342 }
3913a7e @laverdet Fix fibers in strict mode
authored
343 Fiber.yield = yield;
ac01c73 @laverdet Spaces -> Tabs
authored
344
345 /**
346 * run() will start execution of this Fiber, or if it is currently yielding,
347 * it will resume execution. If an argument is supplied, this argument will
348 * be passed to the fiber, either as the first parameter to the main
349 * function [if the fiber has not been started] or as the return value of
350 * yield() [if the fiber is currently yielding].
351 *
352 * This function will return either the parameter passed to yield(), or the
353 * returned value from the fiber's main function.
354 */
355 Fiber.prototype.run = function(param) {
356 [native code]
357 }
358
359 /**
360 * reset() will terminate a running Fiber and restore it to its original
361 * state, as if it had returned execution.
362 *
363 * This is accomplished by causing yield() to throw an exception, and any
364 * futher calls to yield() will also throw an exception. This continues
365 * until the fiber has completely unwound and returns.
366 *
367 * If the fiber returns a value it will be returned by reset().
368 *
369 * If the fiber is not running, reset() will have no effect.
370 */
371 Fiber.prototype.reset = function() {
372 [native code]
373 }
374
375 /**
376 * throwInto() will cause a currently yielding fiber's yield() call to
377 * throw instead of return gracefully. This can be useful for notifying a
378 * fiber that you are no longer interested in its task, and that it should
379 * give up.
380 *
381 * Note that if the fiber does not handle the exception it will continue to
382 * bubble up and throwInto() will throw the exception right back at you.
383 */
384 Fiber.prototype.throwInto = function(exception) {
385 [native code]
386 }
a9e6241 @laverdet README update
authored
387
388
ab566f8 @laverdet npm package
authored
389 GARBAGE COLLECTION
a9e6241 @laverdet README update
authored
390 ------------------
391
5a8946a @laverdet Documentation updates
authored
392 If you intend to build generators, iterators, or "lazy lists", you should be
393 aware that all fibers must eventually unwind. This is implemented by causing
394 yield() to throw unconditionally when the library is trying to unwind your
395 fiber-- either because reset() was called, or all handles to the fiber were lost
396 and v8 wants to delete it.
a9e6241 @laverdet README update
authored
397
398 Something like this will, at some point, cause an infinite loop in your
399 application:
400
ac01c73 @laverdet Spaces -> Tabs
authored
401 var fiber = Fiber(function() {
402 while (true) {
403 try {
404 yield();
405 } catch(e) {}
406 }
407 });
408 fiber.run();
a9e6241 @laverdet README update
authored
409
410 If you either call reset() on this fiber, or the v8 garbage collector decides it
411 is no longer in use, the fiber library will attempt to unwind the fiber by
412 causing all calls to yield() to throw. However, if you catch these exceptions
413 and continue anyway, an infinite loop will occur.
414
415 There are other garbage collection issues that occur with misuse of fiber
416 handles. If you grab a handle to a fiber from within itself, you should make
417 sure that the fiber eventually unwinds. This application will leak memory:
418
ac01c73 @laverdet Spaces -> Tabs
authored
419 var fiber = Fiber(function() {
420 var that = Fiber.current;
421 yield();
422 }
423 fiber.run();
424 fiber = undefined;
a9e6241 @laverdet README update
authored
425
426 There is no way to get back into the fiber that was started, however it's
427 impossible for v8's garbage collector to detect this. With a handle to the fiber
428 still outstanding, v8 will never garbage collect it and the stack will remain in
429 memory until the application exits.
430
431 Thus, you should take care when grabbing references to `Fiber.current`.
Something went wrong with that request. Please try again.