Last active
July 14, 2021 18:10
-
-
Save timoxley/d03c0f37a45f77b8481858aa46f4c1c8 to your computer and use it in GitHub Desktop.
generator return deadlock
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
const assert = require('assert') | |
class Generators { | |
items = new Map() | |
add(id) { | |
const gen = generate(this, id) | |
this.items.set(id, gen) | |
return gen | |
} | |
has(id) { | |
return this.items.has(id) | |
} | |
async remove(id) { | |
console.log('remove >>', id) | |
const gen = this.items.get(id) | |
this.items.delete(id) | |
if (!gen) { return } | |
console.log('gen.return >>') | |
// itention: ensure generator is ended | |
// reality: code just stops here, node silently exits | |
await gen.return() | |
console.log('gen.return <<') // <= never runs | |
console.log('remove >>', id) // <= never runs | |
} | |
} | |
async function* generate(generators, id) { | |
try { | |
yield* [1, 2, 3] // whatever | |
} finally { | |
console.log('finally >>', id) | |
await generators.remove(id) // remove self from collection | |
console.log('finally <<', id) // <= never runs | |
} | |
} | |
async function start() { | |
const generators = new Generators() | |
const id = 'gen1' | |
const gen1 = generators.add(id) | |
assert.ok(generators.has(id)) | |
try { | |
console.log('loop >>') | |
for await (const item of gen1) { | |
console.log('item', item) // whaever | |
} | |
console.log('loop <<') // <= never runs | |
} finally { | |
console.log('outer finally') // <= never runs | |
// should have removed from generators (if line gets executed at all) | |
assert.ok(!generators.has(id)) | |
} | |
} | |
start().finally(() => { | |
console.log('start promise finally') // <= never runs | |
}) | |
/* | |
output: | |
loop >> | |
item 1 | |
item 2 | |
item 3 | |
finally >> gen1 | |
remove >> gen1 | |
gen.return >> | |
Then process silently exits with code 0. | |
*/ |
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
const assert = require('assert') | |
class Generators { | |
items = new Map() | |
add(id) { | |
const gen = generate() | |
const gen2 = (async function* Gen2() { | |
try { | |
yield* gen | |
} finally { | |
await this.remove(id) | |
} | |
}.call(this)) | |
this.items.set(id, gen) // store inner generator, we will call return on this in remove | |
return gen2 | |
} | |
has(id) { | |
return this.items.has(id) | |
} | |
async remove(id) { | |
console.log('remove >>', id) | |
const gen = this.items.get(id) | |
this.items.delete(id) | |
if (!gen) { return } | |
console.log('gen.return >>') | |
await gen.return() // ensure generator is ended | |
console.log('gen.return <<') | |
console.log('remove >>', id) | |
} | |
} | |
async function* generate() { | |
yield* [1, 2, 3] // whatever | |
} | |
async function start() { | |
const generators = new Generators() | |
const id = 'gen1' | |
const gen1 = generators.add(id) | |
assert.ok(generators.has(id)) | |
try { | |
console.log('loop >>') | |
for await (const item of gen1) { | |
console.log('item', item) // whaever | |
if (item === 2) { | |
await generators.remove(id) // short circuit | |
} | |
} | |
console.log('loop <<') | |
} finally { | |
console.log('outer finally') | |
// should have removed from generators (if line gets executed at all) | |
assert.ok(!generators.has(id)) | |
} | |
} | |
start().finally(() => { | |
console.log('start promise finally') | |
}) | |
/* | |
output: | |
lloop >> | |
item 1 | |
item 2 | |
remove >> gen1 | |
gen.return >> | |
gen.return << | |
remove >> gen1 | |
loop << | |
outer finally | |
start promise finally | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment