|
1 | 1 | 'use strict'; |
2 | 2 |
|
3 | 3 | constwait=require('timers/promises').setTimeout; |
| 4 | +constassert=require('assert'); |
| 5 | +constcommon=require('../common'); |
| 6 | +constgcTrackerMap=newWeakMap(); |
| 7 | +constgcTrackerTag='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 | +functiononGC(obj,gcListener){ |
| 18 | +constasync_hooks=require('async_hooks'); |
| 19 | + |
| 20 | +constonGcAsyncHook=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,newasync_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 | +functiongcUntil(name,condition){ |
| 48 | +if(typeofname==='function'){ |
| 49 | +condition=name; |
| 50 | +name=undefined; |
| 51 | +} |
| 52 | +returnnewPromise((resolve,reject)=>{ |
| 53 | +letcount=0; |
| 54 | +functiongcAndCheck(){ |
| 55 | +setImmediate(()=>{ |
| 56 | +count++; |
| 57 | +global.gc(); |
| 58 | +if(condition()){ |
| 59 | +resolve(); |
| 60 | +}elseif(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 | }; |
0 commit comments