You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The write algorithm copies the contents of the chunk before entering the "in parallel" steps. This doesn't match the behavior in Chromium which copies the chunk piecewise into an internal ring buffer as space becomes available. This means that if script modifies the contents of the BufferSource before the Promise returned by write() resolves the data written to the port will change. I think this behavior is correct but should be explicitly specified. The simplest change would be to move the invocation of the "get a copy of the buffer source" algorithm to after the steps go "in parallel". A more accurate description would be to acknowledge that a write may partially complete and so the steps must retry with unwritten pieces of the chunk. WebIDL doesn't seem to define an algorithm for converting a range within a BufferSource to a byte sequence.
Hmm. This is generally something we try to avoid in web API design, by either making copies or taking ownership of the entire buffer via transferring. See https://w3ctag.github.io/design-principles/#js-rtc . (At least, I assume what you're describing would violate run to completion, since within a single event loop task, the other thread might first read one version of the buffer, then read another after the JS has updated it.)
Since we don't yet have writable byte streams with ownership taking/returning semantics, we've been assuming people would make copies...
Outside the web platform it is a pretty common contract for asynchronous I/O functions to receive a buffer and expect the caller not to mutate that buffer until the operation is complete. I agree that copying or transferring the buffer on write() is safer but has the disadvantage of either performing extra copies or not allowing the caller to reuse the buffer. The current design of the write algorithm sounds like it is implementing a version what you are intending to explicitly support with ownership taking/returning semantics.
The documentation of the underlying sink API should probably include a note about the expectation that the chunk is copied if write() cannot complete synchronously.
Following up here because I want to make sure to provide the right guidance on this for other UnderlyingSink implementers. I'm okay with changing the behavior of the Web Serial API in Chromium if the intention is for Streams to eventually support explicitly transfering the buffer to the UnderlyingSink and later returning it to the caller. It looks like this is being discussed in whatwg/streams#495 and the summary mentions the current ambiguities.
Current Chromium still appears to have the behavior described here.
SerialPortUnderlyingSink::write() stores the original V8BufferSource, while WriteData() repeatedly creates a DOMArrayPiece over it and writes the remaining ByteSpan() into the Mojo pipe as capacity becomes available.
So bytes can still be observed incrementally rather than copied atomically when write() is called.
That seems worth either specifying explicitly or changing; right now implementation behavior and the safer copy-at-call-time model remain observably different.
Activity
domenic commentedon Jul 8, 2022
Hmm. This is generally something we try to avoid in web API design, by either making copies or taking ownership of the entire buffer via transferring. See https://w3ctag.github.io/design-principles/#js-rtc . (At least, I assume what you're describing would violate run to completion, since within a single event loop task, the other thread might first read one version of the buffer, then read another after the JS has updated it.)
Since we don't yet have writable byte streams with ownership taking/returning semantics, we've been assuming people would make copies...
reillyeon commentedon Jul 8, 2022
Outside the web platform it is a pretty common contract for asynchronous I/O functions to receive a buffer and expect the caller not to mutate that buffer until the operation is complete. I agree that copying or transferring the buffer on
write()is safer but has the disadvantage of either performing extra copies or not allowing the caller to reuse the buffer. The current design of the write algorithm sounds like it is implementing a version what you are intending to explicitly support with ownership taking/returning semantics.The documentation of the underlying sink API should probably include a note about the expectation that the chunk is copied if
write()cannot complete synchronously.reillyeon commentedon Jul 27, 2022
Following up here because I want to make sure to provide the right guidance on this for other
UnderlyingSinkimplementers. I'm okay with changing the behavior of the Web Serial API in Chromium if the intention is for Streams to eventually support explicitly transfering the buffer to theUnderlyingSinkand later returning it to the caller. It looks like this is being discussed in whatwg/streams#495 and the summary mentions the current ambiguities.Add a warning about async processing of written chunks
Warn about async processing of written/transformed chunks
Discuss async/parallel processing of written/transformed chunks
kvnloo commentedon Oct 1, 2026
Current Chromium still appears to have the behavior described here.
SerialPortUnderlyingSink::write()stores the originalV8BufferSource, whileWriteData()repeatedly creates aDOMArrayPieceover it and writes the remainingByteSpan()into the Mojo pipe as capacity becomes available.So bytes can still be observed incrementally rather than copied atomically when
write()is called.That seems worth either specifying explicitly or changing; right now implementation behavior and the safer copy-at-call-time model remain observably different.