Browse Source

src: move AsyncListener from process to tracing

The AsyncListener API has been moved into the "tracing" module in order
to keep the process object free from unnecessary clutter.

Signed-off-by: Timothy J Fontaine <tjfontaine@gmail.com>
v0.11.12-release
Trevor Norris 11 years ago
parent
commit
6cbfcdad46
  1. 215
      doc/api/process.markdown
  2. 212
      doc/api/tracing.markdown
  3. 17
      lib/timers.js
  4. 357
      lib/tracing.js
  5. 352
      src/node.js
  6. 9
      test/simple/test-asynclistener-error-multiple-handled.js
  7. 9
      test/simple/test-asynclistener-error-multiple-mix.js
  8. 7
      test/simple/test-asynclistener-error-multiple-unhandled.js
  9. 5
      test/simple/test-asynclistener-error-net.js
  10. 5
      test/simple/test-asynclistener-error-throw-in-after.js
  11. 7
      test/simple/test-asynclistener-error-throw-in-before-multiple.js
  12. 5
      test/simple/test-asynclistener-error-throw-in-before.js
  13. 3
      test/simple/test-asynclistener-error-throw-in-error.js
  14. 7
      test/simple/test-asynclistener-error.js
  15. 7
      test/simple/test-asynclistener-multi-timeout.js
  16. 9
      test/simple/test-asynclistener-remove-add-in-before.js
  17. 5
      test/simple/test-asynclistener-remove-before.js
  18. 7
      test/simple/test-asynclistener-remove-in-before.js
  19. 5
      test/simple/test-asynclistener-remove-inflight-error.js
  20. 5
      test/simple/test-asynclistener-remove-inflight.js
  21. 5
      test/simple/test-asynclistener-run-error-once.js
  22. 9
      test/simple/test-asynclistener-run-inst-once.js
  23. 3
      test/simple/test-asynclistener-throw-before-infinite-recursion.js
  24. 7
      test/simple/test-asynclistener.js

215
doc/api/process.markdown

