mirror of https://github.com/lukechilds/node.git
Browse Source
AsyncListener is a JS API that works in tandem with the AsyncWrap class to allow the user to be alerted to key events in the life cycle of an asynchronous event. The AsyncWrap class has its own MakeCallback implementation that core will be migrated to use, and uses state sharing techniques to allow quicker communication between JS and C++ whether the async event callbacks need to be called.
Trevor Norris
11 years ago
26 changed files with 1594 additions and 200 deletions
@ -0,0 +1,324 @@ |
|||||
|
// 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.
|
||||
|
|
||||
|
#ifndef SRC_ASYNC_WRAP_INL_H_ |
||||
|
#define SRC_ASYNC_WRAP_INL_H_ |
||||
|
|
||||
|
#include "async-wrap.h" |
||||
|
#include "env.h" |
||||
|
#include "env-inl.h" |
||||
|
#include "util.h" |
||||
|
#include "util-inl.h" |
||||
|
#include "v8.h" |
||||
|
#include <assert.h> |
||||
|
|
||||
|
namespace node { |
||||
|
|
||||
|
inline AsyncWrap::AsyncWrap(Environment* env, v8::Handle<v8::Object> object) |
||||
|
: object_(env->isolate(), object), |
||||
|
env_(env), |
||||
|
async_flags_(NO_OPTIONS) { |
||||
|
assert(!object.IsEmpty()); |
||||
|
|
||||
|
if (!env->has_async_listeners()) |
||||
|
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_ |= ASYNC_LISTENERS; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline AsyncWrap::~AsyncWrap() { |
||||
|
assert(persistent().IsEmpty()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
template<typename TYPE> |
||||
|
inline void AsyncWrap::AddMethods(v8::Handle<v8::FunctionTemplate> t) { |
||||
|
NODE_SET_PROTOTYPE_METHOD(t, |
||||
|
"addAsyncListener", |
||||
|
AddAsyncListener<TYPE>); |
||||
|
NODE_SET_PROTOTYPE_METHOD(t, |
||||
|
"removeAsyncListener", |
||||
|
RemoveAsyncListener<TYPE>); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline uint32_t AsyncWrap::async_flags() const { |
||||
|
return async_flags_; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline void AsyncWrap::set_flag(unsigned int flag) { |
||||
|
async_flags_ |= flag; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline void AsyncWrap::remove_flag(unsigned int flag) { |
||||
|
async_flags_ &= ~flag; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline bool AsyncWrap::has_async_queue() { |
||||
|
return async_flags() & ASYNC_LISTENERS; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline Environment* AsyncWrap::env() const { |
||||
|
return env_; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline v8::Local<v8::Object> AsyncWrap::object() { |
||||
|
return PersistentToLocal(env()->isolate(), persistent()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline v8::Persistent<v8::Object>& AsyncWrap::persistent() { |
||||
|
return object_; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// I hate you domains.
|
||||
|
inline v8::Handle<v8::Value> AsyncWrap::MakeDomainCallback( |
||||
|
const v8::Handle<v8::Function> cb, |
||||
|
int argc, |
||||
|
v8::Handle<v8::Value>* argv) { |
||||
|
assert(env()->context() == env()->isolate()->GetCurrentContext()); |
||||
|
|
||||
|
v8::Local<v8::Object> context = object(); |
||||
|
v8::Local<v8::Object> process = env()->process_object(); |
||||
|
v8::Local<v8::Value> domain_v = context->Get(env()->domain_string()); |
||||
|
v8::Local<v8::Object> domain; |
||||
|
|
||||
|
v8::TryCatch try_catch; |
||||
|
try_catch.SetVerbose(true); |
||||
|
|
||||
|
if (has_async_queue()) { |
||||
|
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(); |
||||
|
if (has_domain) { |
||||
|
domain = domain_v.As<v8::Object>(); |
||||
|
|
||||
|
if (domain->Get(env()->disposed_string())->IsTrue()) |
||||
|
return Undefined(env()->isolate()); |
||||
|
|
||||
|
v8::Local<v8::Function> enter = |
||||
|
domain->Get(env()->enter_string()).As<v8::Function>(); |
||||
|
assert(enter->IsFunction()); |
||||
|
enter->Call(domain, 0, NULL); |
||||
|
|
||||
|
if (try_catch.HasCaught()) |
||||
|
return Undefined(env()->isolate()); |
||||
|
} |
||||
|
|
||||
|
v8::Local<v8::Value> ret = cb->Call(context, argc, argv); |
||||
|
|
||||
|
if (try_catch.HasCaught()) { |
||||
|
return Undefined(env()->isolate()); |
||||
|
} |
||||
|
|
||||
|
if (has_async_queue()) { |
||||
|
v8::Local<v8::Value> val = context.As<v8::Value>(); |
||||
|
env()->async_listener_unload_function()->Call(process, 1, &val); |
||||
|
} |
||||
|
|
||||
|
if (has_domain) { |
||||
|
v8::Local<v8::Function> exit = |
||||
|
domain->Get(env()->exit_string()).As<v8::Function>(); |
||||
|
assert(exit->IsFunction()); |
||||
|
exit->Call(domain, 0, NULL); |
||||
|
|
||||
|
if (try_catch.HasCaught()) |
||||
|
return Undefined(env()->isolate()); |
||||
|
} |
||||
|
|
||||
|
Environment::TickInfo* tick_info = env()->tick_info(); |
||||
|
|
||||
|
if (tick_info->in_tick()) { |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
if (tick_info->length() == 0) { |
||||
|
tick_info->set_index(0); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
tick_info->set_in_tick(true); |
||||
|
|
||||
|
env()->tick_callback_function()->Call(process, 0, NULL); |
||||
|
|
||||
|
tick_info->set_in_tick(false); |
||||
|
|
||||
|
if (try_catch.HasCaught()) { |
||||
|
tick_info->set_last_threw(true); |
||||
|
return Undefined(env()->isolate()); |
||||
|
} |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline v8::Handle<v8::Value> AsyncWrap::MakeCallback( |
||||
|
const v8::Handle<v8::Function> cb, |
||||
|
int argc, |
||||
|
v8::Handle<v8::Value>* argv) { |
||||
|
if (env()->using_domains()) |
||||
|
return MakeDomainCallback(cb, argc, argv); |
||||
|
|
||||
|
assert(env()->context() == env()->isolate()->GetCurrentContext()); |
||||
|
|
||||
|
v8::Local<v8::Object> context = object(); |
||||
|
v8::Local<v8::Object> process = env()->process_object(); |
||||
|
|
||||
|
v8::TryCatch try_catch; |
||||
|
try_catch.SetVerbose(true); |
||||
|
|
||||
|
if (has_async_queue()) { |
||||
|
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); |
||||
|
|
||||
|
if (try_catch.HasCaught()) { |
||||
|
return Undefined(env()->isolate()); |
||||
|
} |
||||
|
|
||||
|
if (has_async_queue()) { |
||||
|
v8::Local<v8::Value> val = context.As<v8::Value>(); |
||||
|
env()->async_listener_unload_function()->Call(process, 1, &val); |
||||
|
} |
||||
|
|
||||
|
Environment::TickInfo* tick_info = env()->tick_info(); |
||||
|
|
||||
|
if (tick_info->in_tick()) { |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
if (tick_info->length() == 0) { |
||||
|
tick_info->set_index(0); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
tick_info->set_in_tick(true); |
||||
|
|
||||
|
// TODO(trevnorris): Consider passing "context" to _tickCallback so it
|
||||
|
// can then be passed as the first argument to the nextTick callback.
|
||||
|
// That should greatly help needing to create closures.
|
||||
|
env()->tick_callback_function()->Call(process, 0, NULL); |
||||
|
|
||||
|
tick_info->set_in_tick(false); |
||||
|
|
||||
|
if (try_catch.HasCaught()) { |
||||
|
tick_info->set_last_threw(true); |
||||
|
return Undefined(env()->isolate()); |
||||
|
} |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline v8::Handle<v8::Value> AsyncWrap::MakeCallback( |
||||
|
const v8::Handle<v8::String> symbol, |
||||
|
int argc, |
||||
|
v8::Handle<v8::Value>* argv) { |
||||
|
v8::Local<v8::Value> cb_v = object()->Get(symbol); |
||||
|
v8::Local<v8::Function> cb = cb_v.As<v8::Function>(); |
||||
|
assert(cb->IsFunction()); |
||||
|
|
||||
|
return MakeCallback(cb, argc, argv); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline v8::Handle<v8::Value> AsyncWrap::MakeCallback( |
||||
|
uint32_t index, |
||||
|
int argc, |
||||
|
v8::Handle<v8::Value>* argv) { |
||||
|
v8::Local<v8::Value> cb_v = object()->Get(index); |
||||
|
v8::Local<v8::Function> cb = cb_v.As<v8::Function>(); |
||||
|
assert(cb->IsFunction()); |
||||
|
|
||||
|
return MakeCallback(cb, argc, argv); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
template <typename TYPE> |
||||
|
inline void AsyncWrap::AddAsyncListener( |
||||
|
const v8::FunctionCallbackInfo<v8::Value>& args) { |
||||
|
Environment* env = Environment::GetCurrent(args.GetIsolate()); |
||||
|
v8::HandleScope handle_scope(args.GetIsolate()); |
||||
|
|
||||
|
v8::Local<v8::Object> handle = args.This(); |
||||
|
v8::Local<v8::Value> listener = args[0]; |
||||
|
assert(listener->IsObject()); |
||||
|
assert(handle->InternalFieldCount() > 0); |
||||
|
|
||||
|
env->async_listener_push_function()->Call(handle, 1, &listener); |
||||
|
|
||||
|
TYPE* wrap = static_cast<TYPE*>( |
||||
|
handle->GetAlignedPointerFromInternalField(0)); |
||||
|
assert(wrap != NULL); |
||||
|
wrap->set_flag(ASYNC_LISTENERS); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
template <typename TYPE> |
||||
|
inline void AsyncWrap::RemoveAsyncListener( |
||||
|
const v8::FunctionCallbackInfo<v8::Value>& args) { |
||||
|
Environment* env = Environment::GetCurrent(args.GetIsolate()); |
||||
|
v8::HandleScope handle_scope(args.GetIsolate()); |
||||
|
|
||||
|
v8::Local<v8::Object> handle = args.This(); |
||||
|
v8::Local<v8::Value> listener = args[0]; |
||||
|
assert(listener->IsObject()); |
||||
|
assert(handle->InternalFieldCount() > 0); |
||||
|
|
||||
|
v8::Local<v8::Value> ret = |
||||
|
env->async_listener_strip_function()->Call(handle, 1, &listener); |
||||
|
|
||||
|
if (ret->IsFalse()) { |
||||
|
TYPE* wrap = static_cast<TYPE*>( |
||||
|
handle->GetAlignedPointerFromInternalField(0)); |
||||
|
assert(wrap != NULL); |
||||
|
wrap->remove_flag(ASYNC_LISTENERS); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} // namespace node
|
||||
|
|
||||
|
#endif // SRC_ASYNC_WRAP_INL_H_
|
@ -0,0 +1,97 @@ |
|||||
|
// 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.
|
||||
|
|
||||
|
#ifndef SRC_ASYNC_WRAP_H_ |
||||
|
#define SRC_ASYNC_WRAP_H_ |
||||
|
|
||||
|
#include "env.h" |
||||
|
#include "v8.h" |
||||
|
|
||||
|
namespace node { |
||||
|
|
||||
|
class AsyncWrap { |
||||
|
public: |
||||
|
enum AsyncFlags { |
||||
|
NO_OPTIONS = 0, |
||||
|
ASYNC_LISTENERS = 1 |
||||
|
}; |
||||
|
|
||||
|
inline AsyncWrap(Environment* env, v8::Handle<v8::Object> object); |
||||
|
|
||||
|
inline ~AsyncWrap(); |
||||
|
|
||||
|
template <typename Type> |
||||
|
static inline void AddMethods(v8::Handle<v8::FunctionTemplate> t); |
||||
|
|
||||
|
inline uint32_t async_flags() const; |
||||
|
|
||||
|
inline void set_flag(unsigned int flag); |
||||
|
|
||||
|
inline void remove_flag(unsigned int flag); |
||||
|
|
||||
|
inline bool has_async_queue(); |
||||
|
|
||||
|
inline Environment* env() const; |
||||
|
|
||||
|
// Returns the wrapped object. Illegal to call in your destructor.
|
||||
|
inline v8::Local<v8::Object> object(); |
||||
|
|
||||
|
// Parent class is responsible to Dispose.
|
||||
|
inline v8::Persistent<v8::Object>& persistent(); |
||||
|
|
||||
|
// Only call these within a valid HandleScope.
|
||||
|
inline v8::Handle<v8::Value> MakeCallback(const v8::Handle<v8::Function> cb, |
||||
|
int argc, |
||||
|
v8::Handle<v8::Value>* argv); |
||||
|
inline v8::Handle<v8::Value> MakeCallback(const v8::Handle<v8::String> symbol, |
||||
|
int argc, |
||||
|
v8::Handle<v8::Value>* argv); |
||||
|
inline v8::Handle<v8::Value> MakeCallback(uint32_t index, |
||||
|
int argc, |
||||
|
v8::Handle<v8::Value>* argv); |
||||
|
|
||||
|
private: |
||||
|
// TODO(trevnorris): BURN IN FIRE! Remove this as soon as a suitable
|
||||
|
// replacement is committed.
|
||||
|
inline v8::Handle<v8::Value> MakeDomainCallback( |
||||
|
const v8::Handle<v8::Function> cb, |
||||
|
int argc, |
||||
|
v8::Handle<v8::Value>* argv); |
||||
|
|
||||
|
// Add an async listener to an existing handle.
|
||||
|
template <typename Type> |
||||
|
static inline void AddAsyncListener( |
||||
|
const v8::FunctionCallbackInfo<v8::Value>& args); |
||||
|
|
||||
|
// Remove an async listener to an existing handle.
|
||||
|
template <typename Type> |
||||
|
static inline void RemoveAsyncListener( |
||||
|
const v8::FunctionCallbackInfo<v8::Value>& args); |
||||
|
|
||||
|
v8::Persistent<v8::Object> object_; |
||||
|
Environment* const env_; |
||||
|
uint32_t async_flags_; |
||||
|
}; |
||||
|
|
||||
|
} // namespace node
|
||||
|
|
||||
|
|
||||
|
#endif // SRC_ASYNC_WRAP_H_
|
@ -0,0 +1,258 @@ |
|||||
|
// 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 addListener = process.addAsyncListener; |
||||
|
var removeListener = process.removeAsyncListener; |
||||
|
var errorMsgs = []; |
||||
|
var currentMsg = ''; |
||||
|
var caught = 0; |
||||
|
var expectCaught = 0; |
||||
|
var exitCbRan = false; |
||||
|
|
||||
|
function asyncL() { } |
||||
|
|
||||
|
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 = process.createAsyncListener(asyncL, 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); |
||||
|
}); |
@ -0,0 +1,71 @@ |
|||||
|
// 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 addListener = process.addAsyncListener; |
||||
|
var removeListener = process.removeAsyncListener; |
||||
|
var caught = []; |
||||
|
var expect = []; |
||||
|
|
||||
|
function asyncL(a) {} |
||||
|
|
||||
|
var callbacksObj = { |
||||
|
error: function(value, er) { |
||||
|
process._rawDebug('caught', er.message); |
||||
|
caught.push(er.message); |
||||
|
return (expect.indexOf(er.message) !== -1); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var listener = process.createAsyncListener(asyncL, 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); |
||||
|
}); |
@ -0,0 +1,47 @@ |
|||||
|
// 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'); |
||||
|
|
||||
|
// 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; |
||||
|
|
||||
|
|
||||
|
process.addAsyncListener(function() { }, { |
||||
|
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(); |
@ -0,0 +1,185 @@ |
|||||
|
// 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 addListener = process.addAsyncListener; |
||||
|
var removeListener = process.removeAsyncListener; |
||||
|
var actualAsync = 0; |
||||
|
var expectAsync = 0; |
||||
|
|
||||
|
function onAsync() { |
||||
|
actualAsync++; |
||||
|
} |
||||
|
|
||||
|
var listener = process.createAsyncListener(onAsync); |
||||
|
|
||||
|
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…
Reference in new issue