async_hooks: only disable promise hook if wanted

The promise hook has been disabled asynchronously in order to solve
issues when an async hook is disabled during a microtask.

This means that after scheduling the disable-promise-hook call,
attempts to enable it synchronously will later be unintentionally
overridden.

In order to solve this, make sure that the promise hooks are still
no longer desired at the time at which we would disable them.

Fixes: https://github.com/nodejs/node/issues/27585

PR-URL: https://github.com/nodejs/node/pull/27590
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
Anna Henningsen 2019-05-07 00:33:59 +02:00
parent 3309c856bc
commit 618fcbd125
No known key found for this signature in database
GPG Key ID: 9C63F3A6CD2AD8F9
3 changed files with 36 additions and 11 deletions

View File

@ -69,6 +69,7 @@ const active_hooks = {
};
const { registerDestroyHook } = async_wrap;
const { enqueueMicrotask } = internalBinding('task_queue');
// 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
@ -231,14 +232,26 @@ function restoreActiveHooks() {
}
let wantPromiseHook = false;
function enableHooks() {
enablePromiseHook();
async_hook_fields[kCheck] += 1;
wantPromiseHook = true;
enablePromiseHook();
}
function disableHooks() {
disablePromiseHook();
async_hook_fields[kCheck] -= 1;
wantPromiseHook = false;
// Delay the call to `disablePromiseHook()` because we might currently be
// between the `before` and `after` calls of a Promise.
enqueueMicrotask(disablePromiseHookIfNecessary);
}
function disablePromiseHookIfNecessary() {
if (!wantPromiseHook)
disablePromiseHook();
}
// Internal Embedder API //

View File

@ -334,15 +334,10 @@ static void EnablePromiseHook(const FunctionCallbackInfo<Value>& args) {
static void DisablePromiseHook(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// Delay the call to `RemovePromiseHook` because we might currently be
// between the `before` and `after` calls of a Promise.
isolate->EnqueueMicrotask([](void* data) {
// The per-Isolate API provides no way of knowing whether there are multiple
// users of the PromiseHook. That hopefully goes away when V8 introduces
// a per-context API.
Isolate* isolate = static_cast<Isolate*>(data);
isolate->SetPromiseHook(nullptr);
}, static_cast<void*>(isolate));
// The per-Isolate API provides no way of knowing whether there are multiple
// users of the PromiseHook. That hopefully goes away when V8 introduces
// a per-context API.
isolate->SetPromiseHook(nullptr);
}

View File

@ -0,0 +1,17 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
// Regression test for https://github.com/nodejs/node/issues/27585.
async_hooks.createHook({ init: () => {} }).enable().disable().enable();
async_hooks.createHook({ init: () => {} }).enable();
async function main() {
const initialAsyncId = async_hooks.executionAsyncId();
await 0;
assert.notStrictEqual(async_hooks.executionAsyncId(), initialAsyncId);
}
main().then(common.mustCall());