doc: improvements to console.markdown copy
[platform/upstream/nodejs.git] / lib / domain.js
1 'use strict';
2
3 // WARNING: THIS MODULE IS PENDING DEPRECATION.
4 //
5 // No new pull requests targeting this module will be accepted
6 // unless they address existing, critical bugs.
7
8 const util = require('util');
9 const EventEmitter = require('events');
10 const inherits = util.inherits;
11
12 // communicate with events module, but don't require that
13 // module to have to load this one, since this module has
14 // a few side effects.
15 EventEmitter.usingDomains = true;
16
17 // overwrite process.domain with a getter/setter that will allow for more
18 // effective optimizations
19 var _domain = [null];
20 Object.defineProperty(process, 'domain', {
21   enumerable: true,
22   get: function() {
23     return _domain[0];
24   },
25   set: function(arg) {
26     return _domain[0] = arg;
27   }
28 });
29
30 // It's possible to enter one domain while already inside
31 // another one.  the stack is each entered domain.
32 const stack = [];
33 exports._stack = stack;
34
35 // let the process know we're using domains
36 const _domain_flag = process._setupDomainUse(_domain, stack);
37
38 exports.Domain = Domain;
39
40 exports.create = exports.createDomain = function() {
41   return new Domain();
42 };
43
44 // the active domain is always the one that we're currently in.
45 exports.active = null;
46
47
48 inherits(Domain, EventEmitter);
49
50 function Domain() {
51   EventEmitter.call(this);
52
53   this.members = [];
54 }
55
56 Domain.prototype.members = undefined;
57 Domain.prototype._disposed = undefined;
58
59
60 // Called by process._fatalException in case an error was thrown.
61 Domain.prototype._errorHandler = function errorHandler(er) {
62   var caught = false;
63   var self = this;
64
65   function emitError() {
66     var handled = self.emit('error', er);
67
68     // Exit all domains on the stack.  Uncaught exceptions end the
69     // current tick and no domains should be left on the stack
70     // between ticks.
71     stack.length = 0;
72     exports.active = process.domain = null;
73
74     return handled;
75   }
76
77   // ignore errors on disposed domains.
78   //
79   // XXX This is a bit stupid.  We should probably get rid of
80   // domain.dispose() altogether.  It's almost always a terrible
81   // idea.  --isaacs
82   if (this._disposed)
83     return true;
84
85   if (!util.isPrimitive(er)) {
86     er.domain = this;
87     er.domainThrown = true;
88   }
89
90   // The top-level domain-handler is handled separately.
91   //
92   // The reason is that if V8 was passed a command line option
93   // asking it to abort on an uncaught exception (currently
94   // "--abort-on-uncaught-exception"), we want an uncaught exception
95   // in the top-level domain error handler to make the
96   // process abort. Using try/catch here would always make V8 think
97   // that these exceptions are caught, and thus would prevent it from
98   // aborting in these cases.
99   if (stack.length === 1) {
100     // If there's no error handler, do not emit an 'error' event
101     // as this would throw an error, make the process exit, and thus
102     // prevent the process 'uncaughtException' event from being emitted
103     // if a listener is set.
104     if (EventEmitter.listenerCount(self, 'error') > 0) {
105       try {
106         // Set the _emittingTopLevelDomainError so that we know that, even
107         // if technically the top-level domain is still active, it would
108         // be ok to abort on an uncaught exception at this point
109         process._emittingTopLevelDomainError = true;
110         caught = emitError();
111       } finally {
112         process._emittingTopLevelDomainError = false;
113       }
114     }
115   } else {
116     // wrap this in a try/catch so we don't get infinite throwing
117     try {
118       // One of three things will happen here.
119       //
120       // 1. There is a handler, caught = true
121       // 2. There is no handler, caught = false
122       // 3. It throws, caught = false
123       //
124       // If caught is false after this, then there's no need to exit()
125       // the domain, because we're going to crash the process anyway.
126       caught = emitError();
127     } catch (er2) {
128       // The domain error handler threw!  oh no!
129       // See if another domain can catch THIS error,
130       // or else crash on the original one.
131       // If the user already exited it, then don't double-exit.
132       if (this === exports.active) {
133         stack.pop();
134       }
135       if (stack.length) {
136         exports.active = process.domain = stack[stack.length - 1];
137         caught = process._fatalException(er2);
138       } else {
139         caught = false;
140       }
141       return caught;
142     }
143   }
144   return caught;
145 };
146
147
148 Domain.prototype.enter = function() {
149   if (this._disposed) return;
150
151   // note that this might be a no-op, but we still need
152   // to push it onto the stack so that we can pop it later.
153   exports.active = process.domain = this;
154   stack.push(this);
155   _domain_flag[0] = stack.length;
156 };
157
158
159 Domain.prototype.exit = function() {
160   // skip disposed domains, as usual, but also don't do anything if this
161   // domain is not on the stack.
162   var index = stack.lastIndexOf(this);
163   if (this._disposed || index === -1) return;
164
165   // exit all domains until this one.
166   stack.splice(index);
167   _domain_flag[0] = stack.length;
168
169   exports.active = stack[stack.length - 1];
170   process.domain = exports.active;
171 };
172
173
174 // note: this works for timers as well.
175 Domain.prototype.add = function(ee) {
176   // If the domain is disposed or already added, then nothing left to do.
177   if (this._disposed || ee.domain === this)
178     return;
179
180   // has a domain already - remove it first.
181   if (ee.domain)
182     ee.domain.remove(ee);
183
184   // check for circular Domain->Domain links.
185   // This causes bad insanity!
186   //
187   // For example:
188   // var d = domain.create();
189   // var e = domain.create();
190   // d.add(e);
191   // e.add(d);
192   // e.emit('error', er); // RangeError, stack overflow!
193   if (this.domain && (ee instanceof Domain)) {
194     for (var d = this.domain; d; d = d.domain) {
195       if (ee === d) return;
196     }
197   }
198
199   ee.domain = this;
200   this.members.push(ee);
201 };
202
203
204 Domain.prototype.remove = function(ee) {
205   ee.domain = null;
206   var index = this.members.indexOf(ee);
207   if (index !== -1)
208     this.members.splice(index, 1);
209 };
210
211
212 Domain.prototype.run = function(fn) {
213   if (this._disposed)
214     return;
215
216   var ret;
217
218   this.enter();
219   if (arguments.length >= 2) {
220     var len = arguments.length;
221     var args = new Array(len - 1);
222
223     for (var i = 1; i < len; i++)
224       args[i - 1] = arguments[i];
225
226     ret = fn.apply(this, args);
227   } else {
228     ret = fn.call(this);
229   }
230   this.exit();
231
232   return ret;
233 };
234
235
236 function intercepted(_this, self, cb, fnargs) {
237   if (self._disposed)
238     return;
239
240   if (fnargs[0] && fnargs[0] instanceof Error) {
241     var er = fnargs[0];
242     util._extend(er, {
243       domainBound: cb,
244       domainThrown: false,
245       domain: self
246     });
247     self.emit('error', er);
248     return;
249   }
250
251   var args = [];
252   var i, ret;
253
254   self.enter();
255   if (fnargs.length > 1) {
256     for (i = 1; i < fnargs.length; i++)
257       args.push(fnargs[i]);
258     ret = cb.apply(_this, args);
259   } else {
260     ret = cb.call(_this);
261   }
262   self.exit();
263
264   return ret;
265 }
266
267
268 Domain.prototype.intercept = function(cb) {
269   var self = this;
270
271   function runIntercepted() {
272     return intercepted(this, self, cb, arguments);
273   }
274
275   return runIntercepted;
276 };
277
278
279 function bound(_this, self, cb, fnargs) {
280   if (self._disposed)
281     return;
282
283   var ret;
284
285   self.enter();
286   if (fnargs.length > 0)
287     ret = cb.apply(_this, fnargs);
288   else
289     ret = cb.call(_this);
290   self.exit();
291
292   return ret;
293 }
294
295
296 Domain.prototype.bind = function(cb) {
297   var self = this;
298
299   function runBound() {
300     return bound(this, self, cb, arguments);
301   }
302
303   runBound.domain = this;
304
305   return runBound;
306 };
307
308
309 Domain.prototype.dispose = util.deprecate(function() {
310   if (this._disposed) return;
311
312   // if we're the active domain, then get out now.
313   this.exit();
314
315   // remove from parent domain, if there is one.
316   if (this.domain) this.domain.remove(this);
317
318   // kill the references so that they can be properly gc'ed.
319   this.members.length = 0;
320
321   // mark this domain as 'no longer relevant'
322   // so that it can't be entered or activated.
323   this._disposed = true;
324 });