1 | import Fiber = require('../fibers');
|
2 | import RunContext = require('./runContext');
|
3 | export = FiberManager;
|
4 |
|
5 |
|
6 | /** Helpers for Fiber management. */
|
7 | module FiberManager {
|
8 |
|
9 | /** Returns true if the current execution context is within a fiber. */
|
10 | export function isExecutingInFiber() {
|
11 | return !!Fiber.current;
|
12 | }
|
13 |
|
14 | /** Creates and returns a new fiber in which an arbitrary function may be executed. */
|
15 | export function create(): FiberEx {
|
16 | return Fiber(runInFiber);
|
17 | }
|
18 |
|
19 | export interface FiberEx extends Fiber {
|
20 |
|
21 | /**
|
22 | * Executes the wrapped function specified in the RunContext instance. The final
|
23 | * return/throw value of the wrapped function is used to notify the promise resolver
|
24 | * and/or callback specified in the RunContext.
|
25 | */
|
26 | run(runCtx: RunContext): void;
|
27 | }
|
28 | }
|
29 |
|
30 |
|
31 | /**
|
32 | * The runInFiber() function provides the prolog/epilog wrapper code for running a function inside
|
33 | * a fiber. The runInFiber() function accepts a RunContext instance, and calls the wrapped function
|
34 | * specified there. The final return/throw value of the wrapped function is used to notify the
|
35 | * promise resolver and/or callback specified in the RunContext. This function must take all its
|
36 | * information in a single argument because it is called via Fiber#run(), which accepts one argument.
|
37 | * NB: Since try/catch/finally prevents V8 optimisations, the function is split into several parts.
|
38 | */
|
39 | function runInFiber(runCtx: RunContext) {
|
40 | try { tryBlock(runCtx); }
|
41 | catch (err) { catchBlock(runCtx, err); }
|
42 | finally { finallyBlock(runCtx); }
|
43 | }
|
44 | function tryBlock(runCtx: RunContext) {
|
45 |
|
46 | // Maintain an accurate count of currently active fibers, for pool management.
|
47 | adjustFiberCount(+1);
|
48 |
|
49 | // Call the wrapped function. It may be suspended several times (at await and/or yield calls).
|
50 | var result = runCtx.wrapped.apply(runCtx.thisArg, runCtx.argsAsArray);
|
51 |
|
52 | // The wrapped function returned normally. Notify any waiters.
|
53 | if (runCtx.callback) runCtx.callback(null, result);
|
54 | if (runCtx.resolver) runCtx.resolver.resolve(result);
|
55 | }
|
56 | function catchBlock(runCtx: RunContext, err) {
|
57 |
|
58 | // The wrapped function threw an exception. Notify any waiters.
|
59 | if (runCtx.callback) runCtx.callback(err);
|
60 | if (runCtx.resolver) runCtx.resolver.reject(err);
|
61 | }
|
62 | function finallyBlock(runCtx: RunContext) {
|
63 |
|
64 | // Maintain an accurate count of currently active fibers, for pool management.
|
65 | adjustFiberCount(-1);
|
66 |
|
67 | // Execute the done() callback, if provided.
|
68 | if (runCtx.done) runCtx.done();
|
69 | }
|
70 |
|
71 |
|
72 |
|
73 |
|
74 | /**
|
75 | * The following functionality prevents memory leaks in node-fibers by actively managing Fiber.poolSize.
|
76 | * For more information, see https://github.com/laverdet/node-fibers/issues/169.
|
77 | */
|
78 | function adjustFiberCount(delta: number) {
|
79 | activeFiberCount += delta;
|
80 | if (activeFiberCount >= fiberPoolSize) {
|
81 | fiberPoolSize += 100;
|
82 | Fiber.poolSize = fiberPoolSize;
|
83 | }
|
84 | }
|
85 | var fiberPoolSize = Fiber.poolSize;
|
86 | var activeFiberCount = 0;
|