Browse Source

src: remove Async Listener

Async Listener was the name of the user-facing JS API, and is being
completely removed. Instead low level hooks directly into the mechanism
that AL used will be introduced in a future commit.

PR-URL: https://github.com/joyent/node/pull/8110
Signed-off-by: Trevor Norris <trev.norris@gmail.com>
Reviewed-by: Fedor Indutny <fedor@indutny.com>
Reviewed-by: Alexis Campailla <alexis@janeasystems.com>
Reviewed-by: Julien Gilli <julien.gilli@joyent.com>
archived-io.js-v0.12
Trevor Norris 10 years ago
committed by Bert Belder
parent
commit
0d60ab3efe
  1. 222
      doc/api/tracing.markdown
  2. 53
      lib/timers.js
  3. 333
      lib/tracing.js
  4. 50
      src/async-wrap-inl.h
  5. 8
      src/async-wrap.h
  6. 35
      src/env-inl.h
  7. 29
      src/env.h
  8. 63
      src/node.cc
  9. 41
      src/node.js
  10. 76
      test/simple/test-asynclistener-error-multiple-handled.js
  11. 65
      test/simple/test-asynclistener-error-multiple-mix.js
  12. 79
      test/simple/test-asynclistener-error-multiple-unhandled.js
  13. 108
      test/simple/test-asynclistener-error-net.js
  14. 62
      test/simple/test-asynclistener-error-throw-in-after.js
  15. 80
      test/simple/test-asynclistener-error-throw-in-before-multiple.js
  16. 64
      test/simple/test-asynclistener-error-throw-in-before.js
  17. 87
      test/simple/test-asynclistener-error-throw-in-error.js
  18. 257
      test/simple/test-asynclistener-error.js
  19. 70
      test/simple/test-asynclistener-multi-timeout.js
  20. 47
      test/simple/test-asynclistener-remove-add-in-before.js
  21. 53
      test/simple/test-asynclistener-remove-before.js
  22. 43
      test/simple/test-asynclistener-remove-in-before.js
  23. 58
      test/simple/test-asynclistener-remove-inflight-error.js
  24. 53
      test/simple/test-asynclistener-remove-inflight.js
  25. 59
      test/simple/test-asynclistener-run-error-once.js
  26. 46
      test/simple/test-asynclistener-run-inst-once.js
  27. 48
      test/simple/test-asynclistener-throw-before-infinite-recursion.js
  28. 188
      test/simple/test-asynclistener.js

222
doc/api/tracing.markdown

@ -5,16 +5,6 @@
The tracing module is designed for instrumenting your Node application. It is The tracing module is designed for instrumenting your Node application. It is
not meant for general purpose use. not meant for general purpose use.
***Be very careful with callbacks used in conjunction with this module***
Many of these callbacks interact directly with asynchronous subsystems in a
synchronous fashion. That is to say, you may be in a callback where a call to
`console.log()` could result in an infinite recursive loop. Also of note, many
of these callbacks are in hot execution code paths. That is to say your
callbacks are executed quite often in the normal operation of Node, so be wary
of doing CPU bound or synchronous workloads in these functions. Consider a ring
buffer and a timer to defer processing.
`require('tracing')` to use this module. `require('tracing')` to use this module.
## v8 ## v8
@ -74,215 +64,3 @@ setTimeout(function() { v8.setFlagsFromString('--notrace_gc'); }, 60e3);
``` ```
# 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
[Timers]: timers.html
[`tracing.createAsyncListener()`]: #tracing_tracing_createasynclistener_asynclistener_callbacksobj_storagevalue
[`tracing.addAsyncListener()`]: #tracing_tracing_addasynclistener_asynclistener
[`tracing.removeAsyncListener()`]: #tracing_tracing_removeasynclistener_asynclistener

53
lib/timers.js

