Skip to content

Commit e74e661

Browse files
addaleaxtargos
authored andcommittedMay 13, 2019
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: #27585 PR-URL: #27590 Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
1 parent 74046ce commit e74e661

File tree

3 files changed

+36
-11
lines changed

3 files changed

+36
-11
lines changed
 

‎lib/internal/async_hooks.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ const active_hooks = {
6969
};
7070

7171
const { registerDestroyHook } = async_wrap;
72+
const { enqueueMicrotask } = internalBinding('task_queue');
7273

7374
// Each constant tracks how many callbacks there are for any given step of
7475
// async execution. These are tracked so if the user didn't include callbacks
@@ -231,14 +232,26 @@ function restoreActiveHooks() {
231232
}
232233

233234

235+
let wantPromiseHook = false;
234236
function enableHooks() {
235-
enablePromiseHook();
236237
async_hook_fields[kCheck] += 1;
238+
239+
wantPromiseHook = true;
240+
enablePromiseHook();
237241
}
238242

239243
function disableHooks() {
240-
disablePromiseHook();
241244
async_hook_fields[kCheck] -= 1;
245+
246+
wantPromiseHook = false;
247+
// Delay the call to `disablePromiseHook()` because we might currently be
248+
// between the `before` and `after` calls of a Promise.
249+
enqueueMicrotask(disablePromiseHookIfNecessary);
250+
}
251+
252+
function disablePromiseHookIfNecessary() {
253+
if (!wantPromiseHook)
254+
disablePromiseHook();
242255
}
243256

244257
// Internal Embedder API //

‎src/async_wrap.cc

+4-9
Original file line numberDiff line numberDiff line change
@@ -334,15 +334,10 @@ static void EnablePromiseHook(const FunctionCallbackInfo<Value>& args) {
334334
static void DisablePromiseHook(const FunctionCallbackInfo<Value>& args) {
335335
Isolate* isolate = args.GetIsolate();
336336

337-
// Delay the call to `RemovePromiseHook` because we might currently be
338-
// between the `before` and `after` calls of a Promise.
339-
isolate->EnqueueMicrotask([](void* data) {
340-
// The per-Isolate API provides no way of knowing whether there are multiple
341-
// users of the PromiseHook. That hopefully goes away when V8 introduces
342-
// a per-context API.
343-
Isolate* isolate = static_cast<Isolate*>(data);
344-
isolate->SetPromiseHook(nullptr);
345-
}, static_cast<void*>(isolate));
337+
// The per-Isolate API provides no way of knowing whether there are multiple
338+
// users of the PromiseHook. That hopefully goes away when V8 introduces
339+
// a per-context API.
340+
isolate->SetPromiseHook(nullptr);
346341
}
347342

348343

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const async_hooks = require('async_hooks');
5+
6+
// Regression test for https://github.com/nodejs/node/issues/27585.
7+
8+
async_hooks.createHook({ init: () => {} }).enable().disable().enable();
9+
async_hooks.createHook({ init: () => {} }).enable();
10+
11+
async function main() {
12+
const initialAsyncId = async_hooks.executionAsyncId();
13+
await 0;
14+
assert.notStrictEqual(async_hooks.executionAsyncId(), initialAsyncId);
15+
}
16+
17+
main().then(common.mustCall());

0 commit comments

Comments
 (0)