Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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