Created
August 31, 2019 06:47
-
-
Save Slayer95/1aed510b091dbacacbb3d4e61704a1a8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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