Pin async iterator interleavings that follow WebIDL - #7475
Conversation
|
LGTM! |
guybedford
left a comment
There was a problem hiding this comment.
Traced all three shapes through the WebIDL next()/return() algorithm: fulfillSteps/rejectSteps null the ongoing promise before the outer next() settles, so a continuation registered on it runs nextSteps synchronously ahead of the chained calls, which gives exactly the TS pins (n3 'b'/n2 'c'; n2 'b' ahead of a queued return() that still cancels; done after a rejected next()). readable.ts's state.current + read-request clearing mirrors that structure. Ran //src/tests/streams/readable/... with --nocache_test_results (15/15) and confirmed the new test fails on both sides when the pin is flipped.
Two optional nits inline. One out-of-scope aside, unverified: in next() (readable.ts:564), when current is undefined and the read is served synchronously from the queue, chunk() clears current and the assignment then overwrites it with the already-resolved promise, so the following call chains a microtask later where WebIDL (ongoing = null) would run it synchronously. Not exercised here; noting in case it's worth a later pin.
7ebfaa2 to
2e87599
Compare
WebIDL clears an async iterator's ongoing promise whenever a next() settles. A next() from a continuation registered on an earlier next() therefore reads ahead of calls already queued, including a queued return(), and a next() after a rejected one reports done. TS follows the spec, as WPT async-iterator.any checks; C++ serializes every call. Readable ledger #22.
2e87599 to
3dbc959
Compare
WebIDL clears an async iterator's ongoing promise whenever a next() settles. A next() from a continuation registered on an earlier next() therefore reads ahead of calls already queued, including a queued return(), and a next() after a rejected one reports done. TS follows the spec, as WPT async-iterator.any checks; C++ serializes every call. Readable ledger #22.