Skip to content

Commit a052d4a

Browse files
committed
async_wrap: remove erroneous destroy list clear()
Remove a `.clear()` call on the list of destroy ids that may inadvertently swallow `destroy` events. The list is already cleared earlier in the `DestroyIdsCb` function, so usually this was a no-op; but when the garbage collection or its equivalent was active during a `destroy` hook itself, it was possible that `destroy` hooks were scheduled but cleared before the next event loop iteration in which they would have been emitted. Ref: nodejs#13286
1 parent ae6c704 commit a052d4a

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

src/async-wrap.cc

-2
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,6 @@ static void DestroyIdsCb(uv_idle_t* handle) {
164164
FatalException(env->isolate(), try_catch);
165165
}
166166
}
167-
168-
env->destroy_ids_list()->clear();
169167
}
170168

171169

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
// Test that async ids that are added to the destroy queue while running a
3+
// `destroy` callback are handled correctly.
4+
5+
const common = require('../common');
6+
const assert = require('assert');
7+
const async_hooks = require('async_hooks');
8+
9+
const initCalls = new Set();
10+
let destroyResCallCount = 0;
11+
let res2;
12+
13+
async_hooks.createHook({
14+
init: common.mustCallAtLeast((id, provider, triggerId) => {
15+
if (provider === 'foobar')
16+
initCalls.add(id);
17+
}, 2),
18+
destroy: common.mustCallAtLeast((id) => {
19+
if (!initCalls.has(id)) return;
20+
21+
switch (destroyResCallCount++) {
22+
case 0:
23+
// Trigger the second `destroy` call.
24+
res2.emitDestroy();
25+
break;
26+
case 2:
27+
assert.fail('More than 2 destroy() invocations');
28+
break;
29+
}
30+
}, 2)
31+
}).enable();
32+
33+
const res1 = new async_hooks.AsyncResource('foobar');
34+
res2 = new async_hooks.AsyncResource('foobar');
35+
res1.emitDestroy();
36+
37+
process.on('exit', () => assert.strictEqual(destroyResCallCount, 2));
38+
39+
// Keep the event loop alive for a small bit so that the `destroy` callback
40+
// can run. This can be removed once https://github.com/nodejs/node/issues/13262
41+
// is resolved.
42+
setTimeout(() => {}, 100);

0 commit comments

Comments
 (0)