forked from nodejs/node
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
async_hooks: execute destroy hooks earlier
Use a microtask to call destroy hooks in case there are a lot queued as immediate may be scheduled late in case of long running promise chains. Queuing a mircrotasks in GC context is not allowed therefore an interrupt is triggered to do this in JS context as fast as possible. fixes: nodejs#34328 refs: nodejs#33896 PR-URL: nodejs#34342 Fixes: nodejs#34328 Refs: nodejs#33896 Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]>
- Loading branch information
Showing
2 changed files
with
109 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
'use strict'; | ||
// Flags: --expose_gc | ||
|
||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const tick = require('../common/tick'); | ||
|
||
const { createHook, AsyncResource } = require('async_hooks'); | ||
|
||
// Test priority of destroy hook relative to nextTick,... and | ||
// verify a microtask is scheduled in case a lot items are queued | ||
|
||
const resType = 'MyResource'; | ||
let activeId = -1; | ||
createHook({ | ||
init(id, type) { | ||
if (type === resType) { | ||
assert.strictEqual(activeId, -1); | ||
activeId = id; | ||
} | ||
}, | ||
destroy(id) { | ||
if (activeId === id) { | ||
activeId = -1; | ||
} | ||
} | ||
}).enable(); | ||
|
||
function testNextTick() { | ||
assert.strictEqual(activeId, -1); | ||
const res = new AsyncResource(resType); | ||
assert.strictEqual(activeId, res.asyncId()); | ||
res.emitDestroy(); | ||
// nextTick has higher prio than emit destroy | ||
process.nextTick(common.mustCall(() => | ||
assert.strictEqual(activeId, res.asyncId())) | ||
); | ||
} | ||
|
||
function testQueueMicrotask() { | ||
assert.strictEqual(activeId, -1); | ||
const res = new AsyncResource(resType); | ||
assert.strictEqual(activeId, res.asyncId()); | ||
res.emitDestroy(); | ||
// queueMicrotask has higher prio than emit destroy | ||
queueMicrotask(common.mustCall(() => | ||
assert.strictEqual(activeId, res.asyncId())) | ||
); | ||
} | ||
|
||
function testImmediate() { | ||
assert.strictEqual(activeId, -1); | ||
const res = new AsyncResource(resType); | ||
assert.strictEqual(activeId, res.asyncId()); | ||
res.emitDestroy(); | ||
setImmediate(common.mustCall(() => | ||
assert.strictEqual(activeId, -1)) | ||
); | ||
} | ||
|
||
function testPromise() { | ||
assert.strictEqual(activeId, -1); | ||
const res = new AsyncResource(resType); | ||
assert.strictEqual(activeId, res.asyncId()); | ||
res.emitDestroy(); | ||
// Promise has higher prio than emit destroy | ||
Promise.resolve().then(common.mustCall(() => | ||
assert.strictEqual(activeId, res.asyncId())) | ||
); | ||
} | ||
|
||
async function testAwait() { | ||
assert.strictEqual(activeId, -1); | ||
const res = new AsyncResource(resType); | ||
assert.strictEqual(activeId, res.asyncId()); | ||
res.emitDestroy(); | ||
|
||
for (let i = 0; i < 5000; i++) { | ||
await Promise.resolve(); | ||
} | ||
global.gc(); | ||
await Promise.resolve(); | ||
// Limit to trigger a microtask not yet reached | ||
assert.strictEqual(activeId, res.asyncId()); | ||
for (let i = 0; i < 5000; i++) { | ||
await Promise.resolve(); | ||
} | ||
global.gc(); | ||
await Promise.resolve(); | ||
assert.strictEqual(activeId, -1); | ||
} | ||
|
||
testNextTick(); | ||
tick(2, testQueueMicrotask); | ||
tick(4, testImmediate); | ||
tick(6, testPromise); | ||
tick(8, () => testAwait().then(common.mustCall())); |