1 // Copyright Joyent, Inc. and other Node contributors.
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
22 var util = require('util');
23 var EventEmitter = require('events');
24 var inherits = util.inherits;
26 // communicate with events module, but don't require that
27 // module to have to load this one, since this module has
28 // a few side effects.
29 EventEmitter.usingDomains = true;
31 // overwrite process.domain with a getter/setter that will allow for more
32 // effective optimizations
34 Object.defineProperty(process, 'domain', {
40 return _domain[0] = arg;
44 // objects with external array data are excellent ways to communicate state
45 // between js and c++ w/o much overhead
46 var _domain_flag = {};
48 // let the process know we're using domains
49 process._setupDomainUse(_domain, _domain_flag);
51 exports.Domain = Domain;
53 exports.create = exports.createDomain = function() {
57 // it's possible to enter one domain while already inside
58 // another one. the stack is each entered domain.
60 exports._stack = stack;
61 // the active domain is always the one that we're currently in.
62 exports.active = null;
65 inherits(Domain, EventEmitter);
68 EventEmitter.call(this);
73 Domain.prototype.members = undefined;
74 Domain.prototype._disposed = undefined;
77 // Called by process._fatalException in case an error was thrown.
78 Domain.prototype._errorHandler = function errorHandler(er) {
80 // ignore errors on disposed domains.
82 // XXX This is a bit stupid. We should probably get rid of
83 // domain.dispose() altogether. It's almost always a terrible
89 er.domainThrown = true;
90 // wrap this in a try/catch so we don't get infinite throwing
92 // One of three things will happen here.
94 // 1. There is a handler, caught = true
95 // 2. There is no handler, caught = false
96 // 3. It throws, caught = false
98 // If caught is false after this, then there's no need to exit()
99 // the domain, because we're going to crash the process anyway.
100 caught = this.emit('error', er);
102 // Exit all domains on the stack. Uncaught exceptions end the
103 // current tick and no domains should be left on the stack
106 exports.active = process.domain = null;
108 // The domain error handler threw! oh no!
109 // See if another domain can catch THIS error,
110 // or else crash on the original one.
111 // If the user already exited it, then don't double-exit.
112 if (this === exports.active) {
116 exports.active = process.domain = stack[stack.length - 1];
117 caught = process._fatalException(er2);
127 Domain.prototype.enter = function() {
128 if (this._disposed) return;
130 // note that this might be a no-op, but we still need
131 // to push it onto the stack so that we can pop it later.
132 exports.active = process.domain = this;
134 _domain_flag[0] = stack.length;
138 Domain.prototype.exit = function() {
139 // skip disposed domains, as usual, but also don't do anything if this
140 // domain is not on the stack.
141 var index = stack.lastIndexOf(this);
142 if (this._disposed || index === -1) return;
144 // exit all domains until this one.
146 _domain_flag[0] = stack.length;
148 exports.active = stack[stack.length - 1];
149 process.domain = exports.active;
153 // note: this works for timers as well.
154 Domain.prototype.add = function(ee) {
155 // If the domain is disposed or already added, then nothing left to do.
156 if (this._disposed || ee.domain === this)
159 // has a domain already - remove it first.
161 ee.domain.remove(ee);
163 // check for circular Domain->Domain links.
164 // This causes bad insanity!
167 // var d = domain.create();
168 // var e = domain.create();
171 // e.emit('error', er); // RangeError, stack overflow!
172 if (this.domain && (ee instanceof Domain)) {
173 for (var d = this.domain; d; d = d.domain) {
174 if (ee === d) return;
179 this.members.push(ee);
183 Domain.prototype.remove = function(ee) {
185 var index = this.members.indexOf(ee);
187 this.members.splice(index, 1);
191 Domain.prototype.run = function(fn) {
195 var ret = fn.call(this);
201 function intercepted(_this, self, cb, fnargs) {
205 if (fnargs[0] && fnargs[0] instanceof Error) {
212 self.emit('error', er);
216 var len = fnargs.length;
221 if (fnargs.length > 1) {
222 for (i = 1; i < fnargs.length; i++)
223 args.push(fnargs[i]);
224 ret = cb.apply(_this, args);
226 ret = cb.call(_this);
234 Domain.prototype.intercept = function(cb) {
237 function runIntercepted() {
238 return intercepted(this, self, cb, arguments);
241 return runIntercepted;
245 function bound(_this, self, cb, fnargs) {
249 var len = fnargs.length;
254 if (fnargs.length > 0)
255 ret = cb.apply(_this, fnargs);
257 ret = cb.call(_this);
264 Domain.prototype.bind = function(cb) {
267 function runBound() {
268 return bound(this, self, cb, arguments);
271 runBound.domain = this;
277 Domain.prototype.dispose = util.deprecate(function() {
278 if (this._disposed) return;
280 // if we're the active domain, then get out now.
283 // remove from parent domain, if there is one.
284 if (this.domain) this.domain.remove(this);
286 // kill the references so that they can be properly gc'ed.
287 this.members.length = 0;
289 // mark this domain as 'no longer relevant'
290 // so that it can't be entered or activated.
291 this._disposed = true;