-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow aborting an ongoing pipe operation using AbortSignals #744
Conversation
index.bs
Outdated
@@ -700,6 +718,8 @@ ReadableStream(<var>underlyingSource</var> = {}, { <var>size</var>, <var>highWat | |||
an error _error_, which means to perform the following steps: | |||
1. Perform ! WritableStreamDefaultWriterRelease(_writer_). | |||
1. Perform ! ReadableStreamReaderGenericRelease(_reader_). | |||
1. If _signal_ is not *undefined*, remove the above algorithm from _signal_'s <a for="AbortSignal">abort | |||
algorithms</a>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe stuff the algorithm in a variable?
Text changes roughly lgtm. I'm not sure about the name "signal" for the option. I'm asking myself "would I remember that?" and I don't know the answer. Generally the AbortController / AbortSignal design feels like it has a lot of cognitive overhead but I haven't come up with anything better. Compared to that, having two slightly different concepts called "abort" doesn't bother me. |
The main reason we called it "signal" is that we might want to signal things other than abort too. And for Fetch at least the idea was those signals would all be on the same object. |
@bakulf @tschneidereit any chance on implementer support from Firefox on this? Although this is less important than fetch() cancelation, it's also simpler, so we were thinking of using it as the first motivating case for AbortSignal in Blink at least. I realize Firefox doesn't have writable streams / pipeTo yet, so it might be hard for you to judge fully, but I'm hopeful the spec is straightforward enough to get multi-implementer support anyway :) |
Gecko implementation bug for abort things in general: https://bugzilla.mozilla.org/show_bug.cgi?id=1378342. I suspect we want this eventually, but it's highly likely we'll get |
@domenic apologies for the late reply. I still haven't implemented The changes here look quite straight-forward to me, though, so I'm happy to give support for them, with the condition that @annevk is happy with the implications for |
As I understand it there won't be any implications for |
Sorry, that was confusing. What I meant was "as long as this satisfies use cases of specs that use streams, such as fetch, I'm happy to give support." That seems to be the case, but since I'm not familiar enough with the fetch spec to be quite certain on my own I wanted to flag this one more time. |
Yeah, fetch cancelability should be pretty independent of this. Sounds like we're good, once the DOM PR is merged! |
The DOM PR has merged. |
Yep, just gotta write some tests. |
Still lgtm. Do you want me to try adding it to the reference implementation and write some tests? It looks like jsdom already has AbortController, so it should be pretty easy. |
@ricea I strongly suspect that'd be much appreciated! (Rebased the PR.) |
Yes please :). Sorry for letting this languish. |
index.bs
Outdated
</div> | ||
|
||
<emu-alg> | ||
1. If ! IsReadableStream(*this*) is *false*, return <a>a promise rejected with</a> a *TypeError* exception. | ||
1. If ! IsWritableStream(_dest_) is *false*, return <a>a promise rejected with</a> a *TypeError* exception. | ||
1. Set _preventClose_ to ! ToBoolean(_preventClose_), set _preventAbort_ to ! ToBoolean(_preventAbort_), and set | ||
_preventCancel_ to ! ToBoolean(_preventCancel_). | ||
1. If _signal_ is not *undefined*, and _signal_ is not an instance of the {{AbortSignal}} interface, return <a>a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm concerned that this condition may not be strong enough to guarantee we can execute the add algorithm.
Maybe we could use language similar to WebIDL, for example "and signal is not a platform object or does not implement AbortSignal, then ..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The language above is similar to what we use elsewhere though (for "brand" checks), but arguably none of that is precise enough (there's an issue against IDL to define a [[PlatformBrand]] internal slot we could use to formally define these things).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think this is the right thing for now.
index.bs
Outdated
WritableStreamAbort(_dest_, _error_) to _actions_. | ||
1. If _preventCancel_ is *false*, <a for="set">append</a> the action of performing ! | ||
ReadableStreamCancel(*this*, _error_) to _actions_. | ||
1. <a href="#rs-pipeTo-shutdown-with-action">Shutdown with an action</a> consisting of <a>waiting for all</a> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The waiting for all algorithm is problematic because it uses Promise.all()
, which is very sensitive to changes to the global object.
We need to do something else, or change the definition of "waiting for all" in the promises guide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the promises guide is known to be broken and needs to be uplifted into IDL. But nobody has done the work...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Should we consider this blocked on fixing "waiting for all", or is it OK to continue using it in a "do what I mean, not what I say" fashion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I'm concerned we shouldn't block this on that, since there's presumably other dependencies that are similarly broken already? (I don't find it particularly great, but we seem to have no trouble adding dependencies on broken/unclear primitives all over the platform.)
I noticed a couple more issues while writing tests:
If we figure out the answer to the second question then I can solve the first. |
From the point of view of the standard text (and probably the implementation), it's neater to check the signal at the beginning before looking at the state of the streams. I think this behaviour is logical, so unless anyone objects, I'll go ahead and implement it. |
That is what https://fetch.spec.whatwg.org/#dom-global-fetch does too, if I understand things correctly. We might need to document that pattern somehow more clearly so it's adopted by everyone consistently. |
I misread it when I was implementing it. It does indeed reject. |
Add tests for aborting ReadableStream.prototype.pipeTo using an AbortSignal. Tests correspond to PR whatwg/streams#744.
By bundling together the reference implementation using browserify, then evaluating it inside the wpt-runner window context, we fix all the cross-realm issues that have plagued us. This includes promise assimilation (which we had a workaround in place for), as well as Object.prototype identity (came up when testing async iterators) and the presence of DOMException and AbortSignal on the global (came up when testing #744).
By bundling together the reference implementation using browserify, then evaluating it inside the wpt-runner window context, we fix all the cross-realm issues that have plagued us. This includes promise assimilation (which we had a workaround in place for), as well as Object.prototype identity (came up when testing async iterators) and the presence of DOMException and AbortSignal on the global (came up when testing #744).
The reference implementation now supports the "signal" option to pipeTo(). It has diverged from the standard text, which will be updated separately. Tests are at web-platform-tests/wpt#13358.
The test for AbortError now also checks constructor.name, so fake that as well.
Also update the web-platform-tests pointer.
Previously, the reference implementation left _abortAlgorithm_ on _signal_ after pipeTo() exited. Remove it.
Reflect the fixes to the algorithms made in the reference implementation back into the standard. In particular, in order to correctly handle signals that are already aborted, _abortAlgorithm_ is defined earlier so that it can be called synchronously if necessary, and calls to WritableStreamAbort and ReadableStreamCancel are guarded by state checks.
I think this is ready to land. PTAL. |
Add tests for aborting ReadableStream.prototype.pipeTo using an AbortSignal. Tests correspond to PR whatwg/streams#744.
Filed https://bugs.chromium.org/p/chromium/issues/detail?id=902939. My understanding is other browsers don't have a pipeTo() implementation yet, so I didn't file bugs there, but let me know if folks want them. |
…), a=testonly Automatic update from web-platform-tests[streams] Add tests for aborting pipeTo() Add tests for aborting ReadableStream.prototype.pipeTo using an AbortSignal. Tests correspond to PR whatwg/streams#744. -- wpt-commits: 349d41838068164e37a1cbd1c8c6ffeba4c72177 wpt-pr: 13358
…), a=testonly Automatic update from web-platform-tests[streams] Add tests for aborting pipeTo() Add tests for aborting ReadableStream.prototype.pipeTo using an AbortSignal. Tests correspond to PR whatwg/streams#744. -- wpt-commits: 349d41838068164e37a1cbd1c8c6ffeba4c72177 wpt-pr: 13358
…), a=testonly Automatic update from web-platform-tests[streams] Add tests for aborting pipeTo() Add tests for aborting ReadableStream.prototype.pipeTo using an AbortSignal. Tests correspond to PR whatwg/streams#744. -- wpt-commits: 349d41838068164e37a1cbd1c8c6ffeba4c72177 wpt-pr: 13358 UltraBlame original commit: d3eedf1cb5de6c78bdd8060836a53e1ab1e0574c
…), a=testonly Automatic update from web-platform-tests[streams] Add tests for aborting pipeTo() Add tests for aborting ReadableStream.prototype.pipeTo using an AbortSignal. Tests correspond to PR whatwg/streams#744. -- wpt-commits: 349d41838068164e37a1cbd1c8c6ffeba4c72177 wpt-pr: 13358 UltraBlame original commit: d3eedf1cb5de6c78bdd8060836a53e1ab1e0574c
…), a=testonly Automatic update from web-platform-tests[streams] Add tests for aborting pipeTo() Add tests for aborting ReadableStream.prototype.pipeTo using an AbortSignal. Tests correspond to PR whatwg/streams#744. -- wpt-commits: 349d41838068164e37a1cbd1c8c6ffeba4c72177 wpt-pr: 13358 UltraBlame original commit: d3eedf1cb5de6c78bdd8060836a53e1ab1e0574c
Closes #446.
Blocked on whatwg/dom#437 finishing up, but it was important to write this up as a proof of concept; it shows that the AbortSignal design is good for our needs.
There's a bit of a namespace collision in how streams has chosen writer.abort() to mean one thing which is a bit different from signal.abort() on a pipe. Maybe we should put a bit more about that in the developer-facing domintro boxes. But I don't think it's avoidable.
/cc @annevk
Preview | Diff