| 'use strict'; |
| |
| // WARNING: THIS MODULE IS PENDING DEPRECATION. |
| // |
| // No new pull requests targeting this module will be accepted |
| // unless they address existing, critical bugs. |
| |
| const util = require('util'); |
| const EventEmitter = require('events'); |
| const inherits = util.inherits; |
| |
| // communicate with events module, but don't require that |
| // module to have to load this one, since this module has |
| // a few side effects. |
| EventEmitter.usingDomains = true; |
| |
| // overwrite process.domain with a getter/setter that will allow for more |
| // effective optimizations |
| var _domain = [null]; |
| Object.defineProperty(process, 'domain', { |
| enumerable: true, |
| get: function() { |
| return _domain[0]; |
| }, |
| set: function(arg) { |
| return _domain[0] = arg; |
| } |
| }); |
| |
| // objects with external array data are excellent ways to communicate state |
| // between js and c++ w/o much overhead |
| var _domain_flag = {}; |
| |
| // let the process know we're using domains |
| process._setupDomainUse(_domain, _domain_flag); |
| |
| exports.Domain = Domain; |
| |
| exports.create = exports.createDomain = function() { |
| return new Domain(); |
| }; |
| |
| // it's possible to enter one domain while already inside |
| // another one. the stack is each entered domain. |
| var stack = []; |
| exports._stack = stack; |
| // the active domain is always the one that we're currently in. |
| exports.active = null; |
| |
| |
| inherits(Domain, EventEmitter); |
| |
| function Domain() { |
| EventEmitter.call(this); |
| |
| this.members = []; |
| } |
| |
| Domain.prototype.members = undefined; |
| Domain.prototype._disposed = undefined; |
| |
| |
| // Called by process._fatalException in case an error was thrown. |
| Domain.prototype._errorHandler = function errorHandler(er) { |
| var caught = false; |
| // ignore errors on disposed domains. |
| // |
| // XXX This is a bit stupid. We should probably get rid of |
| // domain.dispose() altogether. It's almost always a terrible |
| // idea. --isaacs |
| if (this._disposed) |
| return true; |
| |
| if (!util.isPrimitive(er)) { |
| er.domain = this; |
| er.domainThrown = true; |
| } |
| // wrap this in a try/catch so we don't get infinite throwing |
| try { |
| // One of three things will happen here. |
| // |
| // 1. There is a handler, caught = true |
| // 2. There is no handler, caught = false |
| // 3. It throws, caught = false |
| // |
| // If caught is false after this, then there's no need to exit() |
| // the domain, because we're going to crash the process anyway. |
| caught = this.emit('error', er); |
| |
| // Exit all domains on the stack. Uncaught exceptions end the |
| // current tick and no domains should be left on the stack |
| // between ticks. |
| stack.length = 0; |
| exports.active = process.domain = null; |
| } catch (er2) { |
| // The domain error handler threw! oh no! |
| // See if another domain can catch THIS error, |
| // or else crash on the original one. |
| // If the user already exited it, then don't double-exit. |
| if (this === exports.active) { |
| stack.pop(); |
| } |
| if (stack.length) { |
| exports.active = process.domain = stack[stack.length - 1]; |
| caught = process._fatalException(er2); |
| } else { |
| caught = false; |
| } |
| return caught; |
| } |
| return caught; |
| }; |
| |
| |
| Domain.prototype.enter = function() { |
| if (this._disposed) return; |
| |
| // note that this might be a no-op, but we still need |
| // to push it onto the stack so that we can pop it later. |
| exports.active = process.domain = this; |
| stack.push(this); |
| _domain_flag[0] = stack.length; |
| }; |
| |
| |
| Domain.prototype.exit = function() { |
| // skip disposed domains, as usual, but also don't do anything if this |
| // domain is not on the stack. |
| var index = stack.lastIndexOf(this); |
| if (this._disposed || index === -1) return; |
| |
| // exit all domains until this one. |
| stack.splice(index); |
| _domain_flag[0] = stack.length; |
| |
| exports.active = stack[stack.length - 1]; |
| process.domain = exports.active; |
| }; |
| |
| |
| // note: this works for timers as well. |
| Domain.prototype.add = function(ee) { |
| // If the domain is disposed or already added, then nothing left to do. |
| if (this._disposed || ee.domain === this) |
| return; |
| |
| // has a domain already - remove it first. |
| if (ee.domain) |
| ee.domain.remove(ee); |
| |
| // check for circular Domain->Domain links. |
| // This causes bad insanity! |
| // |
| // For example: |
| // var d = domain.create(); |
| // var e = domain.create(); |
| // d.add(e); |
| // e.add(d); |
| // e.emit('error', er); // RangeError, stack overflow! |
| if (this.domain && (ee instanceof Domain)) { |
| for (var d = this.domain; d; d = d.domain) { |
| if (ee === d) return; |
| } |
| } |
| |
| ee.domain = this; |
| this.members.push(ee); |
| }; |
| |
| |
| Domain.prototype.remove = function(ee) { |
| ee.domain = null; |
| var index = this.members.indexOf(ee); |
| if (index !== -1) |
| this.members.splice(index, 1); |
| }; |
| |
| |
| Domain.prototype.run = function(fn) { |
| if (this._disposed) |
| return; |
| |
| var ret; |
| |
| this.enter(); |
| if (arguments.length >= 2) { |
| var len = arguments.length; |
| var args = new Array(len - 1); |
| |
| for (var i = 1; i < len; i++) |
| args[i - 1] = arguments[i]; |
| |
| ret = fn.apply(this, args); |
| } else { |
| ret = fn.call(this); |
| } |
| this.exit(); |
| |
| return ret; |
| }; |
| |
| |
| function intercepted(_this, self, cb, fnargs) { |
| if (self._disposed) |
| return; |
| |
| if (fnargs[0] && fnargs[0] instanceof Error) { |
| var er = fnargs[0]; |
| util._extend(er, { |
| domainBound: cb, |
| domainThrown: false, |
| domain: self |
| }); |
| self.emit('error', er); |
| return; |
| } |
| |
| var args = []; |
| var i, ret; |
| |
| self.enter(); |
| if (fnargs.length > 1) { |
| for (i = 1; i < fnargs.length; i++) |
| args.push(fnargs[i]); |
| ret = cb.apply(_this, args); |
| } else { |
| ret = cb.call(_this); |
| } |
| self.exit(); |
| |
| return ret; |
| } |
| |
| |
| Domain.prototype.intercept = function(cb) { |
| var self = this; |
| |
| function runIntercepted() { |
| return intercepted(this, self, cb, arguments); |
| } |
| |
| return runIntercepted; |
| }; |
| |
| |
| function bound(_this, self, cb, fnargs) { |
| if (self._disposed) |
| return; |
| |
| var ret; |
| |
| self.enter(); |
| if (fnargs.length > 0) |
| ret = cb.apply(_this, fnargs); |
| else |
| ret = cb.call(_this); |
| self.exit(); |
| |
| return ret; |
| } |
| |
| |
| Domain.prototype.bind = function(cb) { |
| var self = this; |
| |
| function runBound() { |
| return bound(this, self, cb, arguments); |
| } |
| |
| runBound.domain = this; |
| |
| return runBound; |
| }; |
| |
| |
| Domain.prototype.dispose = util.deprecate(function() { |
| if (this._disposed) return; |
| |
| // if we're the active domain, then get out now. |
| this.exit(); |
| |
| // remove from parent domain, if there is one. |
| if (this.domain) this.domain.remove(this); |
| |
| // kill the references so that they can be properly gc'ed. |
| this.members.length = 0; |
| |
| // mark this domain as 'no longer relevant' |
| // so that it can't be entered or activated. |
| this._disposed = true; |
| }); |