Skip to content

HTTPS clone URL

Subversion checkout URL

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