@ -684,219 +684,4 @@ a diff reading, useful for benchmarks and measuring intervals:
// benchmark took 1000000527 nanoseconds // benchmark took 1000000527 nanoseconds
}, 1000); }, 1000);
## Async Listeners
<!-- type=misc -->
Stability: 1 - Experimental
The `AsyncListener` API is the JavaScript interface for the `AsyncWrap`
class which allows developers to be notified about key events in the
lifetime of an asynchronous event. Node performs a lot of asynchronous
events internally, and significant use of this API may have a
**significant performance impact** on your application.
## process.createAsyncListener(callbacksObj[, userData])
* `callbacksObj` {Object} Contains optional callbacks that will fire at
specific times in the life cycle of the asynchronous event.
* `userData` {Value} a value that will be passed to all callbacks.
Returns a constructed `AsyncListener` object.
To begin capturing asynchronous events pass either the `callbacksObj` or
and existing `AsyncListener` instance to [`process.addAsyncListener()`][].
The same `AsyncListener` instance can only be added once to the active
queue, and subsequent attempts to add the instance will be ignored.
To stop capturing pass the `AsyncListener` instance to
[`process.removeAsyncListener()`][]. This does _not_ mean the
`AsyncListener` previously added will stop triggering callbacks. Once
attached to an asynchronous event it will persist with the lifetime of the
asynchronous call stack.
Explanation of function parameters:
`callbacksObj`: An `Object` which may contain three optional fields:
* `create(userData)`: A `Function` called when an asynchronous
event is instantiated. If a `Value` is returned then it will be attached
to the event and overwrite any value that had been passed to
`process.createAsyncListener()`'s `userData` argument. If an initial
`userData` was passed when created, then `create()` will
receive that as a function argument.
* `before(context, userData)`: A `Function` that is called immediately
before the asynchronous callback is about to run. It will be passed both
the `context` (i.e. `this`) of the calling function and the `userData`
either returned from `create()` or passed during construction (if
either occurred).
* `after(context, userData)`: A `Function` called immediately after
the asynchronous event's callback has run. Note this will not be called
if the callback throws and the error is not handled.
* `error(userData, error)`: A `Function` called if the event's
callback threw. If this registered callback returns `true` then Node will
assume the error has been properly handled and resume execution normally.
When multiple `error()` callbacks have been registered only **one** of
those callbacks needs to return `true` for `AsyncListener` to accept that
the error has been handled, but all `error()` callbacks will always be run.
`userData`: A `Value` (i.e. anything) that will be, by default,
attached to all new event instances. This will be overwritten if a `Value`
is returned by `create()`.
Here is an example of overwriting the `userData`:
process.createAsyncListener({
create: function listener(value) {
// value === true
return false;
}, {
before: function before(context, value) {
// value === false
}
}, true);
**Note:** The [EventEmitter][], while used to emit status of an asynchronous
event, is not itself asynchronous. So `create()` will not fire when
an event is added, and `before`/`after` will not fire when emitted
callbacks are called.
## process.addAsyncListener(callbacksObj[, userData])
## process.addAsyncListener(asyncListener)
Returns a constructed `AsyncListener` object and immediately adds it to
the listening queue to begin capturing asynchronous events.
Function parameters can either be the same as
[`process.createAsyncListener()`][], or a constructed `AsyncListener`
object.
Example usage for capturing errors:
var fs = require('fs');
var cntr = 0;
var key = process.addAsyncListener({
create: function onCreate() {
return { uid: cntr++ };
},
before: function onBefore(context, storage) {
// Write directly to stdout or we'll enter a recursive loop
fs.writeSync(1, 'uid: ' + storage.uid + ' is about to run\n');
},
after: function onAfter(context, storage) {
fs.writeSync(1, 'uid: ' + storage.uid + ' ran\n');
},
error: function onError(storage, err) {
// Handle known errors
if (err.message === 'everything is fine') {
fs.writeSync(1, 'handled error just threw:\n');
fs.writeSync(1, err.stack + '\n');
return true;
}
}
});
process.nextTick(function() {
throw new Error('everything is fine');
});
// Output:
// uid: 0 is about to run
// handled error just threw:
// Error: really, it's ok
// at /tmp/test2.js:27:9
// at process._tickCallback (node.js:583:11)
// at Function.Module.runMain (module.js:492:11)
// at startup (node.js:123:16)
// at node.js:1012:3
## process.removeAsyncListener(asyncListener)
Removes the `AsyncListener` from the listening queue.
Removing the `AsyncListener` from the active queue does _not_ mean the
`asyncListener` callbacks will cease to fire on the events they've been
registered. Subsequently, any asynchronous events fired during the
execution of a callback will also have the same `asyncListener` callbacks
attached for future execution. For example:
var fs = require('fs');
var key = process.createAsyncListener({
create: function asyncListener() {
// Write directly to stdout or we'll enter a recursive loop
fs.writeSync(1, 'You summoned me?\n');
}
});
// We want to begin capturing async events some time in the future.
setTimeout(function() {
process.addAsyncListener(key);
// Perform a few additional async events.
setTimeout(function() {
setImmediate(function() {
process.nextTick(function() { });
});
});
// Removing the listener doesn't mean to stop capturing events that
// have already been added.
process.removeAsyncListener(key);
}, 100);
// Output:
// You summoned me?
// You summoned me?
// You summoned me?
// You summoned me?
The fact that we logged 4 asynchronous events is an implementation detail
of Node's [Timers][].
To stop capturing from a specific asynchronous event stack
`process.removeAsyncListener()` must be called from within the call
stack itself. For example:
var fs = require('fs');
var key = process.createAsyncListener({
create: function asyncListener() {
// Write directly to stdout or we'll enter a recursive loop
fs.writeSync(1, 'You summoned me?\n');
}
});
// We want to begin capturing async events some time in the future.
setTimeout(function() {
process.addAsyncListener(key);
// Perform a few additional async events.
setImmediate(function() {
// Stop capturing from this call stack.
process.removeAsyncListener(key);
process.nextTick(function() { });
});
}, 100);
// Output:
// You summoned me?
The user must be explicit and always pass the `AsyncListener` they wish
to remove. It is not possible to simply remove all listeners at once.
[EventEmitter]: events.html#events_class_events_eventemitter [EventEmitter]: events.html#events_class_events_eventemitter
[Timers]: timers.html
[`process.createAsyncListener()`]: #process_process_createasynclistener_asynclistener_callbacksobj_storagevalue
[`process.addAsyncListener()`]: #process_process_addasynclistener_asynclistener
[`process.removeAsyncListener()`]: #process_process_removeasynclistener_asynclistener

212
doc/api/tracing.markdown

@ -58,4 +58,216 @@ Returns an object with the following properties
} }
``` ```
# Async Listeners
The `AsyncListener` API is the JavaScript interface for the `AsyncWrap`
class which allows developers to be notified about key events in the
lifetime of an asynchronous event. Node performs a lot of asynchronous
events internally, and significant use of this API may have a
**significant performance impact** on your application.
## tracing.createAsyncListener(callbacksObj[, userData])
* `callbacksObj` {Object} Contains optional callbacks that will fire at
specific times in the life cycle of the asynchronous event.
* `userData` {Value} a value that will be passed to all callbacks.
Returns a constructed `AsyncListener` object.
To begin capturing asynchronous events pass either the `callbacksObj` or
pass an existing `AsyncListener` instance to [`tracing.addAsyncListener()`][].
The same `AsyncListener` instance can only be added once to the active
queue, and subsequent attempts to add the instance will be ignored.
To stop capturing pass the `AsyncListener` instance to
[`tracing.removeAsyncListener()`][]. This does _not_ mean the
`AsyncListener` previously added will stop triggering callbacks. Once
attached to an asynchronous event it will persist with the lifetime of the
asynchronous call stack.
Explanation of function parameters:
`callbacksObj`: An `Object` which may contain several optional fields:
* `create(userData)`: A `Function` called when an asynchronous
event is instantiated. If a `Value` is returned then it will be attached
to the event and overwrite any value that had been passed to
`tracing.createAsyncListener()`'s `userData` argument. If an initial
`userData` was passed when created, then `create()` will
receive that as a function argument.
* `before(context, userData)`: A `Function` that is called immediately
before the asynchronous callback is about to run. It will be passed both
the `context` (i.e. `this`) of the calling function and the `userData`
either returned from `create()` or passed during construction (if
either occurred).
* `after(context, userData)`: A `Function` called immediately after
the asynchronous event's callback has run. Note this will not be called
if the callback throws and the error is not handled.
* `error(userData, error)`: A `Function` called if the event's
callback threw. If this registered callback returns `true` then Node will
assume the error has been properly handled and resume execution normally.
When multiple `error()` callbacks have been registered only **one** of
those callbacks needs to return `true` for `AsyncListener` to accept that
the error has been handled, but all `error()` callbacks will always be run.
`userData`: A `Value` (i.e. anything) that will be, by default,
attached to all new event instances. This will be overwritten if a `Value`
is returned by `create()`.
Here is an example of overwriting the `userData`:
tracing.createAsyncListener({
create: function listener(value) {
// value === true
return false;
}, {
before: function before(context, value) {
// value === false
}
}, true);
**Note:** The [EventEmitter][], while used to emit status of an asynchronous
event, is not itself asynchronous. So `create()` will not fire when
an event is added, and `before()`/`after()` will not fire when emitted
callbacks are called.
## tracing.addAsyncListener(callbacksObj[, userData])
## tracing.addAsyncListener(asyncListener)
Returns a constructed `AsyncListener` object and immediately adds it to
the listening queue to begin capturing asynchronous events.
Function parameters can either be the same as
[`tracing.createAsyncListener()`][], or a constructed `AsyncListener`
object.
Example usage for capturing errors:
var fs = require('fs');
var cntr = 0;
var key = tracing.addAsyncListener({
create: function onCreate() {
return { uid: cntr++ };
},
before: function onBefore(context, storage) {
// Write directly to stdout or we'll enter a recursive loop
fs.writeSync(1, 'uid: ' + storage.uid + ' is about to run\n');
},
after: function onAfter(context, storage) {
fs.writeSync(1, 'uid: ' + storage.uid + ' ran\n');
},
error: function onError(storage, err) {
// Handle known errors
if (err.message === 'everything is fine') {
// Writing to stderr this time.
fs.writeSync(2, 'handled error just threw:\n');
fs.writeSync(2, err.stack + '\n');
return true;
}
}
});
process.nextTick(function() {
throw new Error('everything is fine');
});
// Output:
// uid: 0 is about to run
// handled error just threw:
// Error: really, it's ok
// at /tmp/test2.js:27:9
// at process._tickCallback (node.js:583:11)
// at Function.Module.runMain (module.js:492:11)
// at startup (node.js:123:16)
// at node.js:1012:3
## tracing.removeAsyncListener(asyncListener)
Removes the `AsyncListener` from the listening queue.
Removing the `AsyncListener` from the active queue does _not_ mean the
`asyncListener` callbacks will cease to fire on the events they've been
registered. Subsequently, any asynchronous events fired during the
execution of a callback will also have the same `asyncListener` callbacks
attached for future execution. For example:
var fs = require('fs');
var key = tracing.createAsyncListener({
create: function asyncListener() {
// Write directly to stdout or we'll enter a recursive loop
fs.writeSync(1, 'You summoned me?\n');
}
});
// We want to begin capturing async events some time in the future.
setTimeout(function() {
tracing.addAsyncListener(key);
// Perform a few additional async events.
setTimeout(function() {
setImmediate(function() {
process.nextTick(function() { });
});
});
// Removing the listener doesn't mean to stop capturing events that
// have already been added.
tracing.removeAsyncListener(key);
}, 100);
// Output:
// You summoned me?
// You summoned me?
// You summoned me?
// You summoned me?
The fact that we logged 4 asynchronous events is an implementation detail
of Node's [Timers][].
To stop capturing from a specific asynchronous event stack
`tracing.removeAsyncListener()` must be called from within the call
stack itself. For example:
var fs = require('fs');
var key = tracing.createAsyncListener({
create: function asyncListener() {
// Write directly to stdout or we'll enter a recursive loop
fs.writeSync(1, 'You summoned me?\n');
}
});
// We want to begin capturing async events some time in the future.
setTimeout(function() {
tracing.addAsyncListener(key);
// Perform a few additional async events.
setImmediate(function() {
// Stop capturing from this call stack.
tracing.removeAsyncListener(key);
process.nextTick(function() { });
});
}, 100);
// Output:
// You summoned me?
The user must be explicit and always pass the `AsyncListener` they wish
to remove. It is not possible to simply remove all listeners at once.
[EventEmitter]: events.html#events_class_events_eventemitter [EventEmitter]: events.html#events_class_events_eventemitter
[Timers]: timers.html
[`tracing.createAsyncListener()`]: #tracing_tracing_createasynclistener_asynclistener_callbacksobj_storagevalue
[`tracing.addAsyncListener()`]: #tracing_tracing_addasynclistener_asynclistener
[`tracing.removeAsyncListener()`]: #tracing_tracing_removeasynclistener_asynclistener

17
lib/timers.js

@ -30,19 +30,20 @@ var TIMEOUT_MAX = 2147483647; // 2^31-1
var debug = require('util').debuglog('timer'); var debug = require('util').debuglog('timer');
var asyncFlags = process._asyncFlags; var tracing = require('tracing');
var runAsyncQueue = process._runAsyncQueue; var asyncFlags = tracing._asyncFlags;
var loadAsyncQueue = process._loadAsyncQueue; var runAsyncQueue = tracing._runAsyncQueue;
var unloadAsyncQueue = process._unloadAsyncQueue; var loadAsyncQueue = tracing._loadAsyncQueue;
var unloadAsyncQueue = tracing._unloadAsyncQueue;
// Same as in AsyncListener in env.h // Same as in AsyncListener in env.h
var kHasListener = 0; var kHasListener = 0;
// Do a little housekeeping. // Do a little housekeeping.
delete process._asyncFlags; delete tracing._asyncFlags;
delete process._runAsyncQueue; delete tracing._runAsyncQueue;
delete process._loadAsyncQueue; delete tracing._loadAsyncQueue;
delete process._unloadAsyncQueue; delete tracing._unloadAsyncQueue;
// IDLE TIMEOUTS // IDLE TIMEOUTS

357
lib/tracing.js

@ -20,7 +20,31 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE. // USE OR OTHER DEALINGS IN THE SOFTWARE.
var EventEmitter = require('events'); var EventEmitter = require('events');
var binding = process.binding('v8'); var v8binding, process;
// This needs to be loaded early, and before the "process" object is made
// global. So allow src/node.js to pass the process object in during
// initialization.
exports._nodeInitialization = function nodeInitialization(pobj) {
process = pobj;
v8binding = process.binding('v8');
// Finish setting up the v8 Object.
v8.getHeapStatistics = v8binding.getHeapStatistics;
// Part of the AsyncListener setup to share objects/callbacks with the
// native layer.
process._setupAsyncListener(asyncFlags,
runAsyncQueue,
loadAsyncQueue,
unloadAsyncQueue);
// Do a little housekeeping.
delete exports._nodeInitialization;
};
// v8
var v8 = exports.v8 = new EventEmitter(); var v8 = exports.v8 = new EventEmitter();
@ -32,16 +56,341 @@ function emitGC(before, after) {
v8.on('newListener', function(name) { v8.on('newListener', function(name) {
if (name === 'gc' && EventEmitter.listenerCount(this, name) === 0) { if (name === 'gc' && EventEmitter.listenerCount(this, name) === 0) {
binding.startGarbageCollectionTracking(emitGC); v8binding.startGarbageCollectionTracking(emitGC);
} }
}); });
v8.on('removeListener', function(name) { v8.on('removeListener', function(name) {
if (name === 'gc' && EventEmitter.listenerCount(this, name) === 0) { if (name === 'gc' && EventEmitter.listenerCount(this, name) === 0) {
binding.stopGarbageCollectionTracking(); v8binding.stopGarbageCollectionTracking();
} }
}); });
v8.getHeapStatistics = binding.getHeapStatistics; // AsyncListener
// new Array() is used here because it is more efficient for sparse
// arrays. Please *do not* change these to simple bracket notation.
// Track the active queue of AsyncListeners that have been added.
var asyncQueue = new Array();
// Keep the stack of all contexts that have been loaded in the
// execution chain of asynchronous events.
var contextStack = new Array();
var currentContext = undefined;
// Incremental uid for new AsyncListener instances.
var alUid = 0;
// Stateful flags shared with Environment for quick JS/C++
// communication.
var asyncFlags = {};
// Prevent accidentally suppressed thrown errors from before/after.
var inAsyncTick = false;
// To prevent infinite recursion when an error handler also throws
// flag when an error is currenly being handled.
var inErrorTick = false;
// Needs to be the same as src/env.h
var kHasListener = 0;
var kWatchedProviders = 1;
// Flags to determine what async listeners are available.
var HAS_CREATE_AL = 1 << 0;
var HAS_BEFORE_AL = 1 << 1;
var HAS_AFTER_AL = 1 << 2;
var HAS_ERROR_AL = 1 << 3;
// _errorHandler is scoped so it's also accessible by _fatalException.
exports._errorHandler = errorHandler;
// Needs to be accessible from lib/timers.js so they know when async
// listeners are currently in queue. They'll be cleaned up once
// references there are made.
exports._asyncFlags = asyncFlags;
exports._runAsyncQueue = runAsyncQueue;
exports._loadAsyncQueue = loadAsyncQueue;
exports._unloadAsyncQueue = unloadAsyncQueue;
// Public API.
exports.createAsyncListener = createAsyncListener;
exports.addAsyncListener = addAsyncListener;
exports.removeAsyncListener = removeAsyncListener;
// Load the currently executing context as the current context, and
// create a new asyncQueue that can receive any added queue items
// during the executing of the callback.
function loadContext(ctx) {
contextStack.push(currentContext);
currentContext = ctx;
asyncFlags[kHasListener] = 1;
}
function unloadContext() {
currentContext = contextStack.pop();
if (currentContext === undefined && asyncQueue.length === 0)
asyncFlags[kHasListener] = 0;
}
// Run all the async listeners attached when an asynchronous event is
// instantiated.
function runAsyncQueue(context) {
var queue = new Array();
var data = new Array();
var ccQueue, i, item, queueItem, value;
context._asyncQueue = queue;
context._asyncData = data;
context._asyncFlags = 0;
inAsyncTick = true;
// First run through all callbacks in the currentContext. These may
// add new AsyncListeners to the asyncQueue during execution. Hence
// why they need to be evaluated first.
if (currentContext) {
ccQueue = currentContext._asyncQueue;
context._asyncFlags |= currentContext._asyncFlags;
for (i = 0; i < ccQueue.length; i++) {
queueItem = ccQueue[i];
queue[queue.length] = queueItem;
if ((queueItem.callback_flags & HAS_CREATE_AL) === 0) {
data[queueItem.uid] = queueItem.data;
continue;
}
value = queueItem.create(queueItem.data);
data[queueItem.uid] = (value === undefined) ? queueItem.data : value;
}
}
// Then run through all items in the asyncQueue
if (asyncQueue) {
for (i = 0; i < asyncQueue.length; i++) {
queueItem = asyncQueue[i];
// Quick way to check if an AL instance with the same uid was
// already run from currentContext.
if (data[queueItem.uid] !== undefined)
continue;
queue[queue.length] = queueItem;
context._asyncFlags |= queueItem.callback_flags;
if ((queueItem.callback_flags & HAS_CREATE_AL) === 0) {
data[queueItem.uid] = queueItem.data;
continue;
}
value = queueItem.create(queueItem.data);
data[queueItem.uid] = (value === undefined) ? queueItem.data : value;
}
}
inAsyncTick = false;
}
// Load the AsyncListener queue attached to context and run all
// "before" callbacks, if they exist.
function loadAsyncQueue(context) {
loadContext(context);
if ((context._asyncFlags & HAS_BEFORE_AL) === 0)
return;
var queue = context._asyncQueue;
var data = context._asyncData;
var i, queueItem;
inAsyncTick = true;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.callback_flags & HAS_BEFORE_AL) > 0)
queueItem.before(context, data[queueItem.uid]);
}
inAsyncTick = false;
}
// Unload the AsyncListener queue attached to context and run all
// "after" callbacks, if they exist.
function unloadAsyncQueue(context) {
if ((context._asyncFlags & HAS_AFTER_AL) === 0) {
unloadContext();
return;
}
var queue = context._asyncQueue;
var data = context._asyncData;
var i, queueItem;
inAsyncTick = true;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.callback_flags & HAS_AFTER_AL) > 0)
queueItem.after(context, data[queueItem.uid]);
}
inAsyncTick = false;
unloadContext();
}
// Handle errors that are thrown while in the context of an
// AsyncListener. If an error is thrown from an AsyncListener
// callback error handlers will be called once more to report
// the error, then the application will die forcefully.
function errorHandler(er) {
if (inErrorTick)
return false;
var handled = false;
var i, queueItem, threw;
inErrorTick = true;
// First process error callbacks from the current context.
if (currentContext && (currentContext._asyncFlags & HAS_ERROR_AL) > 0) {
var queue = currentContext._asyncQueue;
var data = currentContext._asyncData;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.callback_flags & HAS_ERROR_AL) === 0)
continue;
try {
threw = true;
// While it would be possible to pass in currentContext, if
// the error is thrown from the "create" callback then there's
// a chance the object hasn't been fully constructed.
handled = queueItem.error(data[queueItem.uid], er) || handled;
threw = false;
} finally {
// If the error callback thew then die quickly. Only allow the
// exit events to be processed.
if (threw) {
process._exiting = true;
process.emit('exit', 1);
}
}
}
}
// Now process callbacks from any existing queue.
if (asyncQueue) {
for (i = 0; i < asyncQueue.length; i++) {
queueItem = asyncQueue[i];
if ((queueItem.callback_flags & HAS_ERROR_AL) === 0 ||
(data && data[queueItem.uid] !== undefined))
continue;
try {
threw = true;
handled = queueItem.error(queueItem.data, er) || handled;
threw = false;
} finally {
// If the error callback thew then die quickly. Only allow the
// exit events to be processed.
if (threw) {
process._exiting = true;
process.emit('exit', 1);
}
}
}
}
inErrorTick = false;
unloadContext();
// TODO(trevnorris): If the error was handled, should the after callbacks
// be fired anyways?
return handled && !inAsyncTick;
}
// Instance function of an AsyncListener object.
function AsyncListenerInst(callbacks, data) {
if (typeof callbacks.create === 'function') {
this.create = callbacks.create;
this.callback_flags |= HAS_CREATE_AL;
}
if (typeof callbacks.before === 'function') {
this.before = callbacks.before;
this.callback_flags |= HAS_BEFORE_AL;
}
if (typeof callbacks.after === 'function') {
this.after = callbacks.after;
this.callback_flags |= HAS_AFTER_AL;
}
if (typeof callbacks.error === 'function') {
this.error = callbacks.error;
this.callback_flags |= HAS_ERROR_AL;
}
this.uid = ++alUid;
this.data = data === undefined ? null : data;
}
AsyncListenerInst.prototype.create = undefined;
AsyncListenerInst.prototype.before = undefined;
AsyncListenerInst.prototype.after = undefined;
AsyncListenerInst.prototype.error = undefined;
AsyncListenerInst.prototype.data = undefined;
AsyncListenerInst.prototype.uid = 0;
AsyncListenerInst.prototype.callback_flags = 0;
// Create new async listener object. Useful when instantiating a new
// object and want the listener instance, but not add it to the stack.
// If an existing AsyncListenerInst is passed then any new "data" is
// ignored.
function createAsyncListener(callbacks, data) {
if (typeof callbacks !== 'object' || callbacks == null)
throw new TypeError('callbacks argument must be an object');
if (callbacks instanceof AsyncListenerInst)
return callbacks;
else
return new AsyncListenerInst(callbacks, data);
}
// Add a listener to the current queue.
function addAsyncListener(callbacks, data) {
// Fast track if a new AsyncListenerInst has to be created.
if (!(callbacks instanceof AsyncListenerInst)) {
callbacks = createAsyncListener(callbacks, data);
asyncQueue.push(callbacks);
asyncFlags[kHasListener] = 1;
return callbacks;
}
var inQueue = false;
// The asyncQueue will be small. Probably always <= 3 items.
for (var i = 0; i < asyncQueue.length; i++) {
if (callbacks === asyncQueue[i]) {
inQueue = true;
break;
}
}
// Make sure the callback doesn't already exist in the queue.
if (!inQueue) {
asyncQueue.push(callbacks);
asyncFlags[kHasListener] = 1;
}
return callbacks;
}
// Remove listener from the current queue. Though this will not remove
// the listener from the current context. So callback propagation will
// continue.
function removeAsyncListener(obj) {
for (var i = 0; i < asyncQueue.length; i++) {
if (obj === asyncQueue[i]) {
asyncQueue.splice(i, 1);
break;
}
}
if (asyncQueue.length > 0 || currentContext !== undefined)
asyncFlags[kHasListener] = 1;
else
asyncFlags[kHasListener] = 0;
}

