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.