mirror of https://github.com/nodejs/node.git
test: merge ongc and gcutil into gc.js
PR-URL: https://github.com/nodejs/node/pull/54355 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
parent
7616855b2a
commit
0b6b2a4dc7
|
@ -3,7 +3,7 @@
|
|||
// Flags: --expose-gc
|
||||
|
||||
const common = require('../../common');
|
||||
|
||||
const { gcUntil } = require('../../common/gc');
|
||||
// Verify that addons can create GarbageCollected objects and
|
||||
// have them traced properly.
|
||||
|
||||
|
@ -35,7 +35,7 @@ setTimeout(async function() {
|
|||
for (let i = 0; i < count; ++i) {
|
||||
array[i] = new CppGCed();
|
||||
}
|
||||
await common.gcUntil(
|
||||
await gcUntil(
|
||||
'All old CppGCed are destroyed',
|
||||
() => states[kDestructCount] === count,
|
||||
);
|
||||
|
@ -44,7 +44,7 @@ setTimeout(async function() {
|
|||
array = null;
|
||||
globalThis.gc();
|
||||
|
||||
await common.gcUntil(
|
||||
await gcUntil(
|
||||
'All old CppGCed are destroyed',
|
||||
() => states[kDestructCount] === count * 2,
|
||||
);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
const common = require('../common');
|
||||
const { AsyncLocalStorage } = require('async_hooks');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
|
||||
let asyncLocalStorage = new AsyncLocalStorage();
|
||||
|
||||
|
|
|
@ -982,7 +982,7 @@ module exports a single `onGC()` function.
|
|||
|
||||
```js
|
||||
require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
|
||||
onGC({}, { ongc() { console.log('collected'); } });
|
||||
```
|
||||
|
|
|
@ -1,9 +1,72 @@
|
|||
'use strict';
|
||||
|
||||
const wait = require('timers/promises').setTimeout;
|
||||
const assert = require('assert');
|
||||
const common = require('../common');
|
||||
const gcTrackerMap = new WeakMap();
|
||||
const gcTrackerTag = 'NODE_TEST_COMMON_GC_TRACKER';
|
||||
|
||||
// TODO(joyeecheung): merge ongc.js and gcUntil from common/index.js
|
||||
// into this.
|
||||
/**
|
||||
* Installs a garbage collection listener for the specified object.
|
||||
* Uses async_hooks for GC tracking, which may affect test functionality.
|
||||
* A full setImmediate() invocation passes between a global.gc() call and the listener being invoked.
|
||||
* @param {object} obj - The target object to track for garbage collection.
|
||||
* @param {object} gcListener - The listener object containing the ongc callback.
|
||||
* @param {Function} gcListener.ongc - The function to call when the target object is garbage collected.
|
||||
*/
|
||||
function onGC(obj, gcListener) {
|
||||
const async_hooks = require('async_hooks');
|
||||
|
||||
const onGcAsyncHook = async_hooks.createHook({
|
||||
init: common.mustCallAtLeast(function(id, type) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeatedly triggers garbage collection until a specified condition is met or a maximum number of attempts is reached.
|
||||
* @param {string|Function} [name] - Optional name, used in the rejection message if the condition is not met.
|
||||
* @param {Function} condition - A function that returns true when the desired condition is met.
|
||||
* @returns {Promise} A promise that resolves when the condition is met, or rejects after 10 failed attempts.
|
||||
*/
|
||||
function gcUntil(name, condition) {
|
||||
if (typeof name === 'function') {
|
||||
condition = name;
|
||||
name = undefined;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let count = 0;
|
||||
function gcAndCheck() {
|
||||
setImmediate(() => {
|
||||
count++;
|
||||
global.gc();
|
||||
if (condition()) {
|
||||
resolve();
|
||||
} else if (count < 10) {
|
||||
gcAndCheck();
|
||||
} else {
|
||||
reject(name === undefined ? undefined : 'Test ' + name + ' failed');
|
||||
}
|
||||
});
|
||||
}
|
||||
gcAndCheck();
|
||||
});
|
||||
}
|
||||
|
||||
// This function can be used to check if an object factor leaks or not,
|
||||
// but it needs to be used with care:
|
||||
|
@ -124,4 +187,6 @@ module.exports = {
|
|||
checkIfCollectable,
|
||||
runAndBreathe,
|
||||
checkIfCollectableByCounting,
|
||||
onGC,
|
||||
gcUntil,
|
||||
};
|
||||
|
|
|
@ -851,30 +851,6 @@ function skipIfDumbTerminal() {
|
|||
}
|
||||
}
|
||||
|
||||
function gcUntil(name, condition) {
|
||||
if (typeof name === 'function') {
|
||||
condition = name;
|
||||
name = undefined;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let count = 0;
|
||||
function gcAndCheck() {
|
||||
setImmediate(() => {
|
||||
count++;
|
||||
global.gc();
|
||||
if (condition()) {
|
||||
resolve();
|
||||
} else if (count < 10) {
|
||||
gcAndCheck();
|
||||
} else {
|
||||
reject(name === undefined ? undefined : 'Test ' + name + ' failed');
|
||||
}
|
||||
});
|
||||
}
|
||||
gcAndCheck();
|
||||
});
|
||||
}
|
||||
|
||||
function requireNoPackageJSONAbove(dir = __dirname) {
|
||||
let possiblePackage = path.join(dir, '..', 'package.json');
|
||||
let lastPackage = null;
|
||||
|
@ -985,7 +961,6 @@ const common = {
|
|||
expectsError,
|
||||
expectRequiredModule,
|
||||
expectWarning,
|
||||
gcUntil,
|
||||
getArrayBufferViews,
|
||||
getBufferSources,
|
||||
getCallSite,
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const gcTrackerMap = new WeakMap();
|
||||
const gcTrackerTag = 'NODE_TEST_COMMON_GC_TRACKER';
|
||||
|
||||
function onGC(obj, gcListener) {
|
||||
const async_hooks = require('async_hooks');
|
||||
|
||||
const onGcAsyncHook = async_hooks.createHook({
|
||||
init: common.mustCallAtLeast(function(id, type) {
|
||||
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;
|
||||
}
|
||||
|
||||
module.exports = onGC;
|
|
@ -3,11 +3,12 @@
|
|||
'use strict';
|
||||
const common = require('../../common');
|
||||
const addon = require(`./build/${common.buildType}/6_object_wrap`);
|
||||
const { gcUntil } = require('../../common/gc');
|
||||
|
||||
(function scope() {
|
||||
addon.objectWrapDanglingReference({});
|
||||
})();
|
||||
|
||||
common.gcUntil('object-wrap-ref', () => {
|
||||
gcUntil('object-wrap-ref', () => {
|
||||
return addon.objectWrapDanglingReferenceTest();
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
const common = require('../../common');
|
||||
const assert = require('assert');
|
||||
const test = require(`./build/${common.buildType}/7_factory_wrap`);
|
||||
const { gcUntil } = require('../../common/gc');
|
||||
|
||||
assert.strictEqual(test.finalizeCount, 0);
|
||||
async function runGCTests() {
|
||||
|
@ -13,7 +14,7 @@ async function runGCTests() {
|
|||
assert.strictEqual(obj.plusOne(), 12);
|
||||
assert.strictEqual(obj.plusOne(), 13);
|
||||
})();
|
||||
await common.gcUntil('test 1', () => (test.finalizeCount === 1));
|
||||
await gcUntil('test 1', () => (test.finalizeCount === 1));
|
||||
|
||||
(() => {
|
||||
const obj2 = test.createObject(20);
|
||||
|
@ -21,6 +22,6 @@ async function runGCTests() {
|
|||
assert.strictEqual(obj2.plusOne(), 22);
|
||||
assert.strictEqual(obj2.plusOne(), 23);
|
||||
})();
|
||||
await common.gcUntil('test 2', () => (test.finalizeCount === 2));
|
||||
await gcUntil('test 2', () => (test.finalizeCount === 2));
|
||||
}
|
||||
runGCTests();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
const common = require('../../common');
|
||||
const assert = require('assert');
|
||||
const addon = require(`./build/${common.buildType}/8_passing_wrapped`);
|
||||
const { gcUntil } = require('../../common/gc');
|
||||
|
||||
async function runTest() {
|
||||
let obj1 = addon.createObject(10);
|
||||
|
@ -14,7 +15,7 @@ async function runTest() {
|
|||
// Make sure the native destructor gets called.
|
||||
obj1 = null;
|
||||
obj2 = null;
|
||||
await common.gcUntil('8_passing_wrapped',
|
||||
() => (addon.finalizeCount() === 2));
|
||||
await gcUntil('8_passing_wrapped',
|
||||
() => (addon.finalizeCount() === 2));
|
||||
}
|
||||
runTest();
|
||||
|
|
|
@ -5,8 +5,10 @@ const common = require('../../common');
|
|||
const test_finalizer = require(`./build/${common.buildType}/test_finalizer`);
|
||||
const assert = require('assert');
|
||||
|
||||
const { gcUntil } = require('../../common/gc');
|
||||
|
||||
// The goal of this test is to show that we can run "pure" finalizers in the
|
||||
// current JS loop tick. Thus, we do not use common.gcUntil function works
|
||||
// current JS loop tick. Thus, we do not use gcUntil function works
|
||||
// asynchronously using micro tasks.
|
||||
// We use IIFE for the obj scope instead of {} to be compatible with
|
||||
// non-V8 JS engines that do not support scoped variables.
|
||||
|
@ -25,7 +27,7 @@ for (let i = 0; i < 10; ++i) {
|
|||
assert.strictEqual(test_finalizer.getFinalizerCallCount(), 1);
|
||||
|
||||
// The finalizer that access JS cannot run synchronously. They are run in the
|
||||
// next JS loop tick. Thus, we must use common.gcUntil.
|
||||
// next JS loop tick. Thus, we must use gcUntil.
|
||||
async function runAsyncTests() {
|
||||
// We do not use common.mustCall() because we want to see the finalizer
|
||||
// called in response to GC and not as a part of env destruction.
|
||||
|
@ -36,8 +38,8 @@ async function runAsyncTests() {
|
|||
const obj = {};
|
||||
test_finalizer.addFinalizerWithJS(obj, () => { js_is_called = true; });
|
||||
})();
|
||||
await common.gcUntil('ensure JS finalizer called',
|
||||
() => (test_finalizer.getFinalizerCallCount() === 2));
|
||||
await gcUntil('ensure JS finalizer called',
|
||||
() => (test_finalizer.getFinalizerCallCount() === 2));
|
||||
assert(js_is_called);
|
||||
}
|
||||
runAsyncTests();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
const common = require('../../common');
|
||||
const test_general = require(`./build/${common.buildType}/test_general`);
|
||||
const assert = require('assert');
|
||||
const { gcUntil } = require('../../common/gc');
|
||||
|
||||
const val1 = '1';
|
||||
const val2 = 1;
|
||||
|
@ -79,9 +80,9 @@ async function runGCTests() {
|
|||
assert.strictEqual(test_general.derefItemWasCalled(), false);
|
||||
|
||||
(() => test_general.wrap({}))();
|
||||
await common.gcUntil('deref_item() was called upon garbage collecting a ' +
|
||||
await gcUntil('deref_item() was called upon garbage collecting a ' +
|
||||
'wrapped object.',
|
||||
() => test_general.derefItemWasCalled());
|
||||
() => test_general.derefItemWasCalled());
|
||||
|
||||
// Ensure that removing a wrap and garbage collecting does not fire the
|
||||
// finalize callback.
|
||||
|
@ -89,7 +90,7 @@ async function runGCTests() {
|
|||
test_general.testFinalizeWrap(z);
|
||||
test_general.removeWrap(z);
|
||||
z = null;
|
||||
await common.gcUntil(
|
||||
await gcUntil(
|
||||
'finalize callback was not called upon garbage collection.',
|
||||
() => (!test_general.finalizeWasCalled()));
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
const common = require('../../common');
|
||||
const test_general = require(`./build/${common.buildType}/test_general`);
|
||||
const assert = require('assert');
|
||||
const { gcUntil } = require('../../common/gc');
|
||||
|
||||
let finalized = {};
|
||||
const callback = common.mustCall(2);
|
||||
|
@ -30,7 +31,7 @@ async function testFinalizeAndWrap() {
|
|||
test_general.wrap(finalizeAndWrap);
|
||||
test_general.addFinalizerOnly(finalizeAndWrap, common.mustCall());
|
||||
finalizeAndWrap = null;
|
||||
await common.gcUntil('test finalize and wrap',
|
||||
() => test_general.derefItemWasCalled());
|
||||
await gcUntil('test finalize and wrap',
|
||||
() => test_general.derefItemWasCalled());
|
||||
}
|
||||
testFinalizeAndWrap();
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
'use strict';
|
||||
// Flags: --expose-gc
|
||||
|
||||
const { gcUntil, buildType } = require('../../common');
|
||||
const { buildType } = require('../../common');
|
||||
const { gcUntil } = require('../../common/gc');
|
||||
const assert = require('assert');
|
||||
|
||||
const test_reference = require(`./build/${buildType}/test_reference`);
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
// and symbol types, while in newer versions they can be created for
|
||||
// any value type.
|
||||
//
|
||||
const { gcUntil, buildType } = require('../../common');
|
||||
const { buildType } = require('../../common');
|
||||
const { gcUntil } = require('../../common/gc');
|
||||
const assert = require('assert');
|
||||
const addon_v8 = require(`./build/${buildType}/test_reference_obj_only`);
|
||||
const addon_new = require(`./build/${buildType}/test_reference_all_types`);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
// Flags: --expose-gc
|
||||
const common = require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
|
||||
{
|
||||
onGC({}, { ongc: common.mustCall() });
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// Flags: --expose-gc
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const { gcUntil } = require('../common/gc');
|
||||
const assert = require('assert');
|
||||
const async_hooks = require('async_hooks');
|
||||
const domain = require('domain');
|
||||
|
@ -40,7 +41,7 @@ d.run(() => {
|
|||
d = null;
|
||||
|
||||
async function main() {
|
||||
await common.gcUntil(
|
||||
await gcUntil(
|
||||
'All objects garbage collected',
|
||||
() => resourceGCed && domainGCed && emitterGCed);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// but aborting every connection that comes in.
|
||||
|
||||
const common = require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const http = require('http');
|
||||
const os = require('os');
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// but using a net server/client instead
|
||||
|
||||
require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
const os = require('os');
|
||||
|
|
|
@ -9,7 +9,7 @@ if (!common.hasCrypto)
|
|||
common.skip('missing crypto');
|
||||
|
||||
const { duplexPair } = require('stream');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// Check that creating a server without listening does not leak resources.
|
||||
|
||||
require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
const http = require('http');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Flags: --expose-gc
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const { createServer } = require('http');
|
||||
const { connect } = require('net');
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ if (!common.hasCrypto) {
|
|||
common.skip('missing crypto');
|
||||
}
|
||||
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const Countdown = require('../common/countdown');
|
||||
|
||||
const https = require('https');
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Flags: --expose-internals --expose-gc
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
require('../common');
|
||||
const { gcUntil } = require('../common/gc');
|
||||
const assert = require('assert');
|
||||
const { WeakReference } = require('internal/util');
|
||||
|
||||
|
@ -10,7 +11,7 @@ assert.strictEqual(ref.get(), obj);
|
|||
|
||||
async function main() {
|
||||
obj = null;
|
||||
await common.gcUntil(
|
||||
await gcUntil(
|
||||
'Reference is garbage collected',
|
||||
() => ref.get() === undefined);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
// Flags: --expose-gc
|
||||
|
||||
const common = require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
// Flags: --expose-gc
|
||||
require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
|
||||
// See https://github.com/nodejs/node/issues/53335
|
||||
const poller = setInterval(() => {
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
require('../common');
|
||||
const { gcUntil } = require('../common/gc');
|
||||
const assert = require('node:assert');
|
||||
const { findSourceMap } = require('node:module');
|
||||
|
||||
|
@ -28,7 +29,7 @@ function run(moduleId) {
|
|||
run(moduleId);
|
||||
|
||||
// Run until the source map is cleared by GC, or fail the test after determined iterations.
|
||||
common.gcUntil('SourceMap of deleted CJS module is cleared', () => {
|
||||
gcUntil('SourceMap of deleted CJS module is cleared', () => {
|
||||
// Repetitively load a second module with --max-old-space-size=10 to make GC more aggressive.
|
||||
run(moduleIdRepeat);
|
||||
// Verify that the source map is cleared.
|
||||
|
|
|
@ -26,7 +26,7 @@ const common = require('../common');
|
|||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Flags: --expose-gc
|
||||
|
||||
const common = require('../common');
|
||||
const { gcUntil } = require('../common/gc');
|
||||
|
||||
// On IBMi, the rss memory always returns zero
|
||||
if (common.isIBMi)
|
||||
|
@ -16,7 +17,7 @@ for (let i = 0; i < 1000000; i++) {
|
|||
}
|
||||
|
||||
async function main() {
|
||||
await common.gcUntil('RSS should go down', () => {
|
||||
await gcUntil('RSS should go down', () => {
|
||||
const after = process.memoryUsage.rss();
|
||||
if (common.isASan) {
|
||||
console.log(`ASan: before=${before} after=${after}`);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Flags: --expose-gc
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const assert = require('assert');
|
||||
const zlib = require('zlib');
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// but with an on('error') handler that does nothing.
|
||||
|
||||
const common = require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
|
||||
const cpus = require('os').availableParallelism();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// Like test-gc-http-client.js, but with a timeout set.
|
||||
|
||||
const common = require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
const http = require('http');
|
||||
|
||||
function serverHandler(req, res) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// just a simple http server and client.
|
||||
|
||||
const common = require('../common');
|
||||
const onGC = require('../common/ongc');
|
||||
const { onGC } = require('../common/gc');
|
||||
|
||||
const cpus = require('os').availableParallelism();
|
||||
|
||||
|
|
Loading…
Reference in New Issue