Skip to content
This repository
Newer
Older
100644 327 lines (271 sloc) 9.664 kb
ab566f83 »
2011-01-23 npm package
1 fibers(1) -- Fiber support for v8 and Node
2 ==========================================
a9e6241d »
2011-01-22 README update
3
ab566f83 »
2011-01-23 npm package
4 INSTALLING
5 ----------
a9e6241d »
2011-01-22 README update
6
ab566f83 »
2011-01-23 npm package
7 To install node-fibers:
a9e6241d »
2011-01-22 README update
8
ab566f83 »
2011-01-23 npm package
9 npm install fibers
a9e6241d »
2011-01-22 README update
10
11 Only Linux and OS X environments are supported. Windows support is theoretically
12 possible, but not planned.
13
14
ab566f83 »
2011-01-23 npm package
15 GETTING STARTED
a9e6241d »
2011-01-22 README update
16 ---------------
17
ab566f83 »
2011-01-23 npm package
18 If you intend to use fibers, be sure to run `node-fibers` instead of `node`.
19 After that just `require('fibers');` in any Javascript file and you will have
20 fiber support.
21
22
23 EXAMPLES
24 --------
25
26 This is a quick example of how you can write sleep() with fibers. Note that
27 while the sleep() call is blocking inside the fiber, node is able to handle
28 other events.
a9e6241d »
2011-01-22 README update
29
30 $ cat sleep.js
ab566f83 »
2011-01-23 npm package
31 require('fibers');
041247c8 »
2011-01-22 Documentation and other inconsequential changes
32 var print = require('util').print;
a9e6241d »
2011-01-22 README update
33
34 function sleep(ms) {
35 var fiber = Fiber.current;
36 setTimeout(function() {
37 fiber.run();
38 }, ms);
39 yield();
40 }
41
42 Fiber(function() {
041247c8 »
2011-01-22 Documentation and other inconsequential changes
43 print('wait... ' + new Date + '\n');
a9e6241d »
2011-01-22 README update
44 sleep(1000);
041247c8 »
2011-01-22 Documentation and other inconsequential changes
45 print('ok... ' + new Date + '\n');
a9e6241d »
2011-01-22 README update
46 }).run();
041247c8 »
2011-01-22 Documentation and other inconsequential changes
47 print('back in main\n');
a9e6241d »
2011-01-22 README update
48
ab566f83 »
2011-01-23 npm package
49 $ ./node-fibers sleep.js
a9e6241d »
2011-01-22 README update
50 wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
51 back in main
52 ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
53
ab566f83 »
2011-01-23 npm package
54
a9e6241d »
2011-01-22 README update
55 Yielding execution will resume back in the fiber right where you left off. You
56 can also pass values back and forth through yield() and run().
57
58 $ cat generator.js
ab566f83 »
2011-01-23 npm package
59 require('fibers');
041247c8 »
2011-01-22 Documentation and other inconsequential changes
60 var print = require('util').print;
a9e6241d »
2011-01-22 README update
61
62 var inc = Fiber(function(start) {
63 var total = start;
64 while (true) {
65 total += yield(total);
66 }
67 });
68
69 for (var ii = inc.run(1); ii <= 10; ii = inc.run(1)) {
041247c8 »
2011-01-22 Documentation and other inconsequential changes
70 print(ii + '\n');
a9e6241d »
2011-01-22 README update
71 }
72
ab566f83 »
2011-01-23 npm package
73 $ ./node-fibers generator.js
a9e6241d »
2011-01-22 README update
74 1
75 2
76 3
77 4
78 5
79 6
80 7
81 8
82 9
83 10
84
ab566f83 »
2011-01-23 npm package
85
041247c8 »
2011-01-22 Documentation and other inconsequential changes
86 Fibers are exception-safe; exceptions will continue travelling through fiber
87 boundaries:
88
89 $ cat error.js
ab566f83 »
2011-01-23 npm package
90 require('fibers');
041247c8 »
2011-01-22 Documentation and other inconsequential changes
91 var print = require('util').print;
92
93 var fn = Fiber(function() {
94 print('async work here...\n');
95 yield();
96 print('still working...\n');
97 yield();
98 print('just a little bit more...\n');
99 yield();
100 throw new Error('oh crap!');
101 });
102
103 try {
104 while (true) {
105 fn.run();
106 }
107 } catch(e) {
108 print('safely caught that error!\n');
109 print(e.stack + '\n');
110 }
111 print('done!\n');
112
ab566f83 »
2011-01-23 npm package
113 $ ./node-fibers error.js
041247c8 »
2011-01-22 Documentation and other inconsequential changes
114 async work here...
115 still working...
116 just a little bit more...
117 safely caught that error!
118 Error: oh crap!
119 at error.js:11:9
120 done!
121
ab566f83 »
2011-01-23 npm package
122
041247c8 »
2011-01-22 Documentation and other inconsequential changes
123 You can use fibers to provide synchronous adapters on top of asynchronous
124 functions:
125
126 $ cat adapter.js
ab566f83 »
2011-01-23 npm package
127 require('fibers');
041247c8 »
2011-01-22 Documentation and other inconsequential changes
128 var print = require('util').print;
129
130 // This function runs an asynchronous function from within a fiber as if it
131 // were synchronous.
132 function asyncAsSync(fn /* ... */) {
133 var args = [].slice.call(arguments, 1);
134 var fiber = Fiber.current;
135
136 function cb(err, ret) {
137 if (err) {
138 fiber.throwInto(new Error(err));
139 } else {
140 fiber.run(ret);
141 }
142 }
143
144 // Little-known JS features: a function's `length` property is the number
145 // of arguments it takes. Node convention is that the last parameter to
146 // most asynchronous is a `function callback(err, ret) {}`.
147 args[fn.length - 1] = cb;
148 fn.apply(null, args);
149
150 return yield();
151 }
152
153 var fs = require('fs');
154 var Buffer = require('buffer').Buffer;
155 Fiber(function() {
156 // These are all async functions (fs.open, fs.write, fs.close) but we can
157 // use them as if they're synchronous.
158 print('opening /tmp/hello\n');
159 var file = asyncAsSync(fs.open, '/tmp/hello', 'w');
160 var buffer = new Buffer(5);
161 buffer.write('hello');
162 print('writing to file\n');
163 asyncAsSync(fs.write, file, buffer, 0, buffer.length);
164 print('closing file\n');
165 asyncAsSync(fs.close, file);
166
167 // This is a synchronous function. But note that while this function is
168 // running node is totally blocking. Using `asyncAsSync` leaves node
169 // available to handle more events.
170 var data = fs.readFileSync('/tmp/hello');
171 print('file contents: ' +data +'\n');
172
173 // Errors made simple using the magic of exceptions
174 try {
175 print('deleting /tmp/hello2\n');
176 asyncAsSync(fs.unlink, '/tmp/hello2');
177 } catch(e) {
178 print('caught this exception: ' +e.message +'\n');
179 }
180
181 // Cleanup :)
182 print('deleting /tmp/hello\n');
183 asyncAsSync(fs.unlink, '/tmp/hello');
184 }).run();
185 print('returning control to node event loop\n');
186
ab566f83 »
2011-01-23 npm package
187 $ ./node-fibers adapter.js
041247c8 »
2011-01-22 Documentation and other inconsequential changes
188 opening /tmp/hello
189 returning control to node event loop
190 writing to file
191 closing file
192 file contents: hello
193 deleting /tmp/hello2
194 caught this exception: Error: ENOENT, No such file or directory '/tmp/hello2'
195 deleting /tmp/hello
a9e6241d »
2011-01-22 README update
196
ab566f83 »
2011-01-23 npm package
197
198 DOCUMENTATION
a9e6241d »
2011-01-22 README update
199 -------------
200 Fiber's definition looks something like this:
201
202 /**
203 * Instantiate a new Fiber. You may invoke this either as a function or as
204 * a constructor; the behavior is the same.
205 *
206 * When run() is called on this fiber for the first time, `fn` will be
207 * invoked as the first frame on a new stack. Execution will continue on
208 * this new stack until `fn` returns, or yield() is called.
209 *
210 * After the function returns the fiber is reset to original state and
211 * may be restarted with another call to run().
212 */
213 function Fiber(fn) {
214 [native code]
215 }
216
217 /**
218 * `Fiber.current` will contain the currently-running Fiber. It will be
219 * `undefined` if there is no fiber (i.e. the main stack of execution).
220 *
221 * See "Garbage Collection" for more information on responsible use of
222 * `Fiber.current`.
223 */
224 Fiber.current = undefined;
225
226 /**
227 * yield() will halt execution of the current fiber and return control back
228 * to original caller of run(). If an argument is supplied to yield, run()
229 * will return that value.
230 *
231 * When run() is called again, yield() will return.
232 *
233 * Note that this function is a global to allow for correct garbage
234 * collection. This results in no loss of functionality because it is only
235 * valid to yield from the currently running fiber anyway.
236 */
237 function yield(param) {
238 [native code]
239 }
240
241 /**
242 * run() will start execution of this Fiber, or if it is currently yielding,
243 * it will resume execution. If an argument is supplied, this argument will
244 * be passed to the fiber, either as the first parameter to the main
245 * function [if the fiber has not been started] or as the return value of
246 * yield() [if the fiber is currently yielding].
247 *
248 * This function will return either the parameter passed to yield(), or the
249 * returned value from the fiber's main function.
250 */
251 Fiber.prototype.run = function(param) {
252 [native code]
253 }
254
255 /**
256 * reset() will terminate a running Fiber and restore it to its original
257 * state, as if it had returned execution.
258 *
041247c8 »
2011-01-22 Documentation and other inconsequential changes
259 * This is accomplished by causing yield() to throw an exception, and any
a9e6241d »
2011-01-22 README update
260 * futher calls to yield() will also throw an exception. This continues
261 * until the fiber has completely unwound and returns.
262 *
263 * If the fiber returns a value it will be returned by reset().
264 *
265 * If the fiber is not running, reset() will have no effect.
266 */
267 Fiber.prototype.reset = function() {
268 [native code]
269 }
270
271 /**
272 * throwInto() will cause a currently yielding fiber's yield() call to
273 * throw instead of return gracefully. This can be useful for notifying a
274 * fiber that you are no longer interested in its task, and that it should
275 * give up.
276 *
277 * Note that if the fiber does not handle the exception it will continue to
278 * bubble up and throwInto() will throw the exception right back at you.
279 */
280 Fiber.prototype.throwInto = function(exception) {
281 [native code]
282 }
283
284
ab566f83 »
2011-01-23 npm package
285 GARBAGE COLLECTION
a9e6241d »
2011-01-22 README update
286 ------------------
287
288 If you intend to use "lazy lists", you should be aware that all fibers must
289 eventually unwind. This is implemented by causing yield() to throw
290 unconditionally when the library is trying to unwind your fiber-- either
291 because reset() was called, or all handles to the fiber were lost and v8 wants
292 to delete it.
293
294 Something like this will, at some point, cause an infinite loop in your
295 application:
296
297 var fiber = Fiber(function() {
298 while (true) {
299 try {
300 yield();
301 } catch(e) {}
302 }
303 });
304 fiber.run();
305
306 If you either call reset() on this fiber, or the v8 garbage collector decides it
307 is no longer in use, the fiber library will attempt to unwind the fiber by
308 causing all calls to yield() to throw. However, if you catch these exceptions
309 and continue anyway, an infinite loop will occur.
310
311 There are other garbage collection issues that occur with misuse of fiber
312 handles. If you grab a handle to a fiber from within itself, you should make
313 sure that the fiber eventually unwinds. This application will leak memory:
314
315 var fiber = Fiber(function() {
316 var that = Fiber.current;
317 yield();
318 }
319 fiber.run();
320 fiber = undefined;
321
322 There is no way to get back into the fiber that was started, however it's
323 impossible for v8's garbage collector to detect this. With a handle to the fiber
324 still outstanding, v8 will never garbage collect it and the stack will remain in
325 memory until the application exits.
326
327 Thus, you should take care when grabbing references to `Fiber.current`.
Something went wrong with that request. Please try again.