@ -32,21 +32,6 @@ var TIMEOUT_MAX = 2147483647; // 2^31-1
var debug = require('util').debuglog('timer'); var debug = require('util').debuglog('timer');
var tracing = require('tracing');
var asyncFlags = tracing._asyncFlags;
var runAsyncQueue = tracing._runAsyncQueue;
var loadAsyncQueue = tracing._loadAsyncQueue;
var unloadAsyncQueue = tracing._unloadAsyncQueue;
// Same as in AsyncListener in env.h
var kHasListener = 0;
// Do a little housekeeping.
delete tracing._asyncFlags;
delete tracing._runAsyncQueue;
delete tracing._loadAsyncQueue;
delete tracing._unloadAsyncQueue;
// IDLE TIMEOUTS // IDLE TIMEOUTS
// //
@ -61,11 +46,6 @@ delete tracing._unloadAsyncQueue;
// value = list // value = list
var lists = {}; var lists = {};
// Make Timer as monomorphic as possible.
Timer.prototype._asyncQueue = undefined;
Timer.prototype._asyncData = undefined;
Timer.prototype._asyncFlags = 0;
// the main function - creates lists on demand and the watchers associated // the main function - creates lists on demand and the watchers associated
// with them. // with them.
function insert(item, msecs) { function insert(item, msecs) {
@ -102,7 +82,7 @@ function listOnTimeout() {
var now = Timer.now(); var now = Timer.now();
debug('now: %s', now); debug('now: %s', now);
var diff, first, hasQueue, threw; var diff, first, threw;
while (first = L.peek(list)) { while (first = L.peek(list)) {
diff = now - first._idleStart; diff = now - first._idleStart;
if (diff < msecs) { if (diff < msecs) {
@ -124,19 +104,13 @@ function listOnTimeout() {
if (domain && domain._disposed) if (domain && domain._disposed)
continue; continue;
hasQueue = !!first._asyncQueue;
try { try {
if (hasQueue)
loadAsyncQueue(first);
if (domain) if (domain)
domain.enter(); domain.enter();
threw = true; threw = true;
first._onTimeout(); first._onTimeout();
if (domain) if (domain)
domain.exit(); domain.exit();
if (hasQueue)
unloadAsyncQueue(first);
threw = false; threw = false;
} finally { } finally {
if (threw) { if (threw) {
@ -206,11 +180,6 @@ exports.active = function(item) {
L.append(list, item); L.append(list, item);
} }
} }
// Whether or not a new TimerWrap needed to be created, this should run
// for each item. This way each "item" (i.e. timer) can properly have
// their own domain assigned.
if (asyncFlags[kHasListener] > 0)
runAsyncQueue(item);
}; };
@ -356,18 +325,15 @@ L.init(immediateQueue);
function processImmediate() { function processImmediate() {
var queue = immediateQueue; var queue = immediateQueue;
var domain, hasQueue, immediate; var domain, immediate;
immediateQueue = {}; immediateQueue = {};
L.init(immediateQueue); L.init(immediateQueue);
while (L.isEmpty(queue) === false) { while (L.isEmpty(queue) === false) {
immediate = L.shift(queue); immediate = L.shift(queue);
hasQueue = !!immediate._asyncQueue;
domain = immediate.domain; domain = immediate.domain;
if (hasQueue)
loadAsyncQueue(immediate);
if (domain) if (domain)
domain.enter(); domain.enter();
@ -391,8 +357,6 @@ function processImmediate() {
if (domain) if (domain)
domain.exit(); domain.exit();
if (hasQueue)
unloadAsyncQueue(immediate);
} }
// Only round-trip to C++ land if we have to. Calling clearImmediate() on an // Only round-trip to C++ land if we have to. Calling clearImmediate() on an
@ -408,11 +372,8 @@ function Immediate() { }
Immediate.prototype.domain = undefined; Immediate.prototype.domain = undefined;
Immediate.prototype._onImmediate = undefined; Immediate.prototype._onImmediate = undefined;
Immediate.prototype._asyncQueue = undefined;
Immediate.prototype._asyncData = undefined;
Immediate.prototype._idleNext = undefined; Immediate.prototype._idleNext = undefined;
Immediate.prototype._idlePrev = undefined; Immediate.prototype._idlePrev = undefined;
Immediate.prototype._asyncFlags = 0;
exports.setImmediate = function(callback) { exports.setImmediate = function(callback) {
@ -438,9 +399,6 @@ exports.setImmediate = function(callback) {
process._immediateCallback = processImmediate; process._immediateCallback = processImmediate;
} }
// setImmediates are handled more like nextTicks.
if (asyncFlags[kHasListener] > 0)
runAsyncQueue(immediate);
if (process.domain) if (process.domain)
immediate.domain = process.domain; immediate.domain = process.domain;
@ -474,7 +432,7 @@ function unrefTimeout() {
debug('unrefTimer fired'); debug('unrefTimer fired');
var diff, domain, first, hasQueue, threw; var diff, domain, first, threw;
while (first = L.peek(unrefList)) { while (first = L.peek(unrefList)) {
diff = now - first._idleStart; diff = now - first._idleStart;
@ -492,11 +450,8 @@ function unrefTimeout() {
if (!first._onTimeout) continue; if (!first._onTimeout) continue;
if (domain && domain._disposed) continue; if (domain && domain._disposed) continue;
hasQueue = !!first._asyncQueue;
try { try {
if (hasQueue)
loadAsyncQueue(first);
if (domain) domain.enter(); if (domain) domain.enter();
threw = true; threw = true;
debug('unreftimer firing timeout'); debug('unreftimer firing timeout');
@ -504,8 +459,6 @@ function unrefTimeout() {
threw = false; threw = false;
if (domain) if (domain)
domain.exit(); domain.exit();
if (hasQueue)
unloadAsyncQueue(first);
} finally { } finally {
if (threw) process.nextTick(unrefTimeout); if (threw) process.nextTick(unrefTimeout);
} }

333
lib/tracing.js

@ -35,13 +35,6 @@ exports._nodeInitialization = function nodeInitialization(pobj) {
v8.getHeapStatistics = v8binding.getHeapStatistics; v8.getHeapStatistics = v8binding.getHeapStatistics;
v8.setFlagsFromString = v8binding.setFlagsFromString; v8.setFlagsFromString = v8binding.setFlagsFromString;
// Part of the AsyncListener setup to share objects/callbacks with the
// native layer.
process._setupAsyncListener(asyncFlags,
runAsyncQueue,
loadAsyncQueue,
unloadAsyncQueue);
// Do a little housekeeping. // Do a little housekeeping.
delete exports._nodeInitialization; delete exports._nodeInitialization;
}; };
@ -70,329 +63,3 @@ v8.on('removeListener', function(name) {
} }
}); });
// 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;
// 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, 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;
}

50
src/async-wrap-inl.h

@ -37,20 +37,7 @@ inline AsyncWrap::AsyncWrap(Environment* env,
v8::Handle<v8::Object> object, v8::Handle<v8::Object> object,
ProviderType provider) ProviderType provider)
: BaseObject(env, object), : BaseObject(env, object),
async_flags_(NO_OPTIONS),
provider_type_(provider) { provider_type_(provider) {
if (!env->has_async_listener())
return;
// TODO(trevnorris): Do we really need to TryCatch this call?
v8::TryCatch try_catch;
try_catch.SetVerbose(true);
v8::Local<v8::Value> val = object.As<v8::Value>();
env->async_listener_run_function()->Call(env->process_object(), 1, &val);
if (!try_catch.HasCaught())
async_flags_ |= HAS_ASYNC_LISTENER;
} }
@ -59,11 +46,6 @@ inline uint32_t AsyncWrap::provider_type() const {
} }
inline bool AsyncWrap::has_async_listener() {
return async_flags_ & HAS_ASYNC_LISTENER;
}
// I hate you domains. // I hate you domains.
inline v8::Handle<v8::Value> AsyncWrap::MakeDomainCallback( inline v8::Handle<v8::Value> AsyncWrap::MakeDomainCallback(
const v8::Handle<v8::Function> cb, const v8::Handle<v8::Function> cb,
@ -79,14 +61,6 @@ inline v8::Handle<v8::Value> AsyncWrap::MakeDomainCallback(
v8::TryCatch try_catch; v8::TryCatch try_catch;
try_catch.SetVerbose(true); try_catch.SetVerbose(true);
if (has_async_listener()) {
v8::Local<v8::Value> val = context.As<v8::Value>();
env()->async_listener_load_function()->Call(process, 1, &val);
if (try_catch.HasCaught())
return v8::Undefined(env()->isolate());
}
bool has_domain = domain_v->IsObject(); bool has_domain = domain_v->IsObject();
if (has_domain) { if (has_domain) {
domain = domain_v.As<v8::Object>(); domain = domain_v.As<v8::Object>();
@ -119,14 +93,6 @@ inline v8::Handle<v8::Value> AsyncWrap::MakeDomainCallback(
} }
} }
if (has_async_listener()) {
v8::Local<v8::Value> val = context.As<v8::Value>();
env()->async_listener_unload_function()->Call(process, 1, &val);
if (try_catch.HasCaught())
return Undefined(env()->isolate());
}
Environment::TickInfo* tick_info = env()->tick_info(); Environment::TickInfo* tick_info = env()->tick_info();
if (tick_info->in_tick()) { if (tick_info->in_tick()) {
@ -172,28 +138,12 @@ inline v8::Handle<v8::Value> AsyncWrap::MakeCallback(
v8::TryCatch try_catch; v8::TryCatch try_catch;
try_catch.SetVerbose(true); try_catch.SetVerbose(true);
if (has_async_listener()) {
v8::Local<v8::Value> val = context.As<v8::Value>();
env()->async_listener_load_function()->Call(process, 1, &val);
if (try_catch.HasCaught())
return v8::Undefined(env()->isolate());
}
v8::Local<v8::Value> ret = cb->Call(context, argc, argv); v8::Local<v8::Value> ret = cb->Call(context, argc, argv);
if (try_catch.HasCaught()) { if (try_catch.HasCaught()) {
return Undefined(env()->isolate()); return Undefined(env()->isolate());
} }
if (has_async_listener()) {
v8::Local<v8::Value> val = context.As<v8::Value>();
env()->async_listener_unload_function()->Call(process, 1, &val);
if (try_catch.HasCaught())
return v8::Undefined(env()->isolate());
}
Environment::TickInfo* tick_info = env()->tick_info(); Environment::TickInfo* tick_info = env()->tick_info();
if (tick_info->in_tick()) { if (tick_info->in_tick()) {

8
src/async-wrap.h

@ -30,11 +30,6 @@ namespace node {
class AsyncWrap : public BaseObject { class AsyncWrap : public BaseObject {
public: public:
enum AsyncFlags {
NO_OPTIONS = 0,
HAS_ASYNC_LISTENER = 1
};
enum ProviderType { enum ProviderType {
PROVIDER_NONE = 1 << 0, PROVIDER_NONE = 1 << 0,
PROVIDER_CARES = 1 << 1, PROVIDER_CARES = 1 << 1,
@ -63,8 +58,6 @@ class AsyncWrap : public BaseObject {
inline virtual ~AsyncWrap() override = default; inline virtual ~AsyncWrap() override = default;
inline bool has_async_listener();
inline uint32_t provider_type() const; inline uint32_t provider_type() const;
// Only call these within a valid HandleScope. // Only call these within a valid HandleScope.
@ -88,7 +81,6 @@ class AsyncWrap : public BaseObject {
int argc, int argc,
v8::Handle<v8::Value>* argv); v8::Handle<v8::Value>* argv);
uint32_t async_flags_;
uint32_t provider_type_; uint32_t provider_type_;
}; };

35
src/env-inl.h

@ -111,27 +111,6 @@ inline v8::Isolate* Environment::IsolateData::isolate() const {
return isolate_; return isolate_;
} }
inline Environment::AsyncListener::AsyncListener() {
for (int i = 0; i < kFieldsCount; ++i)
fields_[i] = 0;
}
inline uint32_t* Environment::AsyncListener::fields() {
return fields_;
}
inline int Environment::AsyncListener::fields_count() const {
return kFieldsCount;
}
inline bool Environment::AsyncListener::has_listener() const {
return fields_[kHasListener] > 0;
}
inline uint32_t Environment::AsyncListener::watched_providers() const {
return fields_[kWatchedProviders];
}
inline Environment::DomainFlag::DomainFlag() { inline Environment::DomainFlag::DomainFlag() {
for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0; for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0;
} }
@ -277,16 +256,6 @@ inline v8::Isolate* Environment::isolate() const {
return isolate_; return isolate_;
} }
inline bool Environment::has_async_listener() const {
// The const_cast is okay, it doesn't violate conceptual const-ness.
return const_cast<Environment*>(this)->async_listener()->has_listener();
}
inline uint32_t Environment::watched_providers() const {
// The const_cast is okay, it doesn't violate conceptual const-ness.
return const_cast<Environment*>(this)->async_listener()->watched_providers();
}
inline bool Environment::in_domain() const { inline bool Environment::in_domain() const {
// The const_cast is okay, it doesn't violate conceptual const-ness. // The const_cast is okay, it doesn't violate conceptual const-ness.
return using_domains() && return using_domains() &&
@ -338,10 +307,6 @@ inline uv_loop_t* Environment::event_loop() const {
return isolate_data()->event_loop(); return isolate_data()->event_loop();
} }
inline Environment::AsyncListener* Environment::async_listener() {
return &async_listener_count_;
}
inline Environment::DomainFlag* Environment::domain_flag() { inline Environment::DomainFlag* Environment::domain_flag() {
return &domain_flag_; return &domain_flag_;
} }

29
src/env.h

@ -64,7 +64,6 @@ namespace node {
V(address_string, "address") \ V(address_string, "address") \
V(args_string, "args") \ V(args_string, "args") \
V(argv_string, "argv") \ V(argv_string, "argv") \
V(async_queue_string, "_asyncQueue") \
V(async, "async") \ V(async, "async") \
V(atime_string, "atime") \ V(atime_string, "atime") \
V(birthtime_string, "birthtime") \ V(birthtime_string, "birthtime") \
@ -250,9 +249,6 @@ namespace node {
V(zero_return_string, "ZERO_RETURN") \ V(zero_return_string, "ZERO_RETURN") \
#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \ #define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \
V(async_listener_run_function, v8::Function) \
V(async_listener_load_function, v8::Function) \
V(async_listener_unload_function, v8::Function) \
V(binding_cache_object, v8::Object) \ V(binding_cache_object, v8::Object) \
V(buffer_constructor_function, v8::Function) \ V(buffer_constructor_function, v8::Function) \
V(context, v8::Context) \ V(context, v8::Context) \
@ -286,28 +282,6 @@ RB_HEAD(ares_task_list, ares_task_t);
class Environment { class Environment {
public: public:
class AsyncListener {
public:
inline uint32_t* fields();
inline int fields_count() const;
inline bool has_listener() const;
inline uint32_t watched_providers() const;
private:
friend class Environment; // So we can call the constructor.
inline AsyncListener();
enum Fields {
kHasListener,
kWatchedProviders,
kFieldsCount
};
uint32_t fields_[kFieldsCount];
DISALLOW_COPY_AND_ASSIGN(AsyncListener);
};
class DomainFlag { class DomainFlag {
public: public:
inline uint32_t* fields(); inline uint32_t* fields();
@ -399,7 +373,6 @@ class Environment {
inline v8::Isolate* isolate() const; inline v8::Isolate* isolate() const;
inline uv_loop_t* event_loop() const; inline uv_loop_t* event_loop() const;
inline bool has_async_listener() const;
inline bool in_domain() const; inline bool in_domain() const;
inline uint32_t watched_providers() const; inline uint32_t watched_providers() const;
@ -419,7 +392,6 @@ class Environment {
void *arg); void *arg);
inline void FinishHandleCleanup(uv_handle_t* handle); inline void FinishHandleCleanup(uv_handle_t* handle);
inline AsyncListener* async_listener();
inline DomainFlag* domain_flag(); inline DomainFlag* domain_flag();
inline TickInfo* tick_info(); inline TickInfo* tick_info();
@ -513,7 +485,6 @@ class Environment {
uv_idle_t immediate_idle_handle_; uv_idle_t immediate_idle_handle_;
uv_prepare_t idle_prepare_handle_; uv_prepare_t idle_prepare_handle_;
uv_check_t idle_check_handle_; uv_check_t idle_check_handle_;
AsyncListener async_listener_count_;
DomainFlag domain_flag_; DomainFlag domain_flag_;
TickInfo tick_info_; TickInfo tick_info_;
uv_timer_t cares_timer_handle_; uv_timer_t cares_timer_handle_;

63
src/node.cc

@ -918,31 +918,6 @@ Local<Value> WinapiErrnoException(Isolate* isolate,
#endif #endif
void SetupAsyncListener(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK(args[0]->IsObject());
CHECK(args[1]->IsFunction());
CHECK(args[2]->IsFunction());
CHECK(args[3]->IsFunction());
env->set_async_listener_run_function(args[1].As<Function>());
env->set_async_listener_load_function(args[2].As<Function>());
env->set_async_listener_unload_function(args[3].As<Function>());
Local<Object> async_listener_flag_obj = args[0].As<Object>();
Environment::AsyncListener* async_listener = env->async_listener();
async_listener_flag_obj->SetIndexedPropertiesToExternalArrayData(
async_listener->fields(),
kExternalUint32Array,
async_listener->fields_count());
// Do a little housekeeping.
env->process_object()->Delete(
FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupAsyncListener"));
}
void SetupDomainUse(const FunctionCallbackInfo<Value>& args) { void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
@ -1026,20 +1001,6 @@ Handle<Value> MakeDomainCallback(Environment* env,
TryCatch try_catch; TryCatch try_catch;
try_catch.SetVerbose(true); try_catch.SetVerbose(true);
bool has_async_queue = false;
if (recv->IsObject()) {
object = recv.As<Object>();
// TODO(trevnorris): This is sucky for performance. Fix it.
has_async_queue = object->Has(env->async_queue_string());
if (has_async_queue) {
env->async_listener_load_function()->Call(process, 1, &recv);
if (try_catch.HasCaught())
return Undefined(env->isolate());
}
}
bool has_domain = false; bool has_domain = false;
if (!object.IsEmpty()) { if (!object.IsEmpty()) {
@ -1077,13 +1038,6 @@ Handle<Value> MakeDomainCallback(Environment* env,
} }
} }
if (has_async_queue) {
env->async_listener_unload_function()->Call(process, 1, &recv);
if (try_catch.HasCaught())
return Undefined(env->isolate());
}
Environment::TickInfo* tick_info = env->tick_info(); Environment::TickInfo* tick_info = env->tick_info();
if (tick_info->last_threw() == 1) { if (tick_info->last_threw() == 1) {
@ -1135,28 +1089,12 @@ Handle<Value> MakeCallback(Environment* env,
TryCatch try_catch; TryCatch try_catch;
try_catch.SetVerbose(true); try_catch.SetVerbose(true);
// TODO(trevnorris): This is sucky for performance. Fix it.
bool has_async_queue =
recv->IsObject() && recv.As<Object>()->Has(env->async_queue_string());
if (has_async_queue) {
env->async_listener_load_function()->Call(process, 1, &recv);
if (try_catch.HasCaught())
return Undefined(env->isolate());
}
Local<Value> ret = callback->Call(recv, argc, argv); Local<Value> ret = callback->Call(recv, argc, argv);
if (try_catch.HasCaught()) { if (try_catch.HasCaught()) {
return Undefined(env->isolate()); return Undefined(env->isolate());
} }
if (has_async_queue) {
env->async_listener_unload_function()->Call(process, 1, &recv);
if (try_catch.HasCaught())
return Undefined(env->isolate());
}
Environment::TickInfo* tick_info = env->tick_info(); Environment::TickInfo* tick_info = env->tick_info();
if (tick_info->in_tick()) { if (tick_info->in_tick()) {
@ -2865,7 +2803,6 @@ void SetupProcessObject(Environment* env,
env->SetMethod(process, "binding", Binding); env->SetMethod(process, "binding", Binding);
env->SetMethod(process, "_linkedBinding", LinkedBinding); env->SetMethod(process, "_linkedBinding", LinkedBinding);
env->SetMethod(process, "_setupAsyncListener", SetupAsyncListener);
env->SetMethod(process, "_setupNextTick", SetupNextTick); env->SetMethod(process, "_setupNextTick", SetupNextTick);
env->SetMethod(process, "_setupDomainUse", SetupDomainUse); env->SetMethod(process, "_setupDomainUse", SetupDomainUse);

41
src/node.js

@ -232,14 +232,8 @@
}; };
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. var caught;
var caught = _errorHandler(er);
if (process.domain && process.domain._errorHandler) if (process.domain && process.domain._errorHandler)
caught = process.domain._errorHandler(er) || caught; caught = process.domain._errorHandler(er) || caught;
@ -262,10 +256,6 @@
// if we handled an error, then make sure any ticks get processed // if we handled an error, then make sure any ticks get processed
} else { } else {
var t = setImmediate(process._tickCallback); var t = setImmediate(process._tickCallback);
// Complete hack to make sure any errors thrown from async
// listeners don't cause an infinite loop.
if (t._asyncQueue)
t._asyncQueue = [];
} }
return caught; return caught;
@ -299,12 +289,7 @@
}; };
startup.processNextTick = function() { startup.processNextTick = function() {
var tracing = NativeModule.require('tracing');
var nextTickQueue = []; var nextTickQueue = [];
var asyncFlags = tracing._asyncFlags;
var _runAsyncQueue = tracing._runAsyncQueue;
var _loadAsyncQueue = tracing._loadAsyncQueue;
var _unloadAsyncQueue = tracing._unloadAsyncQueue;
var microtasksScheduled = false; var microtasksScheduled = false;
// Used to run V8's micro task queue. // Used to run V8's micro task queue.
@ -318,10 +303,6 @@
var kIndex = 0; var kIndex = 0;
var kLength = 1; var kLength = 1;
// For asyncFlags.
// *Must* match Environment::AsyncListeners::Fields in src/env.h
var kCount = 0;
process.nextTick = nextTick; process.nextTick = nextTick;
// Needs to be accessible from beyond this scope. // Needs to be accessible from beyond this scope.
process._tickCallback = _tickCallback; process._tickCallback = _tickCallback;
@ -368,7 +349,7 @@
// Run callbacks that have no domain. // Run callbacks that have no domain.
// Using domains will cause this to be overridden. // Using domains will cause this to be overridden.
function _tickCallback() { function _tickCallback() {
var callback, hasQueue, threw, tock; var callback, threw, tock;
scheduleMicrotasks(); scheduleMicrotasks();
@ -376,9 +357,6 @@
tock = nextTickQueue[tickInfo[kIndex]++]; tock = nextTickQueue[tickInfo[kIndex]++];
callback = tock.callback; callback = tock.callback;
threw = true; threw = true;
hasQueue = !!tock._asyncQueue;
if (hasQueue)
_loadAsyncQueue(tock);
try { try {
callback(); callback();
threw = false; threw = false;
@ -386,8 +364,6 @@
if (threw) if (threw)
tickDone(); tickDone();
} }
if (hasQueue)
_unloadAsyncQueue(tock);
if (1e4 < tickInfo[kIndex]) if (1e4 < tickInfo[kIndex])
tickDone(); tickDone();
} }
@ -396,7 +372,7 @@
} }
function _tickDomainCallback() { function _tickDomainCallback() {
var callback, domain, hasQueue, threw, tock; var callback, domain, threw, tock;
scheduleMicrotasks(); scheduleMicrotasks();
@ -404,9 +380,6 @@
tock = nextTickQueue[tickInfo[kIndex]++]; tock = nextTickQueue[tickInfo[kIndex]++];
callback = tock.callback; callback = tock.callback;
domain = tock.domain; domain = tock.domain;
hasQueue = !!tock._asyncQueue;
if (hasQueue)
_loadAsyncQueue(tock);
if (domain) if (domain)
domain.enter(); domain.enter();
threw = true; threw = true;
@ -417,8 +390,6 @@
if (threw) if (threw)
tickDone(); tickDone();
} }
if (hasQueue)
_unloadAsyncQueue(tock);
if (1e4 < tickInfo[kIndex]) if (1e4 < tickInfo[kIndex])
tickDone(); tickDone();
if (domain) if (domain)
@ -435,13 +406,9 @@
var obj = { var obj = {
callback: callback, callback: callback,
domain: process.domain || null, domain: process.domain || null
_asyncQueue: undefined
}; };
if (asyncFlags[kCount] > 0)
_runAsyncQueue(obj);
nextTickQueue.push(obj); nextTickQueue.push(obj);
tickInfo[kLength]++; tickInfo[kLength]++;
} }

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

@ -1,76 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var active = null;
var cntr = 0;
function onAsync0() {
return 0;
}
function onAsync1() {
return 1;
}
function onError(stor) {
results.push(stor);
return true;
}
var results = [];
var asyncNoHandleError0 = {
create: onAsync0,
error: onError
};
var asyncNoHandleError1 = {
create: onAsync1,
error: onError
};
var listeners = [
tracing.addAsyncListener(asyncNoHandleError0),
tracing.addAsyncListener(asyncNoHandleError1)
];
process.nextTick(function() {
throw new Error();
});
tracing.removeAsyncListener(listeners[0]);
tracing.removeAsyncListener(listeners[1]);
process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that
// caused the bad return code.
if (code !== 0)
return;
// Handling of errors should propagate to all listeners.
assert.equal(results[0], 0);
assert.equal(results[1], 1);
assert.equal(results.length, 2);
console.log('ok');
});

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

@ -1,65 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var results = [];
var asyncNoHandleError = {
error: function(stor) {
results.push(1);
}
};
var asyncHandleError = {
error: function(stor) {
results.push(0);
return true;
}
};
var listeners = [
tracing.addAsyncListener(asyncHandleError),
tracing.addAsyncListener(asyncNoHandleError)
];
// Even if an error handler returns true, both should fire.
process.nextTick(function() {
throw new Error();
});
tracing.removeAsyncListener(listeners[0]);
tracing.removeAsyncListener(listeners[1]);
process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that
// caused the bad return code.
if (code !== 0)
return;
// Mixed handling of errors should propagate to all listeners.
assert.equal(results[0], 0);
assert.equal(results[1], 1);
assert.equal(results.length, 2);
console.log('ok');
});

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

@ -1,79 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
function onAsync0() {
return 0;
}
function onAsync1() {
return 1;
}
function onError(stor) {
results.push(stor);
}
var results = [];
var asyncNoHandleError0 = {
create: onAsync0,
error: onError
};
var asyncNoHandleError1 = {
create: onAsync1,
error: onError
};
var listeners = [
tracing.addAsyncListener(asyncNoHandleError0),
tracing.addAsyncListener(asyncNoHandleError1)
];
var uncaughtFired = false;
process.on('uncaughtException', function() {
uncaughtFired = true;
// Unhandled errors should propagate to all listeners.
assert.equal(results[0], 0);
assert.equal(results[1], 1);
assert.equal(results.length, 2);
});
process.nextTick(function() {
throw new Error();
});
process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that
// caused the bad return code.
if (code !== 0)
return;
// Need to remove the async listeners or tests will always pass
for (var i = 0; i < listeners.length; i++)
tracing.removeAsyncListener(listeners[i]);
assert.ok(uncaughtFired);
console.log('ok');
});

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

@ -1,108 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var dns = require('dns');
var fs = require('fs');
var net = require('net');
var tracing = require('tracing');
var errorMsgs = [];
var caught = 0;
var expectCaught = 0;
var callbacksObj = {
error: function(value, er) {
var idx = errorMsgs.indexOf(er.message);
caught++;
process._rawDebug('Handling error: ' + er.message);
if (-1 < idx)
errorMsgs.splice(idx, 1);
else
throw new Error('Message not found: ' + er.message);
return true;
}
};
var listener = tracing.addAsyncListener(callbacksObj);
process.on('exit', function(code) {
tracing.removeAsyncListener(listener);
if (code > 0)
return;
if (errorMsgs.length > 0)
throw new Error('Errors not fired: ' + errorMsgs);
assert.equal(caught, expectCaught);
process._rawDebug('ok');
});
// Net
var iter = 3;
for (var i = 0; i < iter; i++) {
errorMsgs.push('net - error: server connection');
errorMsgs.push('net - error: client data');
errorMsgs.push('net - error: server data');
}
errorMsgs.push('net - error: server closed');
var server = net.createServer(function(c) {
c.on('data', function() {
if (0 === --iter) {
server.close(function() {
process._rawDebug('net - server closing');
throw new Error('net - error: server closed');
});
expectCaught++;
}
process._rawDebug('net - connection received data');
throw new Error('net - error: server data');
});
expectCaught++;
c.end('bye');
process._rawDebug('net - connection received');
throw new Error('net - error: server connection');
});
expectCaught += iter;
server.listen(common.PORT, function() {
for (var i = 0; i < iter; i++)
clientConnect();
});
function clientConnect() {
var client = net.connect(common.PORT, function() { });
client.on('data', function() {
client.end('see ya');
process._rawDebug('net - client received data');
throw new Error('net - error: client data');
});
expectCaught++;
}

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

@ -1,62 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var once = 0;
var results = [];
var handlers = {
after: function() {
throw 1;
},
error: function(stor, err) {
// Error handler must be called exactly *once*.
once++;
assert.equal(err, 1);
return true;
}
}
var key = tracing.addAsyncListener(handlers);
var uncaughtFired = false;
process.on('uncaughtException', function(err) {
uncaughtFired = true;
assert.equal(once, 1);
});
process.nextTick(function() { });
tracing.removeAsyncListener(key);
process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that
// caused the bad return code.
if (code !== 0)
return;
assert.ok(uncaughtFired);
console.log('ok');
});

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

@ -1,80 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var once = 0;
var results = [];
var handlers = {
before: function() {
throw 1;
},
error: function(stor, err) {
// Must catch error thrown in before callback.
assert.equal(err, 1);
once++;
return true;
}
}
var handlers1 = {
before: function() {
throw 2;
},
error: function(stor, err) {
// Must catch *other* handlers throw by error callback.
assert.equal(err, 1);
once++;
return true;
}
}
var listeners = [
tracing.addAsyncListener(handlers),
tracing.addAsyncListener(handlers1)
];
var uncaughtFired = false;
process.on('uncaughtException', function(err) {
uncaughtFired = true;
// Both error handlers must fire.
assert.equal(once, 2);
});
process.nextTick(function() { });
for (var i = 0; i < listeners.length; i++)
tracing.removeAsyncListener(listeners[i]);
process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that
// caused the bad return code.
if (code !== 0)
return;
// Make sure uncaughtException actually fired.
assert.ok(uncaughtFired);
console.log('ok');
});

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

@ -1,64 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var once = 0;
var results = [];
var handlers = {
before: function() {
throw 1;
},
error: function(stor, err) {
// Error handler must be called exactly *once*.
once++;
assert.equal(err, 1);
return true;
}
}
var key = tracing.addAsyncListener(handlers);
var uncaughtFired = false;
process.on('uncaughtException', function(err) {
uncaughtFired = true;
// Process should propagate error regardless of handlers return value.
assert.equal(once, 1);
});
process.nextTick(function() { });
tracing.removeAsyncListener(key);
process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that
// caused the bad return code.
if (code !== 0)
return;
// Make sure that the uncaughtException actually fired.
assert.ok(uncaughtFired);
console.log('ok');
});

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

@ -1,87 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var spawn = require('child_process').spawn;
var tracing = require('tracing');
var checkStr = 'WRITTEN ON EXIT';
if (process.argv[2] === 'child')
runChild();
else
runParent();
function runChild() {
var cntr = 0;
var key = tracing.addAsyncListener({
error: function onError() {
cntr++;
throw new Error('onError');
}
});
process.on('unhandledException', function() {
// Throwing in 'error' should bypass unhandledException.
process.exit(2);
});
process.on('exit', function() {
// Make sure that we can still write out to stderr even when the
// process dies.
process._rawDebug(checkStr);
});
process.nextTick(function() {
throw new Error('nextTick');
});
}
function runParent() {
var childDidExit = false;
var childStr = '';
var child = spawn(process.execPath, [__filename, 'child']);
child.stderr.on('data', function(chunk) {
process._rawDebug('received data from child');
childStr += chunk.toString();
});
child.on('exit', function(code) {
process._rawDebug('child process exiting');
childDidExit = true;
// This is thrown when Node throws from _fatalException.
assert.equal(code, 7);
});
process.on('exit', function() {
process._rawDebug('child ondata message:',
childStr.substr(0, checkStr.length));
assert.ok(childDidExit);
assert.equal(childStr.substr(0, checkStr.length), checkStr);
console.log('ok');
});
}

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

@ -1,257 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var dns = require('dns');
var fs = require('fs');
var net = require('net');
var tracing = require('tracing');
var addListener = tracing.addAsyncListener;
var removeListener = tracing.removeAsyncListener;
var errorMsgs = [];
var currentMsg = '';
var caught = 0;
var expectCaught = 0;
var exitCbRan = false;
var callbacksObj = {
error: function(value, er) {
var idx = errorMsgs.indexOf(er.message);
caught++;
if (-1 < idx)
errorMsgs.splice(idx, 1);
return currentMsg === er.message;
}
};
var listener = tracing.createAsyncListener(callbacksObj);
process.on('exit', function(code) {
removeListener(listener);
// Something else went wrong, no need to further check.
if (code > 0)
return;
// Make sure the exit callback only runs once.
assert.ok(!exitCbRan);
exitCbRan = true;
// Check if any error messages weren't removed from the msg queue.
if (errorMsgs.length > 0)
throw new Error('Errors not fired: ' + errorMsgs);
assert.equal(caught, expectCaught, 'caught all expected errors');
process._rawDebug('ok');
});
// Catch synchronous throws
errorMsgs.push('sync throw');
process.nextTick(function() {
addListener(listener);
expectCaught++;
currentMsg = 'sync throw';
throw new Error(currentMsg);
removeListener(listener);
});
// Simple cases
errorMsgs.push('setTimeout - simple');
errorMsgs.push('setImmediate - simple');
errorMsgs.push('setInterval - simple');
errorMsgs.push('process.nextTick - simple');
process.nextTick(function() {
addListener(listener);
setTimeout(function() {
currentMsg = 'setTimeout - simple';
throw new Error(currentMsg);
});
expectCaught++;
setImmediate(function() {
currentMsg = 'setImmediate - simple';
throw new Error(currentMsg);
});
expectCaught++;
var b = setInterval(function() {
clearInterval(b);
currentMsg = 'setInterval - simple';
throw new Error(currentMsg);
});
expectCaught++;
process.nextTick(function() {
currentMsg = 'process.nextTick - simple';
throw new Error(currentMsg);
});
expectCaught++;
removeListener(listener);
});
// Deeply nested
errorMsgs.push('setInterval - nested');
errorMsgs.push('setImmediate - nested');
errorMsgs.push('process.nextTick - nested');
errorMsgs.push('setTimeout2 - nested');
errorMsgs.push('setTimeout - nested');
process.nextTick(function() {
addListener(listener);
setTimeout(function() {
process.nextTick(function() {
setImmediate(function() {
var b = setInterval(function() {
clearInterval(b);
currentMsg = 'setInterval - nested';
throw new Error(currentMsg);
});
expectCaught++;
currentMsg = 'setImmediate - nested';
throw new Error(currentMsg);
});
expectCaught++;
currentMsg = 'process.nextTick - nested';
throw new Error(currentMsg);
});
expectCaught++;
setTimeout(function() {
currentMsg = 'setTimeout2 - nested';
throw new Error(currentMsg);
});
expectCaught++;
currentMsg = 'setTimeout - nested';
throw new Error(currentMsg);
});
expectCaught++;
removeListener(listener);
});
// FS
errorMsgs.push('fs - file does not exist');
errorMsgs.push('fs - exists');
errorMsgs.push('fs - realpath');
process.nextTick(function() {
addListener(listener);
fs.stat('does not exist', function(err, stats) {
currentMsg = 'fs - file does not exist';
throw new Error(currentMsg);
});
expectCaught++;
fs.exists('hi all', function(exists) {
currentMsg = 'fs - exists';
throw new Error(currentMsg);
});
expectCaught++;
fs.realpath('/some/path', function(err, resolved) {
currentMsg = 'fs - realpath';
throw new Error(currentMsg);
});
expectCaught++;
removeListener(listener);
});
// Nested FS
errorMsgs.push('fs - nested file does not exist');
process.nextTick(function() {
addListener(listener);
setTimeout(function() {
setImmediate(function() {
var b = setInterval(function() {
clearInterval(b);
process.nextTick(function() {
fs.stat('does not exist', function(err, stats) {
currentMsg = 'fs - nested file does not exist';
throw new Error(currentMsg);
});
expectCaught++;
});
});
});
});
removeListener(listener);
});
// Net
errorMsgs.push('net - connection listener');
errorMsgs.push('net - client connect');
errorMsgs.push('net - server listening');
process.nextTick(function() {
addListener(listener);
var server = net.createServer(function(c) {
server.close();
currentMsg = 'net - connection listener';
throw new Error(currentMsg);
});
expectCaught++;
server.listen(common.PORT, function() {
var client = net.connect(common.PORT, function() {
client.end();
currentMsg = 'net - client connect';
throw new Error(currentMsg);
});
expectCaught++;
currentMsg = 'net - server listening';
throw new Error(currentMsg);
});
expectCaught++;
removeListener(listener);
});
// DNS
errorMsgs.push('dns - lookup');
process.nextTick(function() {
addListener(listener);
dns.lookup('localhost', function() {
currentMsg = 'dns - lookup';
throw new Error(currentMsg);
});
expectCaught++;
removeListener(listener);
});

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

@ -1,70 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var addListener = tracing.addAsyncListener;
var removeListener = tracing.removeAsyncListener;
var caught = [];
var expect = [];
var callbacksObj = {
error: function(value, er) {
process._rawDebug('caught', er.message);
caught.push(er.message);
return (expect.indexOf(er.message) !== -1);
}
};
var listener = tracing.createAsyncListener(callbacksObj);
process.on('exit', function(code) {
removeListener(listener);
if (code > 0)
return;
expect = expect.sort();
caught = caught.sort();
process._rawDebug('expect', expect);
process._rawDebug('caught', caught);
assert.deepEqual(caught, expect, 'caught all expected errors');
process._rawDebug('ok');
});
expect.push('immediate simple a');
expect.push('immediate simple b');
process.nextTick(function() {
addListener(listener);
// Tests for a setImmediate specific bug encountered while implementing
// AsyncListeners.
setImmediate(function() {
throw new Error('immediate simple a');
});
setImmediate(function() {
throw new Error('immediate simple b');
});
removeListener(listener);
});

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

@ -1,47 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var val;
var callbacks = {
create: function() {
return 42;
},
before: function() {
tracing.removeAsyncListener(listener);
tracing.addAsyncListener(listener);
},
after: function(context, storage) {
val = storage;
}
};
var listener = tracing.addAsyncListener(callbacks);
process.nextTick(function() {});
process.on('exit', function(status) {
tracing.removeAsyncListener(listener);
assert.equal(status, 0);
assert.equal(val, 42);
});

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

@ -1,53 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var set = 0;
var asyncNoHandleError = {
before: function() {
set++;
},
after: function() {
set++;
}
}
var key = tracing.addAsyncListener(asyncNoHandleError);
tracing.removeAsyncListener(key);
process.nextTick(function() { });
process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that
// caused the bad return code.
if (code !== 0)
return;
// The async handler should never be called.
assert.equal(set, 0);
console.log('ok');
});

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

@ -1,43 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var done = false;
var callbacks = {
before: function() {
tracing.removeAsyncListener(listener);
},
after: function() {
done = true;
}
};
var listener = tracing.addAsyncListener(callbacks);
process.nextTick(function() {});
process.on('exit', function(status) {
tracing.removeAsyncListener(listener);
assert.equal(status, 0);
assert.ok(done);
});

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

@ -1,58 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var set = 0;
var asyncNoHandleError = {
error: function() {
set++;
}
}
var key = tracing.addAsyncListener(asyncNoHandleError);
process.nextTick(function() {
throw 1;
});
tracing.removeAsyncListener(key);
var uncaughtFired = false;
process.on('uncaughtException', function() {
uncaughtFired = true;
// Throwing should call the error handler once, then propagate to
// uncaughtException
assert.equal(set, 1);
});
process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that
// caused the bad return code.
if (code !== 0)
return;
assert.ok(uncaughtFired);
console.log('ok');
});

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

@ -1,53 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var set = 0;
var asyncNoHandleError = {
before: function() {
set++;
},
after: function() {
set++;
}
}
var key = tracing.addAsyncListener(asyncNoHandleError);
process.nextTick(function() { });
tracing.removeAsyncListener(key);
process.on('exit', function(code) {
// If the exit code isn't ok then return early to throw the stack that
// caused the bad return code.
if (code !== 0)
return;
// Calling removeAsyncListener *after* a callback is scheduled
// should not affect the handler from responding to the callback.
assert.equal(set, 2);
console.log('ok');
});

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

@ -1,59 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var net = require('net');
var tracing = require('tracing');
var errMsg = 'net - error: server connection';
var cntr = 0;
var al = tracing.addAsyncListener({
error: function(stor, er) {
cntr++;
process._rawDebug('Handling error: ' + er.message);
assert.equal(errMsg, er.message);
return true;
}
});
process.on('exit', function(status) {
tracing.removeAsyncListener(al);
console.log('exit status:', status);
assert.equal(status, 0);
console.log('cntr:', cntr);
assert.equal(cntr, 1);
console.log('ok');
});
var server = net.createServer(function(c) {
this.close();
throw new Error(errMsg);
});
server.listen(common.PORT, function() {
net.connect(common.PORT, function() {
this.destroy();
});
});

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

@ -1,46 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
var cntr = 0;
var al = tracing.createAsyncListener({
create: function() { cntr++; },
});
process.on('exit', function() {
assert.equal(cntr, 4);
console.log('ok');
});
tracing.addAsyncListener(al);
process.nextTick(function() {
tracing.addAsyncListener(al);
process.nextTick(function() {
tracing.addAsyncListener(al);
process.nextTick(function() {
process.nextTick(function() { });
});
});
});

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

@ -1,48 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var tracing = require('tracing');
// If there is an uncaughtException listener then the error thrown from
// "before" will be considered handled, thus calling setImmediate to
// finish execution of the nextTickQueue. This in turn will cause "before"
// to fire again, entering into an infinite loop.
// So the asyncQueue is cleared from the returned setImmediate in
// _fatalException to prevent this from happening.
var cntr = 0;
tracing.addAsyncListener({
before: function() {
if (++cntr > 1) {
// Can't throw since uncaughtException will also catch that.
process._rawDebug('Error: Multiple before callbacks called');
process.exit(1);
}
throw new Error('before');
}
});
process.on('uncaughtException', function() { });
process.nextTick();

188
test/simple/test-asynclistener.js

@ -1,188 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var net = require('net');
var fs = require('fs');
var dgram = require('dgram');
var tracing = require('tracing');
var addListener = tracing.addAsyncListener;
var removeListener = tracing.removeAsyncListener;
var actualAsync = 0;
var expectAsync = 0;
var callbacks = {
create: function onAsync() {
actualAsync++;
}
};
var listener = tracing.createAsyncListener(callbacks);
process.on('exit', function() {
process._rawDebug('expected', expectAsync);
process._rawDebug('actual ', actualAsync);
// TODO(trevnorris): Not a great test. If one was missed, but others
// overflowed then the test would still pass.
assert.ok(actualAsync >= expectAsync);
});
// Test listeners side-by-side
process.nextTick(function() {
addListener(listener);
var b = setInterval(function() {
clearInterval(b);
});
expectAsync++;
var c = setInterval(function() {
clearInterval(c);
});
expectAsync++;
setTimeout(function() { });
expectAsync++;
setTimeout(function() { });
expectAsync++;
process.nextTick(function() { });
expectAsync++;
process.nextTick(function() { });
expectAsync++;
setImmediate(function() { });
expectAsync++;
setImmediate(function() { });
expectAsync++;
setTimeout(function() { }, 10);
expectAsync++;
setTimeout(function() { }, 10);
expectAsync++;
removeListener(listener);
});
// Async listeners should propagate with nested callbacks
process.nextTick(function() {
addListener(listener);
var interval = 3;
process.nextTick(function() {
setTimeout(function() {
setImmediate(function() {
var i = setInterval(function() {
if (--interval <= 0)
clearInterval(i);
});
expectAsync++;
});
expectAsync++;
process.nextTick(function() {
setImmediate(function() {
setTimeout(function() { }, 20);
expectAsync++;
});
expectAsync++;
});
expectAsync++;
});
expectAsync++;
});
expectAsync++;
removeListener(listener);
});
// Test triggers with two async listeners
process.nextTick(function() {
addListener(listener);
addListener(listener);
setTimeout(function() {
process.nextTick(function() { });
expectAsync += 2;
});
expectAsync += 2;
removeListener(listener);
removeListener(listener);
});
// Test callbacks from fs I/O
process.nextTick(function() {
addListener(listener);
fs.stat('something random', function(err, stat) { });
expectAsync++;
setImmediate(function() {
fs.stat('random again', function(err, stat) { });
expectAsync++;
});
expectAsync++;
removeListener(listener);
});
// Test net I/O
process.nextTick(function() {
addListener(listener);
var server = net.createServer(function(c) { });
expectAsync++;
server.listen(common.PORT, function() {
server.close();
expectAsync++;
});
expectAsync++;
removeListener(listener);
});
// Test UDP
process.nextTick(function() {
addListener(listener);
var server = dgram.createSocket('udp4');
expectAsync++;
server.bind(common.PORT);
server.close();
expectAsync++;
removeListener(listener);
});
Loading…
Cancel
Save