Skip to content

Instantly share code, notes, and snippets.

@timoxley
Last active July 14, 2021 18:10
Show Gist options
  • Save timoxley/d03c0f37a45f77b8481858aa46f4c1c8 to your computer and use it in GitHub Desktop.
Save timoxley/d03c0f37a45f77b8481858aa46f4c1c8 to your computer and use it in GitHub Desktop.
generator return deadlock
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.
*/
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