Skip to content

Conversation

@cratelyn
Copy link
Contributor

@cratelyn cratelyn commented Feb 13, 2025

this commit introduces a new method to tokio::sync::oneshot::Receiver<T>.

broadly speaking, users of the oneshot channel are encouraged to .await the Receiver<T> directly, as it will only yield a single value.

users implementing their own std::future::Futures directly may instead poll the receiver via <Receiver<T> as Future<Output = Result<T, RecvError>::poll(..). note that the contract of Future::poll() states that clients should not poll a future after it has yielded Poll::Ready(value).

this commit provides a way to inspect the state of a receiver, to avoid violating the contact of Future::poll(..), or requiring that a oneshot channel users track this state themselves externally via mechanisms like futures::future::FusedFuture, or wrapping the receiver in an Option<T>.

NB: this makes a small behavioral change to the implementation of <Receiver<T> as Future<Output = Result<T, RecvError>::poll(..). this change is acceptable, per the Future::poll() documentation regarding panics:

Once a future has completed (returned Ready from poll), calling its
poll method again may panic, block forever, or cause other kinds of problems; the Future trait places no requirements on the effects of such a call.

the upside of this is that it means a broken or closed channel, e.g. when the sender is dropped, will settle as "finished" after it yields an error.

see:

@github-actions github-actions bot added the R-loom-sync Run loom sync tests on this PR label Feb 13, 2025
@cratelyn cratelyn marked this pull request as ready for review February 13, 2025 20:39
@cratelyn

This comment was marked as resolved.

@Darksonn Darksonn added A-tokio Area: The main tokio crate M-sync Module: tokio/sync labels Feb 14, 2025
cratelyn added a commit to cratelyn/tokio that referenced this pull request Feb 15, 2025
> You could also add a test for `is_terminated` before and after
> `try_recv` as well.

tokio-rs#7152 (comment)

Co-authored-by: M.Amin Rayej <[email protected]>
Signed-off-by: katelyn martin <[email protected]>
cratelyn and others added 2 commits February 15, 2025 14:38
this commit introduces a new method to
`tokio::sync::oneshot::Receiver<T>`.

broadly speaking, users of the oneshot channel are encouraged to
`.await` the `Receiver<T>` directly, as it will only yield a single
value.

users implementing their own `std::future::Future`s directly may instead
poll the receiver via
`<Receiver<T> as Future<Output = Result<T, RecvError>::poll(..)`.
note that the contract of `Future::poll()` states that clients should
not poll a future after it has yielded `Poll::Ready(value)`.

this commit provides a way to inspect the
state of a receiver, to avoid violating the contact of
`Future::poll(..)`, or requiring that a oneshot channel users track this
state themselves externally via mechanisms like
`futures::future::FusedFuture`, or wrapping the receiver in an
`Option<T>`.

NB: this makes a small behavioral change to the implementation of
`<Receiver<T> as Future<Output = Result<T, RecvError>::poll(..)`. this
change is acceptable, per the `Future::poll()` documentation regarding
panics:

> Once a future has completed (returned Ready from poll), calling its
poll method again may panic, block forever, or cause other kinds of
problems; the Future trait places no requirements on the effects of such
a call.

the upside of this is that it means a broken or closed channel, e.g.
when the sender is dropped, will settle as "finished" after it yields an
error.

see:
* tokio-rs#7137 (comment)
* https://doc.rust-lang.org/stable/std/future/trait.Future.html#panics

Signed-off-by: katelyn martin <[email protected]>
> You could also add a test for `is_terminated` before and after
> `try_recv` as well.

tokio-rs#7152 (comment)

Co-authored-by: M.Amin Rayej <[email protected]>
Signed-off-by: katelyn martin <[email protected]>
@cratelyn cratelyn force-pushed the sync-oneshot.is_terminated branch from 036c002 to 8fc0759 Compare February 15, 2025 19:38
cratelyn added a commit to cratelyn/tokio that referenced this pull request Feb 15, 2025
NB: this commit means that this branch (tokio-rs#7153) is dependent upon
interfaces proposed in (tokio-rs#7152).

pr tokio-rs#7152 proposes a `is_terminated()` method for the oneshot channel's
receiver. that is the proper method to use to check whether or not it is
safe to poll a oneshot receiver as a future.

this commit adds a note cross-referencing this method to the
documentation of `is_empty()`, to help mitigate misuse. this method only
reports whether a value is currently waiting in the channel; a channel
that has already received and yielded a value to its receiver is also
empty, but would panic when polled.

Signed-off-by: katelyn martin <[email protected]>
cratelyn added a commit to cratelyn/tokio that referenced this pull request Feb 15, 2025
NB: this commit means that this branch (tokio-rs#7153) is dependent upon
interfaces proposed in (tokio-rs#7152).

pr tokio-rs#7152 proposes a `is_terminated()` method for the oneshot channel's
receiver. that is the proper method to use to check whether or not it is
safe to poll a oneshot receiver as a future.

this commit adds a note cross-referencing this method to the
documentation of `is_empty()`, to help mitigate misuse. this method only
reports whether a value is currently waiting in the channel; a channel
that has already received and yielded a value to its receiver is also
empty, but would panic when polled.

Signed-off-by: katelyn martin <[email protected]>
@maminrayej
Copy link
Member

Thanks.

@maminrayej maminrayej merged commit 17117b5 into tokio-rs:master Feb 16, 2025
82 checks passed
cratelyn added a commit to cratelyn/tokio that referenced this pull request Feb 16, 2025
NB: this commit means that this branch (tokio-rs#7153) is dependent upon
interfaces proposed in (tokio-rs#7152).

pr tokio-rs#7152 proposes a `is_terminated()` method for the oneshot channel's
receiver. that is the proper method to use to check whether or not it is
safe to poll a oneshot receiver as a future.

this commit adds a note cross-referencing this method to the
documentation of `is_empty()`, to help mitigate misuse. this method only
reports whether a value is currently waiting in the channel; a channel
that has already received and yielded a value to its receiver is also
empty, but would panic when polled.

Signed-off-by: katelyn martin <[email protected]>
@cratelyn cratelyn deleted the sync-oneshot.is_terminated branch February 16, 2025 20:46
@rushilmehra
Copy link

Lol this started causing some panics in production for us. Misuse on our part but it was a tricky one to root cause

@mox692
Copy link
Member

mox692 commented Apr 5, 2025

@rushilmehra Can you describe it in a separate issue? I think this change is not supposed to affect the existing code.

@rushilmehra
Copy link

rushilmehra commented Apr 6, 2025

@rushilmehra Can you describe it in a separate issue? I think this change is not supposed to affect the existing code.

We were accidentally polling a oneshot receiver after it was complete, honestly just misuse on our side.

@kylezs
Copy link

kylezs commented Apr 17, 2025

Fortunately for us it only appeared as a flaky test 😄

Thanks for the nice PR description btw, helped finding the issue quickly :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-tokio Area: The main tokio crate M-sync Module: tokio/sync R-loom-sync Run loom sync tests on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants