-
-
Notifications
You must be signed in to change notification settings - Fork 32.6k
Description
There is a bit of undefined grey area when passing a Buffer
instance into a stream.Writable
: who has ownership responsibility for the Buffer
?
See this example as reference: https://github.com/jasnell/piscina/pull/34/files#diff-123d6328fe4f2579686ee49111e38427
In the example, I create a stream.Duplex
instance that wraps a MessagePort
. On _write
, I post the given Buffer
to the MessagePort
and include it in the transfer list to avoid copying the data:
_write (chunk, encoding, callback) {
if (typeof chunk === 'string') {
chunk = Buffer.from(chunk, encoding);
}
this.#port.postMessage(chunk, [chunk.buffer]);
callback();
}
This is where it gets fun. In my example, from within a worker thread, I open a pipeline that reads from a file, passes that into a gzip compressor, then out to my custom duplex. What happens is a fun little Abort...
james@ubuntu:~/nearform/piscina/examples/stream$ node index
node[30456]: ../src/node_zlib.cc:323:static void node::{anonymous}::CompressionStream<CompressionContext>::Write(const v8::FunctionCallbackInfo<v8::Value>&) [with bool async = true; CompressionContext = node::{anonymous}::ZlibContext]: Assertion `Buffer::IsWithinBounds(out_off, out_len, Buffer::Length(out_buf))' failed.
1: 0xa295b0 node::Abort() [node]
2: 0xa2962e [node]
3: 0xae6b1a [node]
4: 0xc0251b [node]
5: 0xc03ac6 [node]
6: 0xc04146 v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [node]
2
7: 0x13a5919 [node]
Aborted (core dumped)
The reason for the Abort is that my custom Duplex
just transferred ownership of the Buffer
away to the main thread while the gzip compressor was still using it.
Replace the gzip compressor with the brotli compressor, and things work! Change the postMessage()
call to remove the transferList and things work!
Obviously the Abort itself is problematic and we need to figure out how to avoid that in the zlib, so that's issue #1.... issue #2 is that we really should try to specify some ownership expectations on Buffer
instances passed into streams instances.