-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
sync: oneshot::Receiver::{is_empty, is_closed}
#7137
Closed
cratelyn
wants to merge
3
commits into
tokio-rs:master
from
cratelyn:sync-oneshot-receiver-is-complete
+298
−3
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Hmm, this feels somewhat different than the semantics of
is_closed
on other channels, which only returnstrue
when the other side has been dropped. On the other hand, there's a valid argument that this is just because those channels don't have the property that sending a value closes the channel. I'm not sure how I feel about this: I think it might be worth being consistent that "closed" means "dropped by the other side", but I'm open to being convinced otherwise.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.
Also, the
Sender::is_closed
method currently exists and only inspects the value of theCLOSED
bit:tokio/tokio/src/sync/oneshot.rs
Lines 719 to 751 in 0931406
Of course, that method can't ever be called if a value was sent, as doing so consumes the sender. so. Hm.
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'm not sure what is the least surprising here.
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.
this is a very interesting conundrum!
to begin by restating the problem directly: we are asking ourselves whether or
not
tokio::sync::oneshot::Receiver::is_closed()
should returntrue
whenthe corresponding
tokio::sync::oneshot::Sender
has been consumed by sendinga value.
let's consider the alternatives and how they would affect callers using them
to determine whether or not a receiver may be polled.
option a:
is_closed()
does not include completed channelin this model,
is_closed()
only returns true when the sender is dropped, orwhen the receiver closes the channel. it does not return true if a value
was sent.
in this case, we would see the following values when inquiring about closedness
and emptiness.
is_closed()
is_empty()
option b:
is_closed()
does include completed channelin this model,
is_closed()
returns true when the receiver closes the channel,when the sender is dropped, or when the sender is used to send a value.
is_closed()
is_empty()
⚖️ comparing a and b
now, this brings us to @hawkw's note:
first, i would note that we actually have some minor semantic differences
between different channels' respective
is_closed
methods already, uponcloser inspection. i'd argue that is fine, and inherent to the fact that
different channel have different semantics, and that is bound to be reflected
in methods querying their conceptual state.
as an example,
sync::watch::Sender::is_closed
returns true if all receiversare dropped, while
sync::mpsc::Sender::is_closed
returns true if allreceivers are dropped or when
Receiver::close
is called. async::watch
channel can have many receivers, so it makes sense that its notion of
closedness would be narrower than a channel that conversely has a single
receiver.
it seems like it would be in line with the above for a
oneshot
channel tohave its own unique caveat related for
is_closed
, returning true when thesender has been dropped by virtue of having sent a value.
...a secret third thing?
now, zooming back out for a moment: i found myself curious about this because
i've frequently encountered a pattern when interacting with
oneshot
channelswherein they are very frequently wrapped in an
Option<T>
. once the receiveris polled and yields a
Poll::Ready(_)
value of some kind, the caller takesthe receiver out of the
Option<T>
andmem::drop()
s it to prevent from beingpolled again.
prior to reading the internals of the
oneshot
channel, and learning moreabout how the sender and receiver interact, my initial intuition was that one
could check that a receiver was finished by checking that the channel was
closed, and empty.
looking at the tables above however, i've found that neither option a or b
provide a surefire way to check that a receiver is finished, or if it is safe
to
poll()
. for example: an empty, closed channel could indicate that thesender was dropped, but that doesn't provide information about whether the
Err(RecvError)
has been returned yet.now, looking at the receiver, and its
Future
implementation:i see utility for a third method
tokio::sync::oneshot::Receiver::is_finished()
.if this was an option, i would feel personally much less attached to the
decision over whether or not
is_closed
reports true when a value has beensent.
this would directly let callers check if polling the receiver would panic, and
in friendly, direct terms. this would also mean that the common pattern of
wrapping receivers in
Option<Receiver<T>>
would be largely outmoded.is_finished
would let us inquire about the state of the receiver's owninternal
Option<T>
, without needing to wrap it in another outer option wecan inspect.
what do you think, @Darksonn, and @hawkw?
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.
If the intent of this PR is a way to detect whether polling the channel will panic, then having a function that does exactly that sounds good to me.
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 intent of this PR, broadly speaking, is to introduce accessors that allows
sync::oneshot::Receiver<T>
to inspect the channel's status in the same manner that other channels likesync::mpsc
's andsync::broadcast
's respectiveReceiver<T>
s can.detecting panics is one acute example, but i've also felt a need for these interfaces when writing
Body
implementations for types backed by a oneshot, to provide another example.a
Body
backed by a oneshot receiver cannot report properis_end_stream()
hints because the receiver doesn't provide a way to inspect the channel. these felt like relatively agnostic methods to expose in part because they follow patterns exposed by other channels.91682a9 introduces an
is_finished()
method. i'd be happy to outline that into a separate PR if we don't feel a disposition to mergeis_empty()
andis_closed()
methods. what do you think?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.
Personally, I'd be inclined to suggest a PR adding a method like that separately, so that the change you actually need isn't blocked on deciding the right semantics for
is_closed
. I think that change should hopefully be fairly uncontroversial.Regarding naming for that method, I think "is_finished" is probably fine, but I'll note that the
futures
crate'sFusedFuture
trait calls its method with similar semanticsis_terminated
. Although we probably won't be addingFusedFuture
implementations in Tokio due to stability concerns, it might be worth choosing a name that's consistent with the one infutures
? I could be convinced either way though.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.
And, to be clear, I'd also still like to have
is_closed()
andis_empty()
— I just think we could add anis_finished_and_or_terminated()
without having to wait for figuring outis_closed()
.I think we could probably also land an
is_empty()
method separately, if you like. I don't think there's much ambiguity about what "empty" means in this case.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.
thank you both for the advice and for your time, @Darksonn and @hawkw! i agree, landing these each separately sounds like a more prudent path forward.
i'm going to close this, and let us consider each of these proposals individually.
see:
oneshot::Receiver::is_terminated()
#7152oneshot::Receiver::is_empty()
#7153oneshot::Receiver::is_closed()
#7154