PageSourceSearch

https://cdn4.hola.org/util/etask.js

js hola.org collected 2026-09-24 10:17:23 UTC 53,781 bytes, 1,781 lines download raw bytes

1// LICENSE_CODE ZON ISC
2'use strict'; /*jslint node:true, browser:true, es6: true*/
3(function(){
4var define, process, zerr, assert;
5var is_node = typeof module=='object' && module.exports && module.children &&
6    typeof __webpack_require__!='function';
7if (!is_node)
8{
9    define = self.define;
10    process = {
11        nextTick: function(fn){ setTimeout(fn, 0); },
12        env: {},
13    };
14    // XXX romank: use zerr.js
15    // XXX bahaa: require bext/pub/zerr.js for extensions
16    if (self.hola && self.hola.zerr)
17        zerr = self.hola.zerr;
18    else
19    {
20        zerr = function(){ console.log.apply(console, arguments); };
21        zerr.perr = zerr;
22        zerr.debug = function(){};
23        zerr.is = function(){ return false; };
24        zerr.L = {DEBUG: 0};
25    }
26    if (!zerr.is)
27        zerr.is = function(){ return false; };
28}
29else
30{
31    require('./config.js');
32    process = global.process||require('_process');
33    zerr = require('./zerr.js');
34    assert = require('assert');
35    define = require('./require_node.js').define(module, '../');
36}
37// XXX odin: normally this would only be run for !is_node, but 'who' unittests
38// loads a stubbed assert
39if (typeof assert!='function')
40    assert = function(){}; // XXX romank: add proper assert
41// XXX yuval: /util/events.js -> events when node 6 (support prependListener)
42// is here
43define(['/util/events.js', '/util/array.js', '/util/util.js'],
44    function(events, array, zutil){
45var E = Etask;
46var GEN_FN = 'GeneratorFunction';
47var env = process.env, assign = Object.assign;
48E.use_bt = +env.ETASK_BT;
49E.root = new Set();
50E.assert_extra = +env.ETASK_ASSERT_EXTRA; // to debug internal etask bugs
51E.nextTick = process.nextTick;
52// XXX arik/romank: hack, rm set_zerr, get zerzerrusing require
53E.set_zerr = function(_zerr){ zerr = _zerr; };
54E.events = new events();
55var cb_pre, cb_post, cb_ctx, longcb_ms, perf_enable;
56E.perf_stat = {};
57function _cb_pre(et){ return {start: Date.now()}; }
58function _cb_post(et, ctx){
59    ctx = ctx||cb_ctx;
60    var ms = Date.now()-ctx.start;
61    if (longcb_ms && ms>longcb_ms)
62    {
63        zerr('long cb '+ms+'ms: '+et.get_name()+', '
64            +et.funcs[et.cur_state].toString().slice(0, 128));
65    }
66    if (perf_enable)
67    {
68        var name = et.get_name();
69        var perf = E.perf_stat[name] ||
70            (E.perf_stat[name] = {ms: 0, n: 0, max: 0});
71        if (perf.max<ms)
72            perf.max = ms;
73        perf.ms += ms;
74        perf.n++;
75    }
76}
77function cb_set(){
78    if (longcb_ms || perf_enable)
79    {
80        cb_pre = _cb_pre;
81        cb_post = _cb_post;
82        cb_ctx = {start: Date.now()};
83    }
84    else
85        cb_pre = cb_post = cb_ctx = undefined;
86}
87E.longcb = function(ms){
88    longcb_ms = ms;
89    cb_set();
90};
91E.perf = function(enable){
92    if (arguments.length)
93    {
94        perf_enable = enable;
95        cb_set();
96    }
97    return perf_enable;
98};
99E.longcb(+env.LONGCB);
100E.perf(+env.ETASK_PERF);
101
102function stack_get(){
103    // new Error(): 200K per second
104    // http://jsperf.com/error-generation
105    // Function.caller (same as arguments.callee.caller): 2M per second
106    // http://jsperf.com/does-function-caller-affect-preformance
107    // http://jsperf.com/the-arguments-object-s-effect-on-speed/2
108    var prev = Error.stackTraceLimit, err;
109    Error.stackTraceLimit = 4;
110    err = new Error();
111    Error.stackTraceLimit = prev;
112    return err;
113}
114
115function Etask(opt, states){
116    if (!(this instanceof Etask))
117    {
118        if (Array.isArray(opt) || typeof opt=='function')
119        {
120            states = opt;
121            opt = undefined;
122        }
123        opt = typeof opt=='string' && {name: opt} || opt || {};
124        if (typeof states=='function' && states.constructor.name==GEN_FN)
125            return E._generator(null, states, opt);
126        return new Etask(opt, typeof states=='function' ? [states] : states);
127    }
128    assert(Array.isArray(states), 'states must be an array');
129    events.EventEmitter.call(this);
130    // init fields
131    this.name = opt.name;
132    this.cancelable = opt.cancel;
133    this.then_waiting = new Set();
134    this.child = new Set();
135    this.child_guess = new Set();
136    this.cur_state = -1;
137    this.next_state = -1;
138    this.state0_args = opt.state0_args;
139    this.states_idx = {};
140    this.tm_create = Date.now();
141    this.use_retval = false;
142    this.at_return = false;
143    this.free = false;
144    this._stack = Etask.use_bt ? stack_get() : undefined;
145    this._finally = -1;
146    this._cancel = -1;
147    if (opt.zexit_on_err!=null)
148        this.info.zexit_on_err = opt.zexit_on_err;
149    if (opt.skip_err_metrics!=null)
150        this.info.skip_err_metrics = opt.skip_err_metrics;
151    // performance: set all rest fields to undefined
152    this.error = this.running = this.at_continue = this.wait_timer =
153    this.retval = this.down = this.up = this.parent = this._alarm =
154    this.tm_completed = this.parent_type = this.parent_guess =
155    this.wait_retval = this.generator = this.generator_ctor = undefined;
156    this.funcs = states;
157    this.states = [];
158    for (var i=0; i<states.length; ++i)
159    {
160        var func = states[i];
161        assert(typeof func=='function', 'invalid state type');
162        var type = this._get_state_type(func.name, undefined);
163        if (type.label)
164            this.states_idx[type.label] = i;
165        if (type.finally)
166        {
167            assert(this._finally==-1, 'more than 1 finally$');
168            this._finally = i;
169        }
170        if (type.cancel)
171        {
172            assert(this._cancel==-1, 'more than 1 cancel$');
173            this._cancel = i;
174        }
175        this.states.push(type);
176    }
177    E.root.add(this);
178    var in_run;
179    if (opt.spawn_parent)
180        this.spawn_parent(opt.spawn_parent);
181    else if (opt.up)
182        opt.up._set_down(this);
183    else if (in_run = E.in_run_top())
184        this._spawn_parent_guess(in_run);
185    if (opt.init)
186        opt.init.call(this);
187    if (opt.async)
188    {
189        var _this = this;
190        var wait_retval = this._set_wait_retval();
191        E.nextTick(function(){
192            if (_this.running===undefined)
193                _this._got_retval(wait_retval);
194        });
195    }
196    else if (this._next())
197        this._run();
198    return this;
199}
200zutil.inherits(Etask, events.EventEmitter);
201
202Object.defineProperty(E.prototype, 'info', {
203    get: function(){
204        if (!this._info)
205            this._info = {};
206        return this._info;
207    },
208    set: function(v){
209        this._info = v;
210    },
211});
212
213E.prototype._root_remove = function(){
214    assert(!this.parent, 'cannot remove from root when has parent');
215    if (!E.root.delete(this))
216        assert(0, 'etask not in root\n'+E.ps({MARK: this}));
217};
218
219E.prototype._parent_remove = function(){
220    if (this.up)
221    {
222        var up = this.up;
223        this.up = this.up.down = undefined;
224        if (up.tm_completed)
225            up._check_free();
226        return;
227    }
228    if (this.parent_guess)
229        this._parent_guess_remove();
230    if (!this.parent)
231        return this._root_remove();
232    if (!this.parent.child.delete(this))
233    {
234        assert(0, 'etask child not in parent\n'
235            +E.ps({MARK: [['child', this], ['parent', this.parent]]}));
236    }
237    if (this.parent.tm_completed)
238        this.parent._check_free();
239    this.parent = undefined;
240};
241
242E.prototype._check_free = function(){
243    if (this.down || this.child.size)
244        return;
245    this._parent_remove();
246    this.free = true;
247};
248
249E.prototype._call_err = function(e){
250    E.ef(e, this);
251    // XXX derry: add assert(0, 'etask err in signal: '+e);
252};
253E.prototype.emit_safe = function(){
254    try { this.emit.apply(this, arguments); }
255    catch(e){ this._call_err(e); }
256};
257E.prototype._call_safe = function(state_fn){
258    try { return state_fn.call(this); }
259    catch(e){ this._call_err(e); }
260};
261E.prototype._complete = function(){
262    if (zerr.is(zerr.L.DEBUG))
263        zerr.debug(this.shortname()+': close');
264    this.tm_completed = Date.now();
265    this.parent_type = this.up ? 'call' : 'spawn';
266    if (this.error)
267        this.emit_safe('uncaught', this.error);
268    if (this._finally!==-1)
269    {
270        var ret = this._call_safe(this.funcs[this._finally]);
271        if (E.is_err(ret))
272            this._set_retval(ret);
273    }
274    this.emit_safe('finally');
275    this.emit_safe('ensure');
276    if (this.error && !this.up && !this.parent && !this.parent_guess)
277        E.events.emit('uncaught', this);
278    if (this.parent)
279        this.parent.emit('child', this);
280    if (this.up && (this.down || this.child.size))
281    {
282        var up = this.up;
283        this.up = this.up.down = undefined;
284        this.parent = up;
285        up.child.add(this);
286    }
287    this._check_free();
288    this._del_wait_timer();
289    this.del_alarm();
290    this._ecancel_child();
291    this.emit_safe('finally1');
292    for (let v of this.then_waiting.values())
293    {
294        this.then_waiting.delete(v);
295        v();
296    }
297};
298E.prototype._next = function(rv){
299    if (this.tm_completed)
300        return false;
301    var states = this.states;
302    var state = this.at_return ? states.length :
303        this.next_state!=-1 ? this.next_state : this.cur_state+1;
304    this.retval = rv&&rv.ret;
305    this.error = rv&&rv.err;
306    if (this.error!==undefined)
307    {
308        if (zerr.on_exception)
309            zerr.on_exception(this.error, this);
310        if (this.cur_state>-1 && states[this.cur_state].try_catch)
311        {
312            this.use_retval = true;
313            for (; state<states.length && states[state].sig; state++);
314        }
315        else
316            for (; state<states.length && !states[state].catch; state++);
317    }
318    else
319        for (; state<states.length && states[state].aux; state++);
320    this.cur_state = state;
321    this.next_state = -1;
322    if (this.cur_state<states.length)
323        return true;
324    this._complete();
325    return false;
326};
327E.prototype._handle_rv = function(rv){
328    var wait_retval, _this = this, ret = rv.ret;
329    if (ret===this.retval); // fast-path: retval already set
330    else if (!ret);
331    else if (ret instanceof Etask)
332    {
333        if (!ret.tm_completed)
334        {
335            this._set_down(ret);
336            wait_retval = this._set_wait_retval();
337            ret.then_waiting.add(function(){
338                _this._got_retval(wait_retval, E.err_res(ret.error,
339                    ret.retval));
340            });
341            return true;
342        }
343        rv.err = ret.error;
344        rv.ret = ret.retval;
345    }
346    else if (ret instanceof Etask_err)
347    {
348        rv.err = ret.error;
349        rv.ret = undefined;
350    }
351    else if (typeof ret=='object')
352    {
353        if (typeof ret.then=='function') // promise
354        {
355            wait_retval = this._set_wait_retval();
356            ret.then(function(_ret){ _this._got_retval(wait_retval, _ret); },
357                function(err){ _this._got_retval(wait_retval, E.err(err)); });
358            return true;
359        }
360        // generator
361        else if (typeof ret.next=='function' && typeof ret.throw=='function')
362        {
363            rv.ret = E._generator(ret, this.funcs[this.cur_state], {});
364            return this._handle_rv(rv);
365        }
366    }
367    return false;
368};
369E.prototype._set_retval = function(ret){
370    if (ret===this.retval && !this.error); // fast-path retval already set
371    else if (!ret)
372    {
373        this.retval = ret;
374        this.error = undefined;
375    }
376    else if (ret instanceof Etask)
377    {
378        if (ret.tm_completed)
379        {
380            this.retval = ret.retval;
381            this.error = ret.error;
382        }
383    }
384    else if (ret instanceof Etask_err)
385    {
386        this.retval = undefined;
387        this.error = ret.error;
388    }
389    else if (typeof ret.then=='function'); // promise
390    // generator
391    else if (typeof ret.next=='function' && typeof ret.throw=='function');
392    else
393    {
394        this.retval = ret;
395        this.error = undefined;
396    }
397    return ret;
398};
399
400E.prototype._set_wait_retval = function(){
401    return this.wait_retval = new Etask_wait(this, 'wait_int'); };
402E.in_run = [];
403E.in_run_top = function(){ return E.in_run[E.in_run.length-1]; };
404E.prototype._run = function(){
405    var rv = {ret: undefined, err: undefined};
406    var _cb_ctx;
407    while (1)
408    {
409        this.running = true;
410        rv.ret = rv.err = undefined;
411        E.in_run.push(this);
412        if (zerr.is(zerr.L.DEBUG))
413            zerr.debug(this.shortname()+':S'+this.cur_state+': running');
414        if (cb_pre)
415            _cb_ctx = cb_pre(this);
416        try {
417            rv.ret = this.state0_args && this.cur_state==0
418                ? this.funcs[this.cur_state].apply(this, this.state0_args)
419                : this.funcs[this.cur_state].call(this,
420                    this.error && !this.use_retval ? this.error : this.retval);
421        } catch(e){
422            rv.err = e;
423            if (rv.err instanceof Error)
424                rv.err.etask = this;
425        }
426        this.use_retval = false;
427        if (cb_post)
428            cb_post(this, _cb_ctx);
429        this.running = false;
430        E.in_run.pop();
431        for (let vv of this.child_guess.values())
432        {
433            this.child_guess.delete(vv);
434            vv.parent_guess = undefined;
435        }
436        if (rv.ret instanceof Etask_wait)
437        {
438            var wait_completed = false, wait = rv.ret;
439            if (!this.at_continue && !wait.ready)
440            {
441                this.wait_retval = wait;
442                if (wait.op=='wait_child')
443                     wait_completed = this._set_wait_child(wait);
444                if (wait.timeout)
445                    this._set_wait_timer(wait.timeout);
446                if (!wait_completed)
447                    return;
448                this.wait_retval = undefined;
449            }
450            rv.ret = this.at_continue ? this.at_continue.ret :
451                wait.ready && !wait.completed ? wait.ready.ret : undefined;
452            wait.completed = true;
453        }
454        this.at_continue = undefined;
455        if (this._handle_rv(rv))
456            return;
457        if (!this._next(rv))
458            return;
459    }
460};
461
462E.prototype._set_down = function(down){
463    if (this.down)
464        assert(0, 'caller already has a down\n'+this.ps());
465    if (down.parent_guess)
466        down._parent_guess_remove();
467    assert(!down.parent, 'returned etask already has a spawn parent');
468    assert(!down.up,
469        'returned etask already has a caller parent, consider using wait_ext');
470    down._parent_remove();
471    this.down = down;
472    down.up = this;
473};
474
475var state_type_cache = {};
476E.prototype._get_state_type = function(name, on_fail){
477    var type = state_type_cache[name];
478    if (type)
479        return type;
480    type = state_type_cache[name] = new Etask_state_type();
481    if (!name)
482        return type;
483    type.name = name;
484    var n = name.split('$');
485    if (n.length==1)
486    {
487        type.label = n[0];
488        return type;
489    }
490    if (n.length>2)
491        return type;
492    if (n[1].length)
493        type.label = n[1];
494    var f = n[0].split('_');
495    for (var j=0; j<f.length; j++)
496    {
497        if (f[j]=='try')
498        {
499            type.try_catch = true;
500            if (j+1<f.length && f[j+1]=='catch')
501                j++;
502        }
503        else if (f[j]=='catch')
504            type['catch'] = true;
505        else if (f[j]=='finally' || f[j]=='ensure')
506            type.finally = true;
507        else if (f[j]=='cancel')
508            type.cancel = true;
509        else
510        {
511            return void (on_fail||assert.bind(null, false))(
512                'unknown func name '+name);
513        }
514    }
515    if ((+(type.catch||type.try_catch))+(+type.finally)+(+type.cancel)>1)
516    {
517        return void (on_fail||assert.bind(null, false))(
518            'invalid multiple state types');
519    }
520    type.sig = type.finally||type.cancel;
521    type.aux = type.sig||type.catch;
522    return type;
523};
524class Etask_state_type {
525    constructor(){
526        this.name = undefined;
527        this.label = undefined;
528        this.try_catch = false;
529        this.catch = false;
530        this.finally = false;
531        this.cancel = false;
532        this.sig = false;
533        this.aux = false;
534    }
535}
536
537E.prototype.spawn = function(child, replace){
538    if (!(child instanceof Etask) && child && typeof child.then=='function')
539    {
540        var promise = child;
541        child = new Etask({}, [function(){ return promise; }]);
542    }
543    if (!(child instanceof Etask)) // promise already completed?
544    {
545        this.emit('child', child);
546        return child;
547    }
548    if (!replace && child.parent)
549        assert(0, 'child already has a parent\n'+child.parent.ps());
550    child.spawn_parent(this);
551    return child;
552};
553
554E.prototype._spawn_parent_guess = function(parent){
555    this.parent_guess = parent;
556    parent.child_guess.add(this);
557};
558E.prototype._parent_guess_remove = function(){
559    if (!this.parent_guess.child_guess.delete(this))
560    {
561        assert(0, 'etask not in parent_guess\n'+this.ps({MARK: this})+'\n'
562            +E.ps({MARK: this}));
563    }
564    this.parent_guess = undefined;
565};
566E.prototype.spawn_parent = function(parent){
567    if (this.up)
568        assert(0, 'child already has an up\n'+this.up.ps());
569    if (this.tm_completed && !this.parent)
570        return;
571    this._parent_remove();
572    if (parent && parent.free)
573        parent = undefined;
574    if (!parent)
575        return void E.root.add(this);
576    parent.child.add(this);
577    this.parent = parent;
578};
579
580E.prototype.set_state = function(name){
581    var state = this.states_idx[name];
582    assert(state!==undefined, 'named func "'+name+'" not found');
583    return this.next_state = state;
584};
585
586E.prototype.finally = function(cb){
587    if (this.tm_completed)
588        process.nextTick(cb);
589    else
590        this.prependListener('finally', cb);
591};
592E.prototype.goto_fn = function(name){
593    return this.goto.bind(this, name); };
594E.prototype.goto = function(name, promise){
595    this.set_state(name);
596    assert(!this.states[this.next_state].sig, 'goto to sig');
597    return this.continue(promise);
598};
599
600E.prototype.loop = function(promise){
601    this.next_state = this.cur_state;
602    return promise;
603};
604
605E.prototype._set_wait_timer = function(timeout){
606    var _this = this;
607    this.wait_timer = setTimeout(function(){
608        _this.wait_timer = undefined;
609        if (_this._next({ret: undefined, err: 'timeout'}))
610            _this._run();
611    }, timeout);
612};
613E.prototype._del_wait_timer = function(){
614    if (this.wait_timer)
615        this.wait_timer = clearTimeout(this.wait_timer);
616    this.wait_retval = undefined;
617};
618
619E.prototype._get_child_running = function(){
620    for (let v of this.child.values())
621    {
622        if (!v.tm_completed)
623            return v;
624    }
625};
626E.prototype._set_wait_child = function(wait_retval){
627    var _this = this, child = wait_retval.child;
628    var cond = wait_retval.cond, wait_on;
629    var rethrow = wait_retval.rethrow;
630    assert(!cond || child=='any', 'condition supported only for "any" '+
631        'option, you can add support if needed');
632    if (child=='any')
633    {
634        if (!this._get_child_running())
635            return true;
636        wait_on = function(){
637            _this.once('child', function(_child){
638                if (rethrow && E.is_err(_child))
639                    return this._got_retval(wait_retval, _child);
640                if (!cond || cond.call(_child, _child.retval))
641                    return _this._got_retval(wait_retval, {child: _child});
642                if (!_this._get_child_running())
643                    return _this._got_retval(wait_retval);
644                wait_on();
645            });
646        };
647        wait_on();
648    }
649    else if (child=='all')
650    {
651        if (!this._get_child_running())
652            return true;
653        wait_on = function(){
654            _this.once('child', function(_child){
655                if (rethrow && E.is_err(_child))
656                    return _this._got_retval(wait_retval, _child);
657                if (!_this._get_child_running())
658                    return _this._got_retval(wait_retval);
659                wait_on();
660            });
661        };
662        wait_on();
663    }
664    else
665    {
666        assert(child, 'no child provided');
667        assert(this===child.parent, 'child does not belong to parent');
668        if (child.tm_completed)
669            return true;
670        child.once('finally', function(){
671            return _this._got_retval(wait_retval, {child: child}); });
672    }
673    this.emit_safe('wait_on_child');
674    return false;
675};
676
677E.prototype._got_retval = function(wait_retval, res){
678    if (this.wait_retval!==wait_retval || wait_retval.completed)
679        return;
680    wait_retval.completed = true;
681    if (this._next(E._res2rv(res)))
682        this._run();
683};
684E.prototype.continue_fn = function(){
685    return this.continue.bind(this); };
686E.continue_depth = 0;
687E.prototype.continue = function(promise, sync){
688    this.wait_retval = undefined;
689    this._set_retval(promise);
690    if (this.tm_completed)
691        return promise;
692    if (this.down)
693        this.down._ecancel();
694    this._del_wait_timer();
695    var rv = {ret: promise, err: undefined};
696    if (this.running)
697    {
698        this.at_continue = rv;
699        return promise;
700    }
701    if (this._handle_rv(rv))
702        return rv.ret;
703    var _this = this;
704    if (E.is_final(promise) &&
705        (!E.continue_depth && !E.in_run.length || sync))
706    {
707        E.continue_depth++;
708        if (this._next(rv))
709            this._run();
710        E.continue_depth--;
711    }
712    else // avoid high stack depth
713    {
714        E.nextTick(function(){
715            if (_this._next(rv))
716                _this._run();
717        });
718    }
719    return promise;
720};
721
722E.prototype._ecancel = function(){
723    if (this.tm_completed)
724        return this;
725    this.emit_safe('cancel');
726    if (this._cancel!=-1)
727        return this._call_safe(this.funcs[this._cancel]);
728    if (this.cancelable)
729        return this.return();
730};
731
732E.prototype._ecancel_child = function(){
733    if (!this.child.size)
734        return;
735    // copy array, since ecancel has side affects and can modify array
736    var children = Array.from(this.child.values());
737    for (var child of children)
738        child._ecancel();
739};
740
741E.prototype.return_fn = function(){
742    return this.return.bind(this); };
743E.prototype.return = function(promise){
744    if (this.tm_completed)
745        return this._set_retval(promise);
746    this.at_return = true;
747    this.next_state = -1;
748    return this.continue(promise, true);
749};
750
751E.prototype.del_alarm = function(){
752    var a = this._alarm;
753    if (!a)
754        return;
755    clearTimeout(a.id);
756    if (a.cb)
757        this.removeListener('sig_alarm', a.cb);
758    this._alarm = undefined;
759};
760
761E.prototype.upd_alarm = function(ms){
762    var a = this._alarm;
763    if (!a)
764        return;
765    var cb = a.cb;
766    this.alarm(ms, cb);
767};
768
769E.prototype.inc_alarm = function(ms){
770    var a = this._alarm;
771    if (!a)
772        return;
773    var cb = a.cb;
774    var left = this.alarm_left();
775    this.alarm(ms+left, cb);
776};
777
778E.prototype.alarm_left = function(){
779    var a = this._alarm;
780    if (!a)
781        return 0;
782    return a.start+a.ms-Date.now();
783};
784
785E.prototype.alarm_elapsed = function(){
786    var a = this._alarm;
787    if (!a)
788        return 0;
789    return Date.now()-a.start;
790};
791
792E.prototype._operation_opt = function(opt){
793    if (opt.goto)
794        return {ret: this.goto(opt.goto, opt.ret)};
795    if (opt.throw)
796        return {ret: this.throw(opt.throw)};
797    if (opt.return!==undefined)
798        return {ret: this.return(opt.return)};
799    if (opt.continue!==undefined)
800        return {ret: this.continue(opt.continue)};
801};
802
803E.prototype.alarm = function(ms, cb){
804    var _this = this, opt, a;
805    if (cb && typeof cb!='function')
806    {
807        opt = cb;
808        cb = function(){
809            var v;
810            if (!(v = _this._operation_opt(opt)))
811                assert(0, 'invalid alarm cb opt');
812            return v.ret;
813        };
814    }
815    this.del_alarm();
816    a = this._alarm = {ms: ms, cb: cb, start: Date.now()};
817    a.id = setTimeout(function(){
818        _this._alarm = undefined;
819        _this.emit('sig_alarm');
820    }, a.ms);
821    if (cb)
822        this.once('sig_alarm', cb);
823};
824
825function Etask_wait(et, op, timeout, child, cond, rethrow){
826    this.timeout = timeout;
827    this.et = et;
828    this.op = op;
829    this.child = child;
830    this.cond = cond;
831    // XXX: rethrow==true should probably be the default behavir
832    this.rethrow = rethrow;
833    this.ready = undefined;
834    this.completed = false;
835}
836Etask_wait.prototype.continue = function(res){
837    if (this.completed)
838        return;
839    if (!this.et.wait_retval)
840        return void(this.ready = {ret: res});
841    if (this!==this.et.wait_retval)
842        return;
843    this.et.continue(res);
844};
845Etask_wait.prototype.continue_fn = function(){
846    return this.continue.bind(this); };
847Etask_wait.prototype.throw = function(err){
848    return this.continue(E.err(err)); };
849Etask_wait.prototype.throw_fn = function(){
850    return this.throw.bind(this); };
851E.prototype.wait = function(timeout){
852    return new Etask_wait(this, 'wait', timeout); };
853E.prototype.wait_child = function(child, timeout, cond, opt){
854    if (typeof timeout=='function')
855    {
856        cond = timeout;
857        opt = cond;
858        timeout = 0;
859    }
860    else if (typeof timeout=='object')
861    {
862        cond = undefined;
863        opt = timeout;
864        timeout = 0;
865    }
866    return new Etask_wait(this, 'wait_child', timeout, child, cond,
867        opt&&opt.rethrow);
868};
869
870E.prototype.throw_fn = function(err){
871    return err ? this.throw.bind(this, err) : this.throw.bind(this); };
872E.prototype.throw = function(err){
873    return this.continue(E.err(err)); };
874
875E.prototype.get_name = function(flags){
876    /* anon: Context.<anonymous> (/home/yoni/zon1/pkg/util/test.js:1740:7)
877     * with name: Etask.etask1_1 (/home/yoni/zon1/pkg/util/test.js:1741:11) */
878    var stack = this._stack instanceof Error ? this._stack.stack.split('\n') :
879        undefined;
880    var caller;
881    flags = flags||{};
882    if (stack)
883    {
884        caller = /^ {4}at (.*)$/.exec(stack[4]);
885        caller = caller ? caller[1] : undefined;
886    }
887    var names = [];
888    if (this.name)
889        names.push(this.name);
890    if (caller && !(this.name && flags.SHORT_NAME))
891        names.push(caller);
892    if (!names.length)
893        names.push('noname');
894    return names.join(' ');
895};
896
897E.prototype.state_str = function(){
898    return this.cur_state+(this.next_state>=0 ? '->'+this.next_state : ''); };
899
900E.prototype.get_depth = function(){
901    var i=0, et = this;
902    for (; et; et = et.up, i++);
903    return i;
904};
905
906function trim_space(s){
907    if (s[s.length-1]!=' ')
908        return s;
909    return s.slice(0, -1);
910}
911function ms_to_str(ms){ // from date.js
912    var s = ''+ms;
913    return s.length<=3 ? s+'ms' : s.slice(0, -3)+'.'+s.slice(-3)+'s';
914}
915E.prototype.get_time_passed = function(){
916    return ms_to_str(Date.now()-this.tm_create); };
917E.prototype.get_time_completed = function(){
918    return ms_to_str(Date.now()-this.tm_completed); };
919E.prototype.get_info = function(){
920    var info = this.info, s = '', _i;
921    if (!info)
922        return '';
923    for (var i in info)
924    {
925        _i = info[i];
926        if (!_i)
927            continue;
928        if (s!=='')
929            s += ' ';
930        if (typeof _i=='function')
931            s += _i();
932        else
933            s += _i;
934    }
935    return trim_space(s);
936};
937
938// light-weight efficient etask/promise error value
939function Etask_err(err){ this.error = err || new Error(); }
940E.Etask_err = Etask_err;
941E.err = function(err){ return new Etask_err(err); };
942E.is_err = function(v){
943    return v instanceof Etask && v.error!==undefined ||
944        v instanceof Etask_err;
945};
946E.err_res = function(err, res){ return err ? E.err(err) : res; };
947E._res2rv = function(res){
948    return E.is_err(res) ? {ret: undefined, err: res.error}
949        : {ret: res, err: undefined};
950};
951E.is_final = function(v){
952    return !v || typeof v.then!='function' || v instanceof Etask_err ||
953        v instanceof Etask && !!v.tm_completed;
954};
955
956// promise compliant .then() implementation for Etask and Etask_err.
957// for unit-test comfort, also .otherwise(), .catch(), .ensure(), resolve() and
958// reject() are implemented.
959E.prototype.then = function(on_res, on_err){
960    var _this = this;
961    function on_done(){
962        if (!_this.error)
963            return !on_res ? _this.retval : on_res(_this.retval);
964        return !on_err ? E.err(_this.error) : on_err(_this.error);
965    }
966    if (this.tm_completed)
967    {
968        return new Etask({name: 'then_completed'},
969            [function(){ return on_done(); }]);
970    }
971    var then_wait = new Etask({name: 'then_wait'},
972        [function(){ return this.wait(); }]);
973    this.then_waiting.add(function(){
974        try { then_wait.continue(on_done()); }
975        catch(e){ then_wait.throw(e); }
976    });
977    return then_wait;
978};
979E.prototype.otherwise = E.prototype.catch = function(on_err){
980    return this.then(null, on_err); };
981E.prototype.ensure = function(on_ensure){
982    return this.then(function(res){ on_ensure(); return res; },
983        function(err){ on_ensure(); throw err; });
984};
985Etask_err.prototype.then = function(on_res, on_err){
986    var _this = this;
987    return new Etask({name: 'then_err'}, [function(){
988        return !on_err ? E.err(_this.error) : on_err(_this.error);
989    }]);
990};
991Etask_err.prototype.otherwise = Etask_err.prototype.catch = function(on_err){
992    return this.then(null, on_err); };
993Etask_err.prototype.ensure = function(on_ensure){
994    this.then(null, function(){ on_ensure(); });
995    return this;
996};
997E.resolve = function(v){ return new Etask({}, [function(){ return v; }]); };
998E.reject = function(e){ return new Etask({}, [function(){ throw e; }]); };
999
1000E.prototype.wait_ext = function(promise){
1001    if (!promise || typeof promise.then!='function')
1002        return promise;
1003    var wait = this.wait();
1004    promise.then(wait.continue_fn(), wait.throw_fn());
1005    return wait;
1006};
1007
1008E.prototype.shortname = function(){
1009    return this.name===undefined ? 'noname' : this.name;
1010};
1011E.prototype.longname = function(flags){
1012    flags = flags||{TIME: 1};
1013    var s = '', _s;
1014    if (this.running)
1015        s += 'RUNNING ';
1016    s += this.get_name(flags)+(!this.tm_completed ? '.'+this.state_str() : '')
1017        +' ';
1018    if (this.tm_completed)
1019        s += 'COMPLETED'+(flags.TIME ? ' '+this.get_time_completed() : '')+' ';
1020    if (flags.TIME)
1021        s += this.get_time_passed()+' ';
1022    if (_s = this.get_info())
1023        s += _s+' ';
1024    return trim_space(s);
1025};
1026E.prototype.stack = function(flags){
1027    var et = this, s = '';
1028    flags = assign({STACK: 1, RECURSIVE: 1, GUESS: 1}, flags);
1029    while (et)
1030    {
1031        var _s = et.longname(flags)+'\n';
1032        if (et.up)
1033            et = et.up;
1034        else if (et.parent)
1035        {
1036            _s = (et.parent_type=='call' ? 'CALL' : 'SPAWN')+' '+_s;
1037            et = et.parent;
1038        }
1039        else if (et.parent_guess && flags.GUESS)
1040        {
1041            _s = 'SPAWN? '+_s;
1042            et = et.parent_guess;
1043        }
1044        else
1045            et = undefined;
1046        if (flags.TOPDOWN)
1047            s = _s+s;
1048        else
1049            s += _s;
1050    }
1051    return s;
1052};
1053E.prototype._ps = function(pre_first, pre_next, flags){
1054    var i, s = '', task_trail, et = this, child_guess;
1055    if (++flags.limit_n>=flags.LIMIT)
1056        return flags.limit_n==flags.LIMIT ? '\nLIMIT '+flags.LIMIT+'\n': '';
1057    /* get top-most et */
1058    for (; et.up; et = et.up);
1059    /* print the sp frames */
1060    for (var first = 1; et; et = et.down, first = 0)
1061    {
1062        s += first ? pre_first : pre_next;
1063        first = 0;
1064        if (flags.MARK && (i = flags.MARK.sp.indexOf(et))>=0)
1065            s += (flags.MARK.name[i]||'***')+' ';
1066        s += et.longname(flags)+'\n';
1067        if (flags.RECURSIVE)
1068        {
1069            var stack_trail = et.down ? '.' : ' ';
1070            var child = et.child;
1071            if (flags.GUESS)
1072                child = new Set([...child, ...et.child_guess]);
1073            i = 0;
1074            for (let child_i of child.values())
1075            {
1076                task_trail = i<child.size-1 ? '|' : stack_trail;
1077                child_guess = child_i.parent_guess ? '\\? ' :
1078                    child_i.parent_type=='call' ? '\\> ' : '\\_ ';
1079                s += child_i._ps(pre_next+task_trail+child_guess,
1080                    pre_next+task_trail+'   ', flags);
1081                i++;
1082            }
1083        }
1084    }
1085    return s;
1086};
1087function ps_flags(flags){
1088    var m, _m;
1089    if (m = flags.MARK)
1090    {
1091        if (!Array.isArray(m))
1092            _m = {sp: [m], name: []};
1093        else if (!Array.isArray(flags.MARK[0]))
1094            _m = {sp: m, name: []};
1095        else
1096        {
1097            _m = {sp: [], name: []};
1098            for (var i=0; i<m.length; i++)
1099            {
1100                _m.name.push(m[i][0]);
1101                _m.sp.push(m[i][1]);
1102            }
1103        }
1104        flags.MARK = _m;
1105    }
1106}
1107E.prototype.ps = function(flags){
1108    flags = assign({STACK: 1, RECURSIVE: 1, LIMIT: 10000000, TIME: 1,
1109        GUESS: 1}, flags, {limit_n: 0});
1110    ps_flags(flags);
1111    return this._ps('', '', flags);
1112};
1113E._longname_root = function(){
1114    return (zerr.prefix ? zerr.prefix+'pid '+process.pid+' ' : '')+'root'; };
1115E.ps = function(flags){
1116    var s = '', task_trail;
1117    flags = assign({STACK: 1, RECURSIVE: 1, LIMIT: 10000000, TIME: 1,
1118        GUESS: 1}, flags, {limit_n: 0});
1119    ps_flags(flags);
1120    s += E._longname_root()+'\n';
1121    var child = Array.from(E.root.values());
1122    if (flags.GUESS)
1123    {
1124        child = [];
1125        E.root.forEach(root_i=>{
1126            if (!root_i.parent_guess)
1127                child.push(root_i);
1128        });
1129    }
1130    child.forEach((child_i, i)=>{
1131        task_trail = i<child.length-1 ? '|' : ' ';
1132        s += child_i._ps(task_trail+'\\_ ', task_trail+'   ', flags);
1133    });
1134    return s;
1135};
1136
1137function assert_tree_unique(a){
1138    var i;
1139    for (i=0; i<a.length-1; i++)
1140        assert(!a.includes(a[i], i+1));
1141}
1142E.prototype._assert_tree = function(opt){
1143    var et;
1144    opt = opt||{};
1145    assert_tree_unique(this.child);
1146    assert(this.parent);
1147    if (this.down)
1148    {
1149        et = this.down;
1150        assert(et.up===this);
1151        assert(!et.parent);
1152        assert(!et.parent_guess);
1153        this.down._assert_tree(opt);
1154    }
1155    for (let _et of this.child.values())
1156    {
1157        assert(_et.parent===this);
1158        assert(!_et.parent_guess);
1159        assert(!_et.up);
1160        _et._assert_tree(opt);
1161    }
1162    if (this.child_guess.size)
1163        assert(E.in_run.includes(this));
1164    for (let _et of this.child_guess.values())
1165    {
1166        assert(_et.parent_guess===this);
1167        assert(!_et.parent);
1168        assert(!_et.up);
1169    }
1170};
1171E._assert_tree = function(opt){
1172    opt = opt||{};
1173    assert_tree_unique(E.root);
1174    for (let et of this.child.values())
1175    {
1176        assert(!et.parent);
1177        assert(!et.up);
1178        et._assert_tree(opt);
1179    }
1180};
1181E.prototype._assert_parent = function(){
1182    if (this.up)
1183        return assert(!this.parent && !this.parent_guess);
1184    assert(this.parent && this.parent_guess,
1185        'parent_guess together with parent');
1186    if (this.parent)
1187    {
1188        var child = this.parent ? this.parent.child : E.root;
1189        assert(child.has(this),
1190            'cannot find in parent '+(this.parent ? '' : 'root'));
1191    }
1192    else if (this.parent_guess)
1193    {
1194        assert(this.parent_guess.child_guess.has(this),
1195            'cannot find in parent_guess');
1196        assert(E.in_run.includes(this.parent_guess));
1197    }
1198};
1199
1200E.prototype.return_child = function(){
1201    // copy array, since return() has side affects and can modify array
1202    var child = Array.from(this.child.values());
1203    for (var i=0; i<child.length; i++)
1204        child[i].return();
1205};
1206
1207E.sleep = function(ms){
1208    var timer;
1209    ms = ms||0;
1210    return new Etask({name: 'sleep', cancel: true}, [function(){
1211        this.info.ms = ms+'ms';
1212        timer = setTimeout(this.continue_fn(), ms);
1213        return this.wait();
1214    }, function finally$(){
1215        clearTimeout(timer);
1216    }]);
1217};
1218
1219var ebreak_obj = {ebreak: 1};
1220E.prototype.break = function(ret){
1221    return this.throw({ebreak: ebreak_obj, ret: ret}); };
1222E.for = function(cond, inc, opt, states){
1223    if (Array.isArray(opt) || typeof opt=='function')
1224    {
1225        states = opt;
1226        opt = undefined;
1227    }
1228    states = typeof states=='function' ? [states] : states;
1229    return new Etask({name: 'for', cancel: true, init: opt&&opt.init_parent},
1230    [function loop(){
1231        return !cond || cond.call(this);
1232    }, function try_catch$(res){
1233        if (!res)
1234            return this.return();
1235        return new Etask({name: 'for_iter', cancel: true, init: opt&&opt.init},
1236            states||[]);
1237    }, function(){
1238        if (this.error)
1239        {
1240            if (this.error.ebreak===ebreak_obj)
1241                return this.return(this.error.ret);
1242            return this.throw(this.error);
1243        }
1244        return inc && inc.call(this);
1245    }, function(){
1246        return this.goto('loop');
1247    }]);
1248};
1249E.for_each = function(obj, states){
1250    var keys = Object.keys(obj);
1251    var iter = {obj: obj, keys: keys, i: 0, key: undefined, val: undefined};
1252    function init_iter(){ this.iter = iter; }
1253    return E.for(function(){
1254            this.iter = this.iter||iter;
1255            iter.key = keys[iter.i];
1256            iter.val = obj[keys[iter.i]];
1257            return iter.i<keys.length;
1258        },
1259        function(){ return iter.i++; },
1260        {init: init_iter, init_parent: init_iter},
1261        states);
1262};
1263E.while = function(cond, states){ return E.for(cond, null, states); };
1264
1265// all([opt, ]a_or_o)
1266E.all = function(a_or_o, ao2){
1267    var i, j, opt = {};
1268    if (ao2)
1269    {
1270        opt = a_or_o;
1271        a_or_o = ao2;
1272    }
1273    if (Array.isArray(a_or_o))
1274    {
1275        var a = Array.from(a_or_o);
1276        i = 0;
1277        return new Etask({name: 'all_a', cancel: true}, [function(){
1278            for (j=0; j<a.length; j++)
1279                this.spawn(a[j]);
1280        }, function try_catch$loop(){
1281            if (i>=a.length)
1282                return this.return(a);
1283            this.info.at = 'at '+i+'/'+a.length;
1284            var _a = a[i];
1285            if (_a instanceof Etask)
1286                _a.spawn_parent();
1287            return _a;
1288        }, function(res){
1289            if (this.error)
1290            {
1291                if (!opt.allow_fail)
1292                    return this.throw(this.error);
1293                res = E.err(this.error);
1294            }
1295            a[i] = res;
1296            i++;
1297            return this.goto('loop');
1298        }]);
1299    }
1300    else if (a_or_o instanceof Object)
1301    {
1302        var keys = Object.keys(a_or_o), o = {};
1303        i = 0;
1304        return new Etask({name: 'all_o', cancel: true}, [function(){
1305            for (j=0; j<keys.length; j++)
1306                this.spawn(a_or_o[keys[j]]);
1307        }, function try_catch$loop(){
1308            if (i>=keys.length)
1309                return this.return(o);
1310            var _i = keys[i], _a = a_or_o[_i];
1311            this.info.at = 'at '+_i+' '+i+'/'+keys.length;
1312            if (_a instanceof Etask)
1313                _a.spawn_parent();
1314            return _a;
1315        }, function(res){
1316            if (this.error)
1317            {
1318                if (!opt.allow_fail)
1319                    return this.throw(this.error);
1320                res = E.err(this.error);
1321            }
1322            o[keys[i]] = res;
1323            i++;
1324            return this.goto('loop');
1325        }]);
1326    }
1327    assert(0, 'invalid type');
1328};
1329
1330E.all_limit = function(limit, arr_iter, cb){
1331    var at = 0;
1332    var iter = !Array.isArray(arr_iter) ? arr_iter : function(){
1333        if (at<arr_iter.length)
1334            return cb.call(this, arr_iter[at], at++);
1335    };
1336    return new Etask({name: 'all_limit', cancel: true}, [function(){
1337        var next;
1338        if (!(next = iter.call(this)))
1339            return this.goto('done');
1340        if (E.is_err(next))
1341            return this.throw(next.error);
1342        var _this = this;
1343        if (typeof next.catch=='function')
1344            next.catch(function(e){ _this.throw(e); });
1345        this.spawn(next);
1346        this.loop();
1347        if (this.child.size>=limit)
1348            return this.wait_child('any', {rethrow: true});
1349    }, function done(){
1350        return this.wait_child('all', {rethrow: true});
1351    }]);
1352};
1353
1354// _apply(opt, func[, _this], args)
1355// _apply(opt, object, method, args)
1356E._apply = function(opt, func, _this, args){
1357    var func_name;
1358    if (typeof _this=='string') // class with '.method' string call
1359    {
1360        assert(_this[0]=='.', 'invalid method '+_this);
1361        var method = _this.slice(1), _class = func;
1362        func = _class[method];
1363        _this = _class;
1364        assert(_this instanceof Object, 'invalid method .'+method);
1365        func_name = method;
1366    }
1367    else if (Array.isArray(_this) && !args)
1368    {
1369        args = _this;
1370        _this = null;
1371    }
1372    opt.name = opt.name||func_name||func.name;
1373    return new Etask(opt, [function(){
1374        var et = this, ret_sync, returned = 0;
1375        args = Array.from(args);
1376        args.push(function cb(err, res){
1377            if (typeof opt.ret_sync=='string' && !returned)
1378            {
1379                // hack to wait for result
1380                var a = arguments;
1381                returned++;
1382                return void E.nextTick(function(){ cb.apply(null, a); });
1383            }
1384            var nfn = opt.nfn===undefined || opt.nfn ? 1 : 0;
1385            if (opt.ret_o)
1386            {
1387                var o = {}, i;
1388                if (Array.isArray(opt.ret_o))
1389                {
1390                    for (i=0; i<opt.ret_o.length; i++)
1391                        o[opt.ret_o[i]] = arguments[i+nfn];
1392                }
1393                else if (typeof opt.ret_o=='string')
1394                    o[opt.ret_o] = array.slice(arguments, nfn);
1395                else
1396                    assert(0, 'invalid opt.ret_o');
1397                if (typeof opt.ret_sync=='string')
1398                    o[opt.ret_sync] = ret_sync;
1399                res = o;
1400            }
1401            else if (opt.ret_a)
1402                res = array.slice(arguments, nfn);
1403            else if (!nfn)
1404                res = err;
1405            et.continue(nfn ? E.err_res(err, res) : res);
1406        });
1407        ret_sync = func.apply(_this, args);
1408        if (Array.isArray(opt.ret_sync))
1409            opt.ret_sync[0][opt.ret_sync[1]] = ret_sync;
1410        returned++;
1411        return this.wait();
1412    }]);
1413};
1414
1415// nfn_apply([opt, ]object, method, args)
1416// nfn_apply([opt, ]func, this, args)
1417E.nfn_apply = function(opt, func, _this, args){
1418    var _opt = {nfn: 1, cancel: 1};
1419    if (typeof opt=='function' || typeof func=='string')
1420    {
1421        args = _this;
1422        _this = func;
1423        func = opt;
1424        opt = _opt;
1425    }
1426    else
1427        opt = assign(_opt, opt);
1428    return E._apply(opt, func, _this, args);
1429};
1430// cb_apply([opt, ]object, method, args)
1431// cb_apply([opt, ]func, this, args)
1432E.cb_apply = function(opt, func, _this, args){
1433    var _opt = {nfn: 0};
1434    if (typeof opt=='function' || typeof func=='string')
1435    {
1436        args = _this;
1437        _this = func;
1438        func = opt;
1439        opt = _opt;
1440    }
1441    else
1442        opt = assign(_opt, opt);
1443    return E._apply(opt, func, _this, args);
1444};
1445
1446E.prototype.continue_nfn = function(){
1447    return function(err, res){ this.continue(E.err_res(err, res)); }
1448    .bind(this);
1449};
1450
1451E.augment = function(_prototype, method, e_method){
1452    var i, opt = {};
1453    if (method instanceof Object && !Array.isArray(method))
1454    {
1455        assign(opt, method);
1456        method = arguments[2];
1457        e_method = arguments[3];
1458    }
1459    if (Array.isArray(method))
1460    {
1461        if (e_method)
1462            opt.prefix = e_method;
1463        for (i=0; i<method.length; i++)
1464            E.augment(_prototype, opt, method[i]);
1465        return;
1466    }
1467    opt.prefix = opt.prefix||'e_';
1468    if (!e_method)
1469        e_method = opt.prefix+method;
1470    var fn = _prototype[method];
1471    _prototype[e_method] = function(){
1472        return E._apply({name: e_method, nfn: 1}, fn, this, arguments); };
1473};
1474
1475E.wait = function(timeout){
1476    return new Etask({name: 'wait', cancel: true},
1477        [function(){ return this.wait(timeout); }]);
1478};
1479E.to_nfn = function(promise, cb, opt){
1480    return new Etask({name: 'to_nfn', async: true}, [function try_catch$(){
1481        return promise;
1482    }, function(res){
1483        var ret = [this.error];
1484        if (opt && opt.ret_a)
1485            ret = ret.concat(res);
1486        else
1487            ret.push(res);
1488        cb.apply(null, ret);
1489    }]);
1490};
1491function etask_fn(opt, states, push_this){
1492    if (Array.isArray(opt) || typeof opt=='function')
1493    {
1494        states = opt;
1495        opt = undefined;
1496    }
1497    var is_gen = typeof states=='function' && states.constructor.name==GEN_FN;
1498    var arg_start = +push_this;
1499    var wrap = function(){
1500        var _opt = assign({}, opt);
1501        _opt.state0_args = new Array(arg_start+arguments.length);
1502        if (push_this)
1503            _opt.state0_args[0] = this;
1504        for (var i=0; i<arguments.length; i++)
1505            _opt.state0_args[arg_start+i] = arguments[i];
1506        if (is_gen)
1507            return E._generator(null, states, _opt);
1508        return new Etask(_opt, typeof states=='function' ? [states] : states);
1509    };
1510    if (states.name)
1511    {
1512        Object.defineProperty(wrap, 'name',
1513            {value: states.name, writable: false});
1514    }
1515    return wrap;
1516}
1517E.fn = function(opt, states){ return etask_fn(opt, states, false); };
1518E._fn = function(opt, states){ return etask_fn(opt, states, true); };
1519E._generator = function(gen, ctor, opt){
1520    opt = opt||{};
1521    opt.name = opt.name || ctor && ctor.name || 'generator';
1522    opt.cancel = opt.cancel===undefined ? true : opt.cancel;
1523    var done = false;
1524    return new Etask(opt, [function(){
1525        this.generator = gen = gen||ctor.apply(this, arguments);
1526        this.generator_ctor = ctor;
1527        return {ret: undefined, err: undefined};
1528    }, function try_catch$loop(rv){
1529        var res;
1530        try { res = rv.err ? gen.throw(rv.err) : gen.next(rv.ret); }
1531        catch(e){ return this.return(E.err(e)); }
1532        if (res.done)
1533        {
1534            done = true;
1535            return this.return(res.value);
1536        }
1537        return res.value;
1538    }, function(ret){
1539        return this.goto('loop', this.error ?
1540            {ret: undefined, err: this.error} : {ret: ret, err: undefined});
1541    }, function finally$(){
1542        // https://kangax.github.io/compat-table/es6/#test-generators_%GeneratorPrototype%.return
1543        // .return() supported only in node>=6.x.x
1544        if (!done && gen && gen.return)
1545            try { gen.return(); } catch(e){}
1546    }]);
1547};
1548E.ef = function(err, et){ // error filter
1549    if (zerr.on_exception)
1550        zerr.on_exception(err, et);
1551    return err;
1552};
1553// similar to setInterval
1554// opt==10000 (or opt.ms==10000) - call states every 10 seconds
1555// opt.mode=='smart' - default mode, like setInterval. If states take
1556//   longer than 'ms' to execute, next execution is delayed.
1557// opt.mode=='fixed' - always sleep 10 seconds between states
1558// opt.mode=='spawn' - spawn every 10 seconds
1559E.interval = function(opt, states){
1560    if (typeof opt=='number')
1561        opt = {ms: opt};
1562    if (!opt.mode || opt.mode=='smart')
1563        return interval_smart(opt, states);
1564    if (opt.mode=='fixed')
1565        return interval_fixed(opt, states);
1566    if (opt.mode=='spawn')
1567        return interval_spawn(opt, states);
1568    throw new Error('unexpected mode '+opt.mode);
1569};
1570function interval_smart(opt, states){
1571    var STATE_BEGIN = 0;
1572    var STATE_OPS = 1;
1573    var STATE_TIMER = 2;
1574    var STATE_DONE = 4;
1575    var state, w, timer, gap_timer;
1576    var init_parent = function(){
1577        w = this.wait();
1578        this.finally(()=>clearTimeout(timer));
1579    };
1580    const set_state = (for_et, flag)=>{
1581        if ((state = state|flag) != (STATE_OPS|STATE_TIMER))
1582            return;
1583        state = state|STATE_DONE;
1584        if (flag==STATE_TIMER)
1585            return void for_et.continue();
1586        if (gap_timer && zutil.is_timer_refresh)
1587            gap_timer.refresh();
1588        else
1589        {
1590            clearTimeout(gap_timer);
1591            gap_timer = setTimeout(for_et.continue_fn(), 0);
1592        }
1593    };
1594    return E.for(function(){
1595        state = STATE_BEGIN;
1596        if (timer && zutil.is_timer_refresh)
1597            timer.refresh();
1598        else
1599        {
1600            clearTimeout(timer);
1601            timer = setTimeout(()=>set_state(this, STATE_TIMER), opt.ms);
1602        }
1603        return true;
1604    }, function(){
1605        set_state(this, STATE_OPS);
1606        return w;
1607    }, {init_parent}, states);
1608}
1609function interval_fixed(opt, states){
1610    var w, timer;
1611    var init_parent = function(){
1612        w = this.wait();
1613        this.finally(()=>clearTimeout(timer));
1614    };
1615    return E.for(null, function(){
1616        if (timer && zutil.is_timer_refresh)
1617            timer.refresh();
1618        else
1619        {
1620            clearTimeout(timer);
1621            timer = setTimeout(this.continue_fn(), opt.ms);
1622        }
1623        return w;
1624    }, {init_parent}, states);
1625}
1626function interval_spawn(opt, states){
1627    var w, timer, stopped = false;
1628    var init = function(){
1629        w = this.wait();
1630        this.finally(()=>clearTimeout(timer));
1631    };
1632    states = typeof states=='function' ? [states] : states;
1633    return new Etask({name: 'interval_spawn', cancel: true, init},
1634        [function loop(){
1635            new Etask({}, [function try_catch$(){
1636                return new Etask({}, states);
1637            }, function(res){
1638                if (!this.error)
1639                    return;
1640                if (this.error.ebreak!==ebreak_obj)
1641                    return this.throw(this.error);
1642                stopped = true;
1643            }]);
1644        }, function(){
1645            if (stopped)
1646                return this.return();
1647            if (timer && zutil.is_timer_refresh)
1648                timer.refresh();
1649            else
1650            {
1651                clearTimeout(timer);
1652                timer = setTimeout(this.continue_fn(), opt.ms);
1653            }
1654            return w;
1655        }, function(){
1656            if (stopped) // stopped during sleep by prev long iteration
1657                return this.return();
1658            return this.goto('loop');
1659        }]);
1660}
1661
1662// For now we're deliberately not supporting passing opt here to all
1663// class fns.
1664// The existing pattern to handle this kind of thing is like this:
1665//
1666// method(){ return etask(special_options, function*(){
1667//    // code
1668// }); }
1669//
1670// If you think you have a good reason to add opt passing, talk with @vadim
1671// first
1672E._class = function(cls){
1673    var proto = cls.prototype, keys = Object.getOwnPropertyNames(proto);
1674    for (var i=0; i<keys.length; i++)
1675    {
1676        var key = keys[i];
1677        var descr = Object.getOwnPropertyDescriptor(proto, key);
1678        if (descr.get||descr.set)
1679            continue;
1680        var p = proto[key];
1681        if (typeof p=='function' && p.constructor.name==GEN_FN)
1682            proto[key] = E._fn(p);
1683    }
1684    return cls;
1685};
1686E.shutdown = function(){
1687    var prev;
1688    while (E.root.size)
1689    {
1690        var e = E.root.values().next().value;
1691        if (e==prev)
1692        {
1693            assert(e.tm_completed);
1694            zerr.zexit('etask root not removed after return - '+
1695                'fix non-cancelable child etask');
1696        }
1697        prev = e;
1698        e.return();
1699    }
1700};
1701
1702E.race = function(ets){
1703    return new Etask({}, [function race(){
1704        if (!Array.isArray(ets))
1705            zerr.zexit('provided argument is not an array');
1706        if (!ets.length)
1707            return;
1708        var race_et = E.wait();
1709        var _this = this;
1710        function _child_error_handler(e){ race_et.throw(e); }
1711        var _race = new Etask({}, [
1712            function(){
1713                return E.for_each(ets, function(){
1714                    var et = this.iter.val;
1715                    _this.spawn(new Etask({}, [
1716                        function(){
1717                            this.on('uncaught', _child_error_handler);
1718                            return et;
1719                        },
1720                        function(res){ race_et.continue(res); },
1721                    ]));
1722                });
1723            },
1724            function(){ return race_et; },
1725        ]);
1726        _race.finally(function(){ _this.return_child(); });
1727        return _race;
1728    }]);
1729};
1730
1731E.any = function(opt, ets){
1732    if (!ets)
1733    {
1734        ets = opt;
1735        opt = {};
1736    }
1737    opt=opt||{};
1738    opt.rethrow = opt.rethrow==undefined ? 1 : opt.rethrow;
1739    return new Etask({}, [function race(){
1740        if (!Array.isArray(ets))
1741            zerr.zexit('provided argument is not an array');
1742        var length = ets.length;
1743        if (!length)
1744            return;
1745        var race_et = E.wait();
1746        var errors = [];
1747        var _this = this;
1748        function _child_error_handler(e){
1749            errors.push(e);
1750            if (errors.length!=length)
1751                return;
1752            var err = new Error('aggregation error');
1753            err.errors = errors;
1754            if (opt.rethrow)
1755                race_et.throw(err);
1756            else
1757                race_et.continue();
1758        }
1759        var _race = new Etask({}, [
1760            function(){
1761                return E.for_each(ets, function(){
1762                    var et = this.iter.val;
1763                    var child = _this.spawn(new Etask({}, [
1764                        function(){
1765                            this.on('uncaught', _child_error_handler);
1766                            return et;
1767                        },
1768                        function(res){ race_et.continue(res); },
1769                    ]));
1770                    if (opt.timeout)
1771                        setTimeout(()=>child.throw('timeout'), opt.timeout);
1772                });
1773            },
1774            function(){ return race_et; },
1775        ]);
1776        _race.finally(function(){ _this.return_child(); });
1777        return _race;
1778    }]);
1779};
1780
1781return Etask; }); }());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.