Skip to content
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

Merged
merged 11 commits into from
Nov 7, 2018

Conversation

domenic
Copy link
Member

@domenic domenic commented May 15, 2017

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

@domenic domenic added blocked do not merge yet Pull request must not be merged per rationale in comment piping labels May 15, 2017
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>.
Copy link
Member

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?

@ricea
Copy link
Collaborator

ricea commented Jun 2, 2017

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.

@annevk
Copy link
Member

annevk commented Jun 22, 2017

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.

@domenic
Copy link
Member Author

domenic commented Jul 5, 2017

@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 :)

@annevk
Copy link
Member

annevk commented Jul 5, 2017

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 fetch() done first.

@tschneidereit
Copy link
Contributor

@domenic apologies for the late reply. I still haven't implemented pipeTo, but I do have WritableStream implemented (but not yet published, @bakulf and me are focusing on RS right now).

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 fetch.

@annevk
Copy link
Member

annevk commented Jul 13, 2017

As I understand it there won't be any implications for fetch() as it only uses ReadableStream. Maybe I'm missing something though.

@tschneidereit
Copy link
Contributor

As I understand it there won't be any implications for fetch() as it only uses ReadableStream. Maybe I'm missing something though.

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.

@domenic
Copy link
Member Author

domenic commented Jul 13, 2017

Yeah, fetch cancelability should be pretty independent of this. Sounds like we're good, once the DOM PR is merged!

@annevk annevk removed the blocked label Jul 17, 2017
@annevk
Copy link
Member

annevk commented Jul 17, 2017

The DOM PR has merged.

@domenic
Copy link
Member Author

domenic commented Jul 17, 2017

Yep, just gotta write some tests.

@ricea
Copy link
Collaborator

ricea commented Sep 6, 2018

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.

@annevk
Copy link
Member

annevk commented Sep 6, 2018

@ricea I strongly suspect that'd be much appreciated! (Rebased the PR.)

@domenic
Copy link
Member Author

domenic commented Sep 6, 2018

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
Copy link
Collaborator

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 ..."

Copy link
Member

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).

Copy link
Member Author

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>
Copy link
Collaborator

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.

Copy link
Member

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...

Copy link
Member Author

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?

Copy link
Member

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.)

@ricea
Copy link
Collaborator

ricea commented Sep 27, 2018

I noticed a couple more issues while writing tests:

  • It doesn't work if the AbortSignal is already signalled when pipeTo() is called.
  • What happens when there are other shutdown conditions is not defined; eg. calling rs.pipeTo(ws, { signal }) where rs is already closed and signal is already signalled.

If we figure out the answer to the second question then I can solve the first.

@ricea
Copy link
Collaborator

ricea commented Oct 1, 2018

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.

@annevk
Copy link
Member

annevk commented Oct 1, 2018

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.

@ricea
Copy link
Collaborator

ricea commented Oct 4, 2018

I probably should have noticed this before, but when pipeTo() is aborted, the promise is fulfilled with an AbortError. I think it should be rejected instead, to match the behaviour of fetch().

I misread it when I was implementing it. It does indeed reject.

ricea added a commit to ricea/web-platform-tests that referenced this pull request Oct 4, 2018
Add tests for aborting ReadableStream.prototype.pipeTo using an
AbortSignal.

Tests correspond to PR whatwg/streams#744.
domenic added a commit that referenced this pull request Oct 11, 2018
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).
domenic added a commit that referenced this pull request Oct 12, 2018
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).
domenic and others added 5 commits October 19, 2018 23:29
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.
ricea added 5 commits October 19, 2018 23:58
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.
@ricea ricea removed the do not merge yet Pull request must not be merged per rationale in comment label Oct 25, 2018
@ricea
Copy link
Collaborator

ricea commented Oct 25, 2018

I think this is ready to land. PTAL.

domenic pushed a commit to web-platform-tests/wpt that referenced this pull request Nov 7, 2018
Add tests for aborting ReadableStream.prototype.pipeTo using an
AbortSignal.

Tests correspond to PR whatwg/streams#744.
@domenic domenic merged commit 46c3b89 into master Nov 7, 2018
@domenic domenic deleted the abortsignals branch November 7, 2018 22:14
@domenic
Copy link
Member Author

domenic commented Nov 7, 2018

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.

moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this pull request Nov 14, 2018
…), 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
staktrace pushed a commit to staktrace/gecko that referenced this pull request Nov 15, 2018
…), 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
gecko-dev-updater pushed a commit to marco-c/gecko-dev-comments-removed that referenced this pull request Oct 3, 2019
…), 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
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified that referenced this pull request Oct 3, 2019
…), 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
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified-and-comments-removed that referenced this pull request Oct 3, 2019
…), 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

4 participants