Skip to content

Commit

Permalink
add resolve hook
Browse files Browse the repository at this point in the history
  • Loading branch information
hayes committed Jun 3, 2017
1 parent 1dc3272 commit 4439e99
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 5 deletions.
66 changes: 61 additions & 5 deletions lib/async_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var tmp_async_hook_fields = null;
// Each constant tracks how many callbacks there are for any given step of
// async execution. These are tracked so if the user didn't include callbacks
// for a given step, that step can bail out early.
const { kInit, kBefore, kAfter, kDestroy, kCurrentAsyncId, kCurrentTriggerId,
const { kInit, kBefore, kAfter, kDestroy, kResolve, kCurrentAsyncId, kCurrentTriggerId,
kAsyncUidCntr, kInitTriggerId } = async_wrap.constants;

const { async_id_symbol, trigger_id_symbol } = async_wrap;
Expand All @@ -48,6 +48,7 @@ const init_symbol = Symbol('init');
const before_symbol = Symbol('before');
const after_symbol = Symbol('after');
const destroy_symbol = Symbol('destroy');
const resolve_symbol = Symbol('resolve');

let setupHooksCalled = false;

Expand All @@ -71,7 +72,7 @@ function fatalError(e) {
// Public API //

class AsyncHook {
constructor({ init, before, after, destroy }) {
constructor({ init, before, after, destroy, resolve }) {
if (init !== undefined && typeof init !== 'function')
throw new TypeError('init must be a function');
if (before !== undefined && typeof before !== 'function')
Expand All @@ -80,11 +81,14 @@ class AsyncHook {
throw new TypeError('after must be a function');
if (destroy !== undefined && typeof destroy !== 'function')
throw new TypeError('destroy must be a function');
if (resolve !== undefined && typeof resolve !== 'function')
throw new TypeError('resolve must be a function');

this[init_symbol] = init;
this[before_symbol] = before;
this[after_symbol] = after;
this[destroy_symbol] = destroy;
this[resolve_symbol] = resolve
}

enable() {
Expand All @@ -105,7 +109,8 @@ class AsyncHook {
async_wrap.setupHooks({ init,
before: emitBeforeN,
after: emitAfterN,
destroy: emitDestroyN });
destroy: emitDestroyN,
resolve: emitResolveN });
}

// createHook() has already enforced that the callbacks are all functions,
Expand All @@ -115,6 +120,7 @@ class AsyncHook {
hook_fields[kBefore] += +!!this[before_symbol];
hook_fields[kAfter] += +!!this[after_symbol];
hook_fields[kDestroy] += +!!this[destroy_symbol];
hook_fields[kResolve] += +!!this[resolve_symbol];
hooks_array.push(this);
return this;
}
Expand All @@ -130,6 +136,7 @@ class AsyncHook {
hook_fields[kBefore] -= +!!this[before_symbol];
hook_fields[kAfter] -= +!!this[after_symbol];
hook_fields[kDestroy] -= +!!this[destroy_symbol];
hook_fields[kResolve] -= +!!this[resolve_symbol];
hooks_array.splice(index, 1);
return this;
}
Expand All @@ -151,13 +158,14 @@ function getHookArrays() {

function storeActiveHooks() {
tmp_active_hooks_array = active_hooks_array.slice();
// Don't want to make the assumption that kInit to kDestroy are indexes 0 to
// 4. So do this the long way.
// Don't want to make the assumption that kInit to kResolve are indexes 0 to
// 5. So do this the long way.
tmp_async_hook_fields = [];
tmp_async_hook_fields[kInit] = async_hook_fields[kInit];
tmp_async_hook_fields[kBefore] = async_hook_fields[kBefore];
tmp_async_hook_fields[kAfter] = async_hook_fields[kAfter];
tmp_async_hook_fields[kDestroy] = async_hook_fields[kDestroy];
tmp_async_hook_fields[kResolve] = async_hook_fields[kResolve];
}


Expand All @@ -169,6 +177,7 @@ function restoreTmpHooks() {
async_hook_fields[kBefore] = tmp_async_hook_fields[kBefore];
async_hook_fields[kAfter] = tmp_async_hook_fields[kAfter];
async_hook_fields[kDestroy] = tmp_async_hook_fields[kDestroy];
async_hook_fields[kResolve] = tmp_async_hook_fields[kResolve];

tmp_active_hooks_array = null;
tmp_async_hook_fields = null;
Expand Down Expand Up @@ -242,6 +251,11 @@ class AsyncResource {
return this;
}

emitResolve() {
emitResolveS(this[async_id_symbol], currentId());
return this;
}

asyncId() {
return this[async_id_symbol];
}
Expand Down Expand Up @@ -426,6 +440,40 @@ function emitDestroyN(asyncId) {
}


function emitResolveN(asyncId, triggerId) {
processing_hook = true;
for (var i = 0; i < active_hooks_array.length; i++) {
if (typeof active_hooks_array[i][resolve_symbol] === 'function') {
runResolveCallback(active_hooks_array[i][resolve_symbol], asyncId, triggerId);
}
}
processing_hook = false;

if (tmp_active_hooks_array !== null) {
restoreTmpHooks();
}
}


// Usage: emitResolveS(asyncId, triggerId).
function emitResolveS(asyncId, triggerId) {
// CHECK(Number.isSafeInteger(asyncId) && asyncId > 0)
// CHECK(Number.isSafeInteger(triggerId) && triggerId > 0)

// Validate the ids.
if (asyncId < 0 || triggerId < 0) {
fatalError('resolve(): asyncId or triggerId is less than zero ' +
`(asyncId: ${asyncId}, triggerId: ${triggerId})`);
}

if (async_hook_fields[kResolve] === 0) {
return;
}

emitResolveN(asyncId, triggerId);
}


// Emit callbacks for native calls. Since some state can be setup directly from
// C++ there's no need to perform all the work here.

Expand Down Expand Up @@ -462,6 +510,13 @@ function runInitCallback(cb, asyncId, type, triggerId, resource) {
}
}

function runResolveCallback(cb, asyncId, triggerId) {
try {
cb(asyncId, triggerId);
} catch (e) {
fatalError(e);
}
}

function runCallback(cb, asyncId) {
try {
Expand Down Expand Up @@ -490,4 +545,5 @@ module.exports = {
emitBefore: emitBeforeS,
emitAfter: emitAfterS,
emitDestroy: emitDestroyS,
emitResolve: emitResolveS,
};
27 changes: 27 additions & 0 deletions src/async-wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,29 @@ bool AsyncWrap::EmitAfter(Environment* env, double async_id) {
return true;
}

bool AsyncWrap::EmitResolve(Environment* env, double async_id, double trigger_id) {
AsyncHooks* async_hooks = env->async_hooks();

if (async_hooks->fields()[AsyncHooks::kResolve] > 0) {
Local<Function> fn = env->async_hooks_resolve_function();

Local<Value> argv[] = {
Number::New(env->isolate(), async_id),
Number::New(env->isolate(), trigger_id),
};
TryCatch try_catch(env->isolate());
MaybeLocal<Value> ar = fn->Call(
env->context(), Undefined(env->isolate()), arraysize(argv), argv);
if (ar.IsEmpty()) {
ClearFatalExceptionHandlers(env);
FatalException(env->isolate(), try_catch);
return false;
}
}

return true;
}

class PromiseWrap : public AsyncWrap {
public:
PromiseWrap(Environment* env, Local<Object> object, bool silent)
Expand Down Expand Up @@ -315,6 +338,7 @@ static void PromiseHook(PromiseHookType type, Local<Promise> promise,
wrap->MakeWeak(wrap);
} else if (type == PromiseHookType::kResolve) {
// TODO(matthewloring): need to expose this through the async hooks api.
AsyncWrap::EmitResolve(env, wrap->get_id(), env->current_async_id());
}
CHECK_NE(wrap, nullptr);
if (type == PromiseHookType::kBefore) {
Expand Down Expand Up @@ -349,6 +373,7 @@ static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
SET_HOOK_FN(before);
SET_HOOK_FN(after);
SET_HOOK_FN(destroy);
SET_HOOK_FN(resolve);
env->AddPromiseHook(PromiseHook, nullptr);
#undef SET_HOOK_FN
}
Expand Down Expand Up @@ -460,6 +485,7 @@ void AsyncWrap::Initialize(Local<Object> target,
SET_HOOKS_CONSTANT(kBefore);
SET_HOOKS_CONSTANT(kAfter);
SET_HOOKS_CONSTANT(kDestroy);
SET_HOOKS_CONSTANT(kResolve);
SET_HOOKS_CONSTANT(kCurrentAsyncId);
SET_HOOKS_CONSTANT(kCurrentTriggerId);
SET_HOOKS_CONSTANT(kAsyncUidCntr);
Expand Down Expand Up @@ -492,6 +518,7 @@ void AsyncWrap::Initialize(Local<Object> target,
env->set_async_hooks_before_function(Local<Function>());
env->set_async_hooks_after_function(Local<Function>());
env->set_async_hooks_destroy_function(Local<Function>());
env->set_async_hooks_resolve_function(Local<Function>());
}


Expand Down
1 change: 1 addition & 0 deletions src/async-wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class AsyncWrap : public BaseObject {

static bool EmitBefore(Environment* env, double id);
static bool EmitAfter(Environment* env, double id);
static bool EmitResolve(Environment* env, double promise_id, double async_id);

inline ProviderType provider_type() const;

Expand Down
2 changes: 2 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ namespace node {
V(async_hooks_init_function, v8::Function) \
V(async_hooks_before_function, v8::Function) \
V(async_hooks_after_function, v8::Function) \
V(async_hooks_resolve_function, v8::Function) \
V(binding_cache_object, v8::Object) \
V(buffer_constructor_function, v8::Function) \
V(buffer_prototype_object, v8::Object) \
Expand Down Expand Up @@ -343,6 +344,7 @@ class Environment {
kBefore,
kAfter,
kDestroy,
kResolve,
kFieldsCount,
};

Expand Down

0 comments on commit 4439e99

Please sign in to comment.