352
src/node.js

@ -26,7 +26,6 @@
// of the startup process, so many dependencies are invoked lazily. // of the startup process, so many dependencies are invoked lazily.
(function(process) { (function(process) {
this.global = this; this.global = this;
var _errorHandler;
function startup() { function startup() {
var EventEmitter = NativeModule.require('events').EventEmitter; var EventEmitter = NativeModule.require('events').EventEmitter;
@ -40,6 +39,9 @@
process.EventEmitter = EventEmitter; // process.EventEmitter is deprecated process.EventEmitter = EventEmitter; // process.EventEmitter is deprecated
// Setup the tracing module
NativeModule.require('tracing')._nodeInitialization(process);
// do this good and early, since it handles errors. // do this good and early, since it handles errors.
startup.processFatal(); startup.processFatal();
@ -47,7 +49,6 @@
startup.globalTimeouts(); startup.globalTimeouts();
startup.globalConsole(); startup.globalConsole();
startup.processAsyncListener();
startup.processAssert(); startup.processAssert();
startup.processConfig(); startup.processConfig();
startup.processNextTick(); startup.processNextTick();
@ -220,6 +221,11 @@
}; };
startup.processFatal = function() { startup.processFatal = function() {
var tracing = NativeModule.require('tracing');
var _errorHandler = tracing._errorHandler;
// Cleanup
delete tracing._errorHandler;
process._fatalException = function(er) { process._fatalException = function(er) {
// First run through error handlers from asyncListener. // First run through error handlers from asyncListener.
var caught = _errorHandler(er); var caught = _errorHandler(er);
@ -255,339 +261,6 @@
}; };
}; };
startup.processAsyncListener = function() {
// new Array() is used here because it is more efficient for sparse
// arrays. Please *do not* change these to simple bracket notation.
// Track the active queue of AsyncListeners that have been added.
var asyncQueue = new Array();
// Keep the stack of all contexts that have been loaded in the
// execution chain of asynchronous events.
var contextStack = new Array();
var currentContext = undefined;
// Incremental uid for new AsyncListener instances.
var alUid = 0;
// Stateful flags shared with Environment for quick JS/C++
// communication.
var asyncFlags = {};
// Prevent accidentally suppressed thrown errors from before/after.
var inAsyncTick = false;
// To prevent infinite recursion when an error handler also throws
// flag when an error is currenly being handled.
var inErrorTick = false;
// Needs to be the same as src/env.h
var kHasListener = 0;
var kWatchedProviders = 1;
// Flags to determine what async listeners are available.
var HAS_CREATE_AL = 1 << 0;
var HAS_BEFORE_AL = 1 << 1;
var HAS_AFTER_AL = 1 << 2;
var HAS_ERROR_AL = 1 << 3;
// _errorHandler is scoped so it's also accessible by _fatalException.
_errorHandler = errorHandler;
// Needs to be accessible from lib/timers.js so they know when async
// listeners are currently in queue. They'll be cleaned up once
// references there are made.
process._asyncFlags = asyncFlags;
process._runAsyncQueue = runAsyncQueue;
process._loadAsyncQueue = loadAsyncQueue;
process._unloadAsyncQueue = unloadAsyncQueue;
// Public API.
process.createAsyncListener = createAsyncListener;
process.addAsyncListener = addAsyncListener;
process.removeAsyncListener = removeAsyncListener;
// Setup shared objects/callbacks with native layer.
process._setupAsyncListener(asyncFlags,
runAsyncQueue,
loadAsyncQueue,
unloadAsyncQueue);
// Load the currently executing context as the current context, and
// create a new asyncQueue that can receive any added queue items
// during the executing of the callback.
function loadContext(ctx) {
contextStack.push(currentContext);
currentContext = ctx;
asyncFlags[kHasListener] = 1;
}
function unloadContext() {
currentContext = contextStack.pop();
if (currentContext === undefined && asyncQueue.length === 0)
asyncFlags[kHasListener] = 0;
}
// Run all the async listeners attached when an asynchronous event is
// instantiated.
function runAsyncQueue(context) {
var queue = new Array();
var data = new Array();
var ccQueue, i, item, queueItem, value;
context._asyncQueue = queue;
context._asyncData = data;
context._asyncFlags = 0;
inAsyncTick = true;
// First run through all callbacks in the currentContext. These may
// add new AsyncListeners to the asyncQueue during execution. Hence
// why they need to be evaluated first.
if (currentContext) {
ccQueue = currentContext._asyncQueue;
context._asyncFlags |= currentContext._asyncFlags;
for (i = 0; i < ccQueue.length; i++) {
queueItem = ccQueue[i];
queue[queue.length] = queueItem;
if ((queueItem.callback_flags & HAS_CREATE_AL) === 0) {
data[queueItem.uid] = queueItem.data;
continue;
}
value = queueItem.create(queueItem.data);
data[queueItem.uid] = (value === undefined) ? queueItem.data : value;
}
}
// Then run through all items in the asyncQueue
if (asyncQueue) {
for (i = 0; i < asyncQueue.length; i++) {
queueItem = asyncQueue[i];
// Quick way to check if an AL instance with the same uid was
// already run from currentContext.
if (data[queueItem.uid] !== undefined)
continue;
queue[queue.length] = queueItem;
context._asyncFlags |= queueItem.callback_flags;
if ((queueItem.callback_flags & HAS_CREATE_AL) === 0) {
data[queueItem.uid] = queueItem.data;
continue;
}
value = queueItem.create(queueItem.data);
data[queueItem.uid] = (value === undefined) ? queueItem.data : value;
}
}
inAsyncTick = false;
}
// Load the AsyncListener queue attached to context and run all
// "before" callbacks, if they exist.
function loadAsyncQueue(context) {
loadContext(context);
if ((context._asyncFlags & HAS_BEFORE_AL) === 0)
return;
var queue = context._asyncQueue;
var data = context._asyncData;
var i, queueItem;
inAsyncTick = true;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.callback_flags & HAS_BEFORE_AL) > 0)
queueItem.before(context, data[queueItem.uid]);
}
inAsyncTick = false;
}
// Unload the AsyncListener queue attached to context and run all
// "after" callbacks, if they exist.
function unloadAsyncQueue(context) {
if ((context._asyncFlags & HAS_AFTER_AL) === 0) {
unloadContext();
return;
}
var queue = context._asyncQueue;
var data = context._asyncData;
var i, queueItem;
inAsyncTick = true;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.callback_flags & HAS_AFTER_AL) > 0)
queueItem.after(context, data[queueItem.uid]);
}
inAsyncTick = false;
unloadContext();
}
// Handle errors that are thrown while in the context of an
// AsyncListener. If an error is thrown from an AsyncListener
// callback error handlers will be called once more to report
// the error, then the application will die forcefully.
function errorHandler(er) {
if (inErrorTick)
return false;
var handled = false;
var i, queueItem, threw;
inErrorTick = true;
// First process error callbacks from the current context.
if (currentContext && (currentContext._asyncFlags & HAS_ERROR_AL) > 0) {
var queue = currentContext._asyncQueue;
var data = currentContext._asyncData;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.callback_flags & HAS_ERROR_AL) === 0)
continue;
try {
threw = true;
// While it would be possible to pass in currentContext, if
// the error is thrown from the "create" callback then there's
// a chance the object hasn't been fully constructed.
handled = queueItem.error(data[queueItem.uid], er) || handled;
threw = false;
} finally {
// If the error callback thew then die quickly. Only allow the
// exit events to be processed.
if (threw) {
process._exiting = true;
process.emit('exit', 1);
}
}
}
}
// Now process callbacks from any existing queue.
if (asyncQueue) {
for (i = 0; i < asyncQueue.length; i++) {
queueItem = asyncQueue[i];
if ((queueItem.callback_flags & HAS_ERROR_AL) === 0 ||
(data && data[queueItem.uid] !== undefined))
continue;
try {
threw = true;
handled = queueItem.error(queueItem.data, er) || handled;
threw = false;
} finally {
// If the error callback thew then die quickly. Only allow the
// exit events to be processed.
if (threw) {
process._exiting = true;
process.emit('exit', 1);
}
}
}
}
inErrorTick = false;
unloadContext();
// TODO(trevnorris): If the error was handled, should the after callbacks
// be fired anyways?
return handled && !inAsyncTick;
}
// Instance function of an AsyncListener object.
function AsyncListenerInst(callbacks, data) {
if (typeof callbacks.create === 'function') {
this.create = callbacks.create;
this.callback_flags |= HAS_CREATE_AL;
}
if (typeof callbacks.before === 'function') {
this.before = callbacks.before;
this.callback_flags |= HAS_BEFORE_AL;
}
if (typeof callbacks.after === 'function') {
this.after = callbacks.after;
this.callback_flags |= HAS_AFTER_AL;
}
if (typeof callbacks.error === 'function') {
this.error = callbacks.error;
this.callback_flags |= HAS_ERROR_AL;
}
this.uid = ++alUid;
this.data = data === undefined ? null : data;
}
AsyncListenerInst.prototype.create = undefined;
AsyncListenerInst.prototype.before = undefined;
AsyncListenerInst.prototype.after = undefined;
AsyncListenerInst.prototype.error = undefined;
AsyncListenerInst.prototype.data = undefined;
AsyncListenerInst.prototype.uid = 0;
AsyncListenerInst.prototype.callback_flags = 0;
// Create new async listener object. Useful when instantiating a new
// object and want the listener instance, but not add it to the stack.
// If an existing AsyncListenerInst is passed then any new "data" is
// ignored.
function createAsyncListener(callbacks, data) {
if (typeof callbacks !== 'object' || callbacks == null)
throw new TypeError('callbacks argument must be an object');
if (callbacks instanceof AsyncListenerInst)
return callbacks;
else
return new AsyncListenerInst(callbacks, data);
}
// Add a listener to the current queue.
function addAsyncListener(callbacks, data) {
// Fast track if a new AsyncListenerInst has to be created.
if (!(callbacks instanceof AsyncListenerInst)) {
callbacks = createAsyncListener(callbacks, data);
asyncQueue.push(callbacks);
asyncFlags[kHasListener] = 1;
return callbacks;
}
var inQueue = false;
// The asyncQueue will be small. Probably always <= 3 items.
for (var i = 0; i < asyncQueue.length; i++) {
if (callbacks === asyncQueue[i]) {
inQueue = true;
break;
}
}
// Make sure the callback doesn't already exist in the queue.
if (!inQueue) {
asyncQueue.push(callbacks);
asyncFlags[kHasListener] = 1;
}
return callbacks;
}
// Remove listener from the current queue. Though this will not remove
// the listener from the current context. So callback propagation will
// continue.
function removeAsyncListener(obj) {
for (var i = 0; i < asyncQueue.length; i++) {
if (obj === asyncQueue[i]) {
asyncQueue.splice(i, 1);
break;
}
}
if (asyncQueue.length > 0 || currentContext !== undefined)
asyncFlags[kHasListener] = 1;
else
asyncFlags[kHasListener] = 0;
}
};
var assert; var assert;
startup.processAssert = function() { startup.processAssert = function() {
assert = process.assert = function(x, msg) { assert = process.assert = function(x, msg) {
@ -611,11 +284,12 @@
}; };
startup.processNextTick = function() { startup.processNextTick = function() {
var tracing = NativeModule.require('tracing');
var nextTickQueue = []; var nextTickQueue = [];
var asyncFlags = process._asyncFlags; var asyncFlags = tracing._asyncFlags;
var _runAsyncQueue = process._runAsyncQueue; var _runAsyncQueue = tracing._runAsyncQueue;
var _loadAsyncQueue = process._loadAsyncQueue; var _loadAsyncQueue = tracing._loadAsyncQueue;
var _unloadAsyncQueue = process._unloadAsyncQueue; var _unloadAsyncQueue = tracing._unloadAsyncQueue;
// This tickInfo thing is used so that the C++ code in src/node.cc // This tickInfo thing is used so that the C++ code in src/node.cc
// can have easy accesss to our nextTick state, and avoid unnecessary // can have easy accesss to our nextTick state, and avoid unnecessary

9
test/simple/test-asynclistener-error-multiple-handled.js

@ -21,6 +21,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var active = null; var active = null;
var cntr = 0; var cntr = 0;
@ -49,16 +50,16 @@ var asyncNoHandleError1 = {
}; };
var listeners = [ var listeners = [
process.addAsyncListener(asyncNoHandleError0), tracing.addAsyncListener(asyncNoHandleError0),
process.addAsyncListener(asyncNoHandleError1) tracing.addAsyncListener(asyncNoHandleError1)
]; ];
process.nextTick(function() { process.nextTick(function() {
throw new Error(); throw new Error();
}); });
process.removeAsyncListener(listeners[0]); tracing.removeAsyncListener(listeners[0]);
process.removeAsyncListener(listeners[1]); tracing.removeAsyncListener(listeners[1]);
process.on('exit', function(code) { process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that // If the exit code isn't ok then return early to throw the stack that

9
test/simple/test-asynclistener-error-multiple-mix.js

@ -21,6 +21,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var results = []; var results = [];
var asyncNoHandleError = { var asyncNoHandleError = {
@ -37,8 +38,8 @@ var asyncHandleError = {
}; };
var listeners = [ var listeners = [
process.addAsyncListener(asyncHandleError), tracing.addAsyncListener(asyncHandleError),
process.addAsyncListener(asyncNoHandleError) tracing.addAsyncListener(asyncNoHandleError)
]; ];
// Even if an error handler returns true, both should fire. // Even if an error handler returns true, both should fire.
@ -46,8 +47,8 @@ process.nextTick(function() {
throw new Error(); throw new Error();
}); });
process.removeAsyncListener(listeners[0]); tracing.removeAsyncListener(listeners[0]);
process.removeAsyncListener(listeners[1]); tracing.removeAsyncListener(listeners[1]);
process.on('exit', function(code) { process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that // If the exit code isn't ok then return early to throw the stack that

7
test/simple/test-asynclistener-error-multiple-unhandled.js

@ -21,6 +21,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
function onAsync0() { function onAsync0() {
return 0; return 0;
@ -45,8 +46,8 @@ var asyncNoHandleError1 = {
}; };
var listeners = [ var listeners = [
process.addAsyncListener(asyncNoHandleError0), tracing.addAsyncListener(asyncNoHandleError0),
process.addAsyncListener(asyncNoHandleError1) tracing.addAsyncListener(asyncNoHandleError1)
]; ];
var uncaughtFired = false; var uncaughtFired = false;
@ -71,7 +72,7 @@ process.on('exit', function(code) {
// Need to remove the async listeners or tests will always pass // Need to remove the async listeners or tests will always pass
for (var i = 0; i < listeners.length; i++) for (var i = 0; i < listeners.length; i++)
process.removeAsyncListener(listeners[i]); tracing.removeAsyncListener(listeners[i]);
assert.ok(uncaughtFired); assert.ok(uncaughtFired);
console.log('ok'); console.log('ok');

5
test/simple/test-asynclistener-error-net.js

@ -24,6 +24,7 @@ var assert = require('assert');
var dns = require('dns'); var dns = require('dns');
var fs = require('fs'); var fs = require('fs');
var net = require('net'); var net = require('net');
var tracing = require('tracing');
var errorMsgs = []; var errorMsgs = [];
var caught = 0; var caught = 0;
@ -45,10 +46,10 @@ var callbacksObj = {
} }
}; };
var listener = process.addAsyncListener(callbacksObj); var listener = tracing.addAsyncListener(callbacksObj);
process.on('exit', function(code) { process.on('exit', function(code) {
process.removeAsyncListener(listener); tracing.removeAsyncListener(listener);
if (code > 0) if (code > 0)
return; return;

5
test/simple/test-asynclistener-error-throw-in-after.js

@ -21,6 +21,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var once = 0; var once = 0;
@ -37,7 +38,7 @@ var handlers = {
} }
} }
var key = process.addAsyncListener(handlers); var key = tracing.addAsyncListener(handlers);
var uncaughtFired = false; var uncaughtFired = false;
process.on('uncaughtException', function(err) { process.on('uncaughtException', function(err) {
@ -48,7 +49,7 @@ process.on('uncaughtException', function(err) {
process.nextTick(function() { }); process.nextTick(function() { });
process.removeAsyncListener(key); tracing.removeAsyncListener(key);
process.on('exit', function(code) { process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that // If the exit code isn't ok then return early to throw the stack that

7
test/simple/test-asynclistener-error-throw-in-before-multiple.js

@ -21,6 +21,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var once = 0; var once = 0;
@ -50,8 +51,8 @@ var handlers1 = {
} }
var listeners = [ var listeners = [
process.addAsyncListener(handlers), tracing.addAsyncListener(handlers),
process.addAsyncListener(handlers1) tracing.addAsyncListener(handlers1)
]; ];
var uncaughtFired = false; var uncaughtFired = false;
@ -65,7 +66,7 @@ process.on('uncaughtException', function(err) {
process.nextTick(function() { }); process.nextTick(function() { });
for (var i = 0; i < listeners.length; i++) for (var i = 0; i < listeners.length; i++)
process.removeAsyncListener(listeners[i]); tracing.removeAsyncListener(listeners[i]);
process.on('exit', function(code) { process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that // If the exit code isn't ok then return early to throw the stack that

5
test/simple/test-asynclistener-error-throw-in-before.js

@ -21,6 +21,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var once = 0; var once = 0;
@ -37,7 +38,7 @@ var handlers = {
} }
} }
var key = process.addAsyncListener(handlers); var key = tracing.addAsyncListener(handlers);
var uncaughtFired = false; var uncaughtFired = false;
process.on('uncaughtException', function(err) { process.on('uncaughtException', function(err) {
@ -49,7 +50,7 @@ process.on('uncaughtException', function(err) {
process.nextTick(function() { }); process.nextTick(function() { });
process.removeAsyncListener(key); tracing.removeAsyncListener(key);
process.on('exit', function(code) { process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that // If the exit code isn't ok then return early to throw the stack that

3
test/simple/test-asynclistener-error-throw-in-error.js

@ -22,6 +22,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var spawn = require('child_process').spawn; var spawn = require('child_process').spawn;
var tracing = require('tracing');
var checkStr = 'WRITTEN ON EXIT'; var checkStr = 'WRITTEN ON EXIT';
@ -34,7 +35,7 @@ else
function runChild() { function runChild() {
var cntr = 0; var cntr = 0;
var key = process.addAsyncListener({ var key = tracing.addAsyncListener({
error: function onError() { error: function onError() {
cntr++; cntr++;
throw new Error('onError'); throw new Error('onError');

7
test/simple/test-asynclistener-error.js

@ -24,9 +24,10 @@ var assert = require('assert');
var dns = require('dns'); var dns = require('dns');
var fs = require('fs'); var fs = require('fs');
var net = require('net'); var net = require('net');
var tracing = require('tracing');
var addListener = process.addAsyncListener; var addListener = tracing.addAsyncListener;
var removeListener = process.removeAsyncListener; var removeListener = tracing.removeAsyncListener;
var errorMsgs = []; var errorMsgs = [];
var currentMsg = ''; var currentMsg = '';
var caught = 0; var caught = 0;
@ -46,7 +47,7 @@ var callbacksObj = {
} }
}; };
var listener = process.createAsyncListener(callbacksObj); var listener = tracing.createAsyncListener(callbacksObj);
process.on('exit', function(code) { process.on('exit', function(code) {
removeListener(listener); removeListener(listener);

7
test/simple/test-asynclistener-multi-timeout.js

@ -21,9 +21,10 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var addListener = process.addAsyncListener; var addListener = tracing.addAsyncListener;
var removeListener = process.removeAsyncListener; var removeListener = tracing.removeAsyncListener;
var caught = []; var caught = [];
var expect = []; var expect = [];
@ -35,7 +36,7 @@ var callbacksObj = {
} }
}; };
var listener = process.createAsyncListener(callbacksObj); var listener = tracing.createAsyncListener(callbacksObj);
process.on('exit', function(code) { process.on('exit', function(code) {
removeListener(listener); removeListener(listener);

9
test/simple/test-asynclistener-remove-add-in-before.js

@ -21,26 +21,27 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var val; var val;
var callbacks = { var callbacks = {
create: function() { create: function() {
return 42; return 42;
}, },
before: function() { before: function() {
process.removeAsyncListener(listener); tracing.removeAsyncListener(listener);
process.addAsyncListener(listener); tracing.addAsyncListener(listener);
}, },
after: function(context, storage) { after: function(context, storage) {
val = storage; val = storage;
} }
}; };
var listener = process.addAsyncListener(callbacks); var listener = tracing.addAsyncListener(callbacks);
process.nextTick(function() {}); process.nextTick(function() {});
process.on('exit', function(status) { process.on('exit', function(status) {
process.removeAsyncListener(listener); tracing.removeAsyncListener(listener);
assert.equal(status, 0); assert.equal(status, 0);
assert.equal(val, 42); assert.equal(val, 42);
}); });

5
test/simple/test-asynclistener-remove-before.js

@ -21,6 +21,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var set = 0; var set = 0;
var asyncNoHandleError = { var asyncNoHandleError = {
@ -32,9 +33,9 @@ var asyncNoHandleError = {
} }
} }
var key = process.addAsyncListener(asyncNoHandleError); var key = tracing.addAsyncListener(asyncNoHandleError);
process.removeAsyncListener(key); tracing.removeAsyncListener(key);
process.nextTick(function() { }); process.nextTick(function() { });

7
test/simple/test-asynclistener-remove-in-before.js

@ -21,22 +21,23 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var done = false; var done = false;
var callbacks = { var callbacks = {
before: function() { before: function() {
process.removeAsyncListener(listener); tracing.removeAsyncListener(listener);
}, },
after: function() { after: function() {
done = true; done = true;
} }
}; };
var listener = process.addAsyncListener(callbacks); var listener = tracing.addAsyncListener(callbacks);
process.nextTick(function() {}); process.nextTick(function() {});
process.on('exit', function(status) { process.on('exit', function(status) {
process.removeAsyncListener(listener); tracing.removeAsyncListener(listener);
assert.equal(status, 0); assert.equal(status, 0);
assert.ok(done); assert.ok(done);
}); });

5
test/simple/test-asynclistener-remove-inflight-error.js

@ -21,6 +21,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var set = 0; var set = 0;
var asyncNoHandleError = { var asyncNoHandleError = {
@ -29,13 +30,13 @@ var asyncNoHandleError = {
} }
} }
var key = process.addAsyncListener(asyncNoHandleError); var key = tracing.addAsyncListener(asyncNoHandleError);
process.nextTick(function() { process.nextTick(function() {
throw 1; throw 1;
}); });
process.removeAsyncListener(key); tracing.removeAsyncListener(key);
var uncaughtFired = false; var uncaughtFired = false;
process.on('uncaughtException', function() { process.on('uncaughtException', function() {

5
test/simple/test-asynclistener-remove-inflight.js

@ -21,6 +21,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var set = 0; var set = 0;
var asyncNoHandleError = { var asyncNoHandleError = {
@ -32,11 +33,11 @@ var asyncNoHandleError = {
} }
} }
var key = process.addAsyncListener(asyncNoHandleError); var key = tracing.addAsyncListener(asyncNoHandleError);
process.nextTick(function() { }); process.nextTick(function() { });
process.removeAsyncListener(key); tracing.removeAsyncListener(key);
process.on('exit', function(code) { process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that // If the exit code isn't ok then return early to throw the stack that

5
test/simple/test-asynclistener-run-error-once.js

@ -22,9 +22,10 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var net = require('net'); var net = require('net');
var tracing = require('tracing');
var cntr = 0; var cntr = 0;
var al = process.addAsyncListener({ var al = tracing.addAsyncListener({
error: function(stor, er) { error: function(stor, er) {
cntr++; cntr++;
process._rawDebug('Handling error: ' + er.message); process._rawDebug('Handling error: ' + er.message);
@ -33,7 +34,7 @@ var al = process.addAsyncListener({
}); });
process.on('exit', function(status) { process.on('exit', function(status) {
process.removeAsyncListener(al); tracing.removeAsyncListener(al);
assert.equal(status, 0); assert.equal(status, 0);
assert.equal(cntr, 1); assert.equal(cntr, 1);

9
test/simple/test-asynclistener-run-inst-once.js

@ -21,9 +21,10 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
var cntr = 0; var cntr = 0;
var al = process.createAsyncListener({ var al = tracing.createAsyncListener({
create: function() { cntr++; }, create: function() { cntr++; },
}); });
@ -32,12 +33,12 @@ process.on('exit', function() {
console.log('ok'); console.log('ok');
}); });
process.addAsyncListener(al); tracing.addAsyncListener(al);
process.nextTick(function() { process.nextTick(function() {
process.addAsyncListener(al); tracing.addAsyncListener(al);
process.nextTick(function() { process.nextTick(function() {
process.addAsyncListener(al); tracing.addAsyncListener(al);
process.nextTick(function() { process.nextTick(function() {
process.nextTick(function() { }); process.nextTick(function() { });
}); });

3
test/simple/test-asynclistener-throw-before-infinite-recursion.js

@ -21,6 +21,7 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var tracing = require('tracing');
// If there is an uncaughtException listener then the error thrown from // If there is an uncaughtException listener then the error thrown from
// "before" will be considered handled, thus calling setImmediate to // "before" will be considered handled, thus calling setImmediate to
@ -31,7 +32,7 @@ var assert = require('assert');
var cntr = 0; var cntr = 0;
process.addAsyncListener({ tracing.addAsyncListener({
before: function() { before: function() {
if (++cntr > 1) { if (++cntr > 1) {
// Can't throw since uncaughtException will also catch that. // Can't throw since uncaughtException will also catch that.

7
test/simple/test-asynclistener.js

@ -24,9 +24,10 @@ var assert = require('assert');
var net = require('net'); var net = require('net');
var fs = require('fs'); var fs = require('fs');
var dgram = require('dgram'); var dgram = require('dgram');
var tracing = require('tracing');
var addListener = process.addAsyncListener; var addListener = tracing.addAsyncListener;
var removeListener = process.removeAsyncListener; var removeListener = tracing.removeAsyncListener;
var actualAsync = 0; var actualAsync = 0;
var expectAsync = 0; var expectAsync = 0;
@ -36,7 +37,7 @@ var callbacks = {
} }
}; };
var listener = process.createAsyncListener(callbacks); var listener = tracing.createAsyncListener(callbacks);
process.on('exit', function() { process.on('exit', function() {
process._rawDebug('expected', expectAsync); process._rawDebug('expected', expectAsync);

Loading…
Cancel
Save