diff --git a/test/common/README.md b/test/common/README.md index c0051ad9f7ca6e..41cc88aca10f59 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -322,6 +322,21 @@ otherwise. ### noWarnCode See `common.expectWarning()` for usage. +### onGC(target, listener) +* `target` [<Object>] +* `listener` [<Object>] + * `ongc` [<Function>] + +Installs a GC listener for the collection of `target`. + +This uses `async_hooks` for GC tracking. This means that it enables +`async_hooks` tracking, which may affect the test functionality. It also +means that between a `global.gc()` call and the listener being invoked +a full `setImmediate()` invocation passes. + +`listener` is an object to make it easier to use a closure; the target object +should not be in scope when `listener.ongc()` is created. + ### opensslCli * [<boolean>] diff --git a/test/common/index.js b/test/common/index.js index bf6b1077d1859b..cca289337ef4a2 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -883,3 +883,30 @@ exports.isCPPSymbolsNotMapped = exports.isWindows || exports.isAIX || exports.isLinuxPPCBE || exports.isFreeBSD; + +const gcTrackerMap = new WeakMap(); +const gcTrackerTag = 'NODE_TEST_COMMON_GC_TRACKER'; + +exports.onGC = function(obj, gcListener) { + const async_hooks = require('async_hooks'); + + const onGcAsyncHook = async_hooks.createHook({ + init: exports.mustCallAtLeast(function(id, type, trigger, resource) { + if (this.trackedId === undefined) { + assert.strictEqual(type, gcTrackerTag); + this.trackedId = id; + } + }), + destroy(id) { + assert.notStrictEqual(this.trackedId, -1); + if (id === this.trackedId) { + this.gcListener.ongc(); + onGcAsyncHook.disable(); + } + } + }).enable(); + onGcAsyncHook.gcListener = gcListener; + + gcTrackerMap.set(obj, new async_hooks.AsyncResource(gcTrackerTag)); + obj = null; +}; diff --git a/test/parallel/test-common-gc.js b/test/parallel/test-common-gc.js new file mode 100644 index 00000000000000..210b1d6d5f49ea --- /dev/null +++ b/test/parallel/test-common-gc.js @@ -0,0 +1,15 @@ +'use strict'; +// Flags: --expose-gc +const common = require('../common'); + +{ + const gcListener = { ongc: common.mustCall() }; + common.onGC({}, gcListener); + global.gc(); +} + +{ + const gcListener = { ongc: common.mustNotCall() }; + common.onGC(process, gcListener); + global.gc(); +}