Closed
Description
- Version: 14.8.0
- Platform: MacOS
- Subsystem: child_process
What steps will reproduce the bug?
I start a child process using fork()
and { serialization: 'advanced' }
. In the child process I synchronously call process.send()
until it returns false
. I keep process.channel
referenced until all my send()
callbacks have been called. Every so often, the main process crashes. Other times it exits gracefully, though it does not log all received messages. Presumably because the child process exits without flushing its IPC buffer. That's manageable and probably not a bug.
main.js
:
const childProcess = require('child_process')
const channel = childProcess.fork('./child.js', [], { serialization: 'advanced' })
channel.on('message', message => {
console.error('main:', message.count)
})
child.js
:
// Keep the process alive until all messages have been sent.
process.channel.ref()
let pending = 0
const drain = () => {
if (--pending === 0) {
console.error('all messages sent')
if (!process.connected) {
console.error('already disconnected')
} else {
console.error('unref channel')
// Allow the process to exit.
process.channel.unref()
}
}
}
// Fill up any internal buffers.
const filler = Buffer.alloc(2 ** 12, 1)
// Enqueue as many messages as possible until we're told to back off.
let count = 0
let ok
do {
pending++
ok = process.send({count: ++count, filler}, drain)
console.error('child:', count, ok)
} while (ok)
And then run node main.js
.
How often does it reproduce? Is there a required condition?
It's intermittent.
What is the expected behavior?
The main process does not crash.
What do you see instead?
The main process crashes with the following error:
internal/child_process/serialization.js:69
deserializer.readHeader();
^
Error: Unable to deserialize cloned data due to invalid or unsupported version.
at parseChannelMessages (internal/child_process/serialization.js:69:20)
at parseChannelMessages.next (<anonymous>)
at Pipe.channel.onread (internal/child_process.js:595:18)
Additional information
If I modify child.process
to schedule sends using setImmediate()
there is no crash, and the main process receives all messages:
let count = 0
do {
pending++
setImmediate(() => {
process.send({count: ++count, filler}, drain)
console.error('child:', count)
})
} while (pending < 100)