Skip to content

Instantly share code, notes, and snippets.

@Slayer95
Created August 31, 2019 06:47
Show Gist options
  • Save Slayer95/1aed510b091dbacacbb3d4e61704a1a8 to your computer and use it in GitHub Desktop.
Save Slayer95/1aed510b091dbacacbb3d4e61704a1a8 to your computer and use it in GitHub Desktop.
'use strict';
/* global gc */
const RESULT_OBJECT = new Array(128).fill(0).map(x => ~~(Math.random() * 128));
const STREAM_LENGTH = 10;
const SUITE_SIZE = 10000;
function deepCloneInner(obj) {
/* Some heavy sync operation */
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) return obj.map(prop => deepCloneInner(prop));
const clone = Object.create(Object.getPrototypeOf(obj));
for (const key of Object.keys(obj)) {
clone[key] = deepCloneInner(obj[key]);
}
return clone;
}
function deepCloneSync(obj) {
return deepCloneInner(obj);
}
async function deepClone(obj) {
return deepCloneInner(obj);
}
async function deepCloneNextTick(obj) {
return new Promise((resolve, reject) => {
process.nextTick(() => resolve(deepCloneInner(obj)));
});
}
async function deepCloneSetImmediate(obj) {
return new Promise((resolve, reject) => {
setImmediate(() => resolve(deepCloneInner(obj)));
});
}
async function deepCloneAwait(obj) {
await 'ayuwoki';
return deepCloneInner(obj);
}
const MY_STREAM = {
iterations: 0,
async read() {
if (++this.iterations % STREAM_LENGTH === 0) {
return null;
}
/*
* Using deepCloneSync() causes a leak of Promises in Node 10.15.3 (V8 6.8.275.32-node.51),
* but it doesn't leak in Node 8.15.1 (V8 6.2.414.75)
*
* Leak also confirmed (Increase SUITE_SIZE x10) in:
* node 12.0.0-nightly20190331bb98f27181, v8 7.4.288.13-node.13
* node 12.0.0-v8-canary201903313c649ecee6, v8 7.5.149-node.0
*
*/
return deepCloneSync(RESULT_OBJECT);
// return deepClone(RESULT_OBJECT);
// return deepCloneNextTick(RESULT_OBJECT);
// return deepCloneSetImmediate(RESULT_OBJECT);
// return deepCloneAwait(RESULT_OBJECT);
},
};
const ENV = {
iterations: 0,
getNextGameParameters() {
if (++this.iterations % SUITE_SIZE === 0) {
return null;
}
return this.iterations % 2 ? 'arg1' : 'arg2';
},
};
async function runGame(logOutput) {
let chunk;
let x = Promise.resolve();
// Changing this to chunk = await MY_STREAM.read() removes the leak.
while ((chunk = await Promise.race([MY_STREAM.read(), x]))) {
if (logOutput) console.log(chunk);
}
}
async function runSuite() {
let parameters;
let iterations = 0;
while ((parameters = ENV.getNextGameParameters())) {
await runGame().catch(err => {
console.error(`Error for parameters ${parameters}\n${err.stack}`);
});
// Uncommenting fixes the leak (Node.js only)
/*
if (iterations++ % 50 === 0) {
await new Promise(resolve => setImmediate(resolve));
}
//*/
}
}
(async () => {
await runSuite();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment