| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,7 +3,7 @@ | |||
| 3 | 3 | // Flags: --expose-gc | |
| 4 | 4 | ||
| 5 | 5 | const common = require('../../common'); | |
| 6 | - | ||
| 6 | + const { gcUntil } = require('../../common/gc'); | ||
| 7 | 7 | // Verify that addons can create GarbageCollected objects and | |
| 8 | 8 | // have them traced properly. | |
| 9 | 9 | ||
@@ -35,7 +35,7 @@ setTimeout(async function() { | |||
| 35 | 35 | for (let i = 0; i < count; ++i) { | |
| 36 | 36 | array[i] = new CppGCed(); | |
| 37 | 37 | } | |
| 38 | - await common.gcUntil( | ||
| 38 | + await gcUntil( | ||
| 39 | 39 | 'All old CppGCed are destroyed', | |
| 40 | 40 | () => states[kDestructCount] === count, | |
| 41 | 41 | ); | |
@@ -44,7 +44,7 @@ setTimeout(async function() { | |||
| 44 | 44 | array = null; | |
| 45 | 45 | globalThis.gc(); | |
| 46 | 46 | ||
| 47 | - await common.gcUntil( | ||
| 47 | + await gcUntil( | ||
| 48 | 48 | 'All old CppGCed are destroyed', | |
| 49 | 49 | () => states[kDestructCount] === count * 2, | |
| 50 | 50 | ); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | 7 | const common = require('../common'); | |
| 8 | 8 | const { AsyncLocalStorage } = require('async_hooks'); | |
| 9 | - const onGC = require('../common/ongc'); | ||
| 9 | + const { onGC } = require('../common/gc'); | ||
| 10 | 10 | ||
| 11 | 11 | let asyncLocalStorage = new AsyncLocalStorage(); | |
| 12 | 12 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -982,7 +982,7 @@ module exports a single `onGC()` function. | |||
| 982 | 982 | ||
| 983 | 983 | ```js | |
| 984 | 984 | require('../common'); | |
| 985 | - const onGC = require('../common/ongc'); | ||
| 985 | + const { onGC } = require('../common/gc'); | ||
| 986 | 986 | ||
| 987 | 987 | onGC({}, { ongc() { console.log('collected'); } }); | |
| 988 | 988 | ``` | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,9 +1,72 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | ||
| 3 | 3 | const wait = require('timers/promises').setTimeout; | |
| 4 | + const assert = require('assert'); | ||
| 5 | + const common = require('../common'); | ||
| 6 | + const gcTrackerMap = new WeakMap(); | ||
| 7 | + const gcTrackerTag = 'NODE_TEST_COMMON_GC_TRACKER'; | ||
| 4 | 8 | ||
| 5 | - // TODO(joyeecheung): merge ongc.js and gcUntil from common/index.js | ||
| 6 | - // into this. | ||
| 9 | + /** | ||
| 10 | + * Installs a garbage collection listener for the specified object. | ||
| 11 | + * Uses async_hooks for GC tracking, which may affect test functionality. | ||
| 12 | + * A full setImmediate() invocation passes between a global.gc() call and the listener being invoked. | ||
| 13 | + * @param {object} obj - The target object to track for garbage collection. | ||
| 14 | + * @param {object} gcListener - The listener object containing the ongc callback. | ||
| 15 | + * @param {Function} gcListener.ongc - The function to call when the target object is garbage collected. | ||
| 16 | + */ | ||
| 17 | + function onGC(obj, gcListener) { | ||
| 18 | + const async_hooks = require('async_hooks'); | ||
| 19 | + | ||
| 20 | + const onGcAsyncHook = async_hooks.createHook({ | ||
| 21 | + init: common.mustCallAtLeast(function(id, type) { | ||
| 22 | + if (this.trackedId === undefined) { | ||
| 23 | + assert.strictEqual(type, gcTrackerTag); | ||
| 24 | + this.trackedId = id; | ||
| 25 | + } | ||
| 26 | + }), | ||
| 27 | + destroy(id) { | ||
| 28 | + assert.notStrictEqual(this.trackedId, -1); | ||
| 29 | + if (id === this.trackedId) { | ||
| 30 | + this.gcListener.ongc(); | ||
| 31 | + onGcAsyncHook.disable(); | ||
| 32 | + } | ||
| 33 | + }, | ||
| 34 | + }).enable(); | ||
| 35 | + onGcAsyncHook.gcListener = gcListener; | ||
| 36 | + | ||
| 37 | + gcTrackerMap.set(obj, new async_hooks.AsyncResource(gcTrackerTag)); | ||
| 38 | + obj = null; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * Repeatedly triggers garbage collection until a specified condition is met or a maximum number of attempts is reached. | ||
| 43 | + * @param {string|Function} [name] - Optional name, used in the rejection message if the condition is not met. | ||
| 44 | + * @param {Function} condition - A function that returns true when the desired condition is met. | ||
| 45 | + * @returns {Promise} A promise that resolves when the condition is met, or rejects after 10 failed attempts. | ||
| 46 | + */ | ||
| 47 | + function gcUntil(name, condition) { | ||
| 48 | + if (typeof name === 'function') { | ||
| 49 | + condition = name; | ||
| 50 | + name = undefined; | ||
| 51 | + } | ||
| 52 | + return new Promise((resolve, reject) => { | ||
| 53 | + let count = 0; | ||
| 54 | + function gcAndCheck() { | ||
| 55 | + setImmediate(() => { | ||
| 56 | + count++; | ||
| 57 | + global.gc(); | ||
| 58 | + if (condition()) { | ||
| 59 | + resolve(); | ||
| 60 | + } else if (count < 10) { | ||
| 61 | + gcAndCheck(); | ||
| 62 | + } else { | ||
| 63 | + reject(name === undefined ? undefined : 'Test ' + name + ' failed'); | ||
| 64 | + } | ||
| 65 | + }); | ||
| 66 | + } | ||
| 67 | + gcAndCheck(); | ||
| 68 | + }); | ||
| 69 | + } | ||
| 7 | 70 | ||
| 8 | 71 | // This function can be used to check if an object factor leaks or not, | |
| 9 | 72 | // but it needs to be used with care: | |
@@ -124,4 +187,6 @@ module.exports = { | |||
| 124 | 187 | checkIfCollectable, | |
| 125 | 188 | runAndBreathe, | |
| 126 | 189 | checkIfCollectableByCounting, | |
| 190 | + onGC, | ||
| 191 | + gcUntil, | ||
| 127 | 192 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -851,30 +851,6 @@ function skipIfDumbTerminal() { | |||
| 851 | 851 | } | |
| 852 | 852 | } | |
| 853 | 853 | ||
| 854 | - function gcUntil(name, condition) { | ||
| 855 | - if (typeof name === 'function') { | ||
| 856 | - condition = name; | ||
| 857 | - name = undefined; | ||
| 858 | - } | ||
| 859 | - return new Promise((resolve, reject) => { | ||
| 860 | - let count = 0; | ||
| 861 | - function gcAndCheck() { | ||
| 862 | - setImmediate(() => { | ||
| 863 | - count++; | ||
| 864 | - global.gc(); | ||
| 865 | - if (condition()) { | ||
| 866 | - resolve(); | ||
| 867 | - } else if (count < 10) { | ||
| 868 | - gcAndCheck(); | ||
| 869 | - } else { | ||
| 870 | - reject(name === undefined ? undefined : 'Test ' + name + ' failed'); | ||
| 871 | - } | ||
| 872 | - }); | ||
| 873 | - } | ||
| 874 | - gcAndCheck(); | ||
| 875 | - }); | ||
| 876 | - } | ||
| 877 | - | ||
| 878 | 854 | function requireNoPackageJSONAbove(dir = __dirname) { | |
| 879 | 855 | let possiblePackage = path.join(dir, '..', 'package.json'); | |
| 880 | 856 | let lastPackage = null; | |
@@ -985,7 +961,6 @@ const common = { | |||
| 985 | 961 | expectsError, | |
| 986 | 962 | expectRequiredModule, | |
| 987 | 963 | expectWarning, | |
| 988 | - gcUntil, | ||
| 989 | 964 | getArrayBufferViews, | |
| 990 | 965 | getBufferSources, | |
| 991 | 966 | getCallSite, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,11 +3,12 @@ | |||
| 3 | 3 | 'use strict'; | |
| 4 | 4 | const common = require('../../common'); | |
| 5 | 5 | const addon = require(`./build/${common.buildType}/6_object_wrap`); | |
| 6 | + const { gcUntil } = require('../../common/gc'); | ||
| 6 | 7 | ||
| 7 | 8 | (function scope() { | |
| 8 | 9 | addon.objectWrapDanglingReference({}); | |
| 9 | 10 | })(); | |
| 10 | 11 | ||
| 11 | - common.gcUntil('object-wrap-ref', () => { | ||
| 12 | + gcUntil('object-wrap-ref', () => { | ||
| 12 | 13 | return addon.objectWrapDanglingReferenceTest(); | |
| 13 | 14 | }); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,6 +4,7 @@ | |||
| 4 | 4 | const common = require('../../common'); | |
| 5 | 5 | const assert = require('assert'); | |
| 6 | 6 | const test = require(`./build/${common.buildType}/7_factory_wrap`); | |
| 7 | + const { gcUntil } = require('../../common/gc'); | ||
| 7 | 8 | ||
| 8 | 9 | assert.strictEqual(test.finalizeCount, 0); | |
| 9 | 10 | async function runGCTests() { | |
@@ -13,14 +14,14 @@ async function runGCTests() { | |||
| 13 | 14 | assert.strictEqual(obj.plusOne(), 12); | |
| 14 | 15 | assert.strictEqual(obj.plusOne(), 13); | |
| 15 | 16 | })(); | |
| 16 | - await common.gcUntil('test 1', () => (test.finalizeCount === 1)); | ||
| 17 | + await gcUntil('test 1', () => (test.finalizeCount === 1)); | ||
| 17 | 18 | ||
| 18 | 19 | (() => { | |
| 19 | 20 | const obj2 = test.createObject(20); | |
| 20 | 21 | assert.strictEqual(obj2.plusOne(), 21); | |
| 21 | 22 | assert.strictEqual(obj2.plusOne(), 22); | |
| 22 | 23 | assert.strictEqual(obj2.plusOne(), 23); | |
| 23 | 24 | })(); | |
| 24 | - await common.gcUntil('test 2', () => (test.finalizeCount === 2)); | ||
| 25 | + await gcUntil('test 2', () => (test.finalizeCount === 2)); | ||
| 25 | 26 | } | |
| 26 | 27 | runGCTests(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,6 +4,7 @@ | |||
| 4 | 4 | const common = require('../../common'); | |
| 5 | 5 | const assert = require('assert'); | |
| 6 | 6 | const addon = require(`./build/${common.buildType}/8_passing_wrapped`); | |
| 7 | + const { gcUntil } = require('../../common/gc'); | ||
| 7 | 8 | ||
| 8 | 9 | async function runTest() { | |
| 9 | 10 | let obj1 = addon.createObject(10); | |
@@ -14,7 +15,7 @@ async function runTest() { | |||
| 14 | 15 | // Make sure the native destructor gets called. | |
| 15 | 16 | obj1 = null; | |
| 16 | 17 | obj2 = null; | |
| 17 | - await common.gcUntil('8_passing_wrapped', | ||
| 18 | - () => (addon.finalizeCount() === 2)); | ||
| 18 | + await gcUntil('8_passing_wrapped', | ||
| 19 | + () => (addon.finalizeCount() === 2)); | ||
| 19 | 20 | } | |
| 20 | 21 | runTest(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,8 +5,10 @@ const common = require('../../common'); | |||
| 5 | 5 | const test_finalizer = require(`./build/${common.buildType}/test_finalizer`); | |
| 6 | 6 | const assert = require('assert'); | |
| 7 | 7 | ||
| 8 | + const { gcUntil } = require('../../common/gc'); | ||
| 9 | + | ||
| 8 | 10 | // The goal of this test is to show that we can run "pure" finalizers in the | |
| 9 | - // current JS loop tick. Thus, we do not use common.gcUntil function works | ||
| 11 | + // current JS loop tick. Thus, we do not use gcUntil function works | ||
| 10 | 12 | // asynchronously using micro tasks. | |
| 11 | 13 | // We use IIFE for the obj scope instead of {} to be compatible with | |
| 12 | 14 | // non-V8 JS engines that do not support scoped variables. | |
@@ -25,7 +27,7 @@ for (let i = 0; i < 10; ++i) { | |||
| 25 | 27 | assert.strictEqual(test_finalizer.getFinalizerCallCount(), 1); | |
| 26 | 28 | ||
| 27 | 29 | // The finalizer that access JS cannot run synchronously. They are run in the | |
| 28 | - // next JS loop tick. Thus, we must use common.gcUntil. | ||
| 30 | + // next JS loop tick. Thus, we must use gcUntil. | ||
| 29 | 31 | async function runAsyncTests() { | |
| 30 | 32 | // We do not use common.mustCall() because we want to see the finalizer | |
| 31 | 33 | // called in response to GC and not as a part of env destruction. | |
@@ -36,8 +38,8 @@ async function runAsyncTests() { | |||
| 36 | 38 | const obj = {}; | |
| 37 | 39 | test_finalizer.addFinalizerWithJS(obj, () => { js_is_called = true; }); | |
| 38 | 40 | })(); | |
| 39 | - await common.gcUntil('ensure JS finalizer called', | ||
| 40 | - () => (test_finalizer.getFinalizerCallCount() === 2)); | ||
| 41 | + await gcUntil('ensure JS finalizer called', | ||
| 42 | + () => (test_finalizer.getFinalizerCallCount() === 2)); | ||
| 41 | 43 | assert(js_is_called); | |
| 42 | 44 | } | |
| 43 | 45 | runAsyncTests(); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments