Skip to content

Commit 4d2ad0e

Browse files
stepanchegtaiki-e
authored andcommitted
Allow calling UnboundedReceiver::try_next after None
Allow calling `UnboundedReceiver::try_next` and `Receiver::try_next` after `None`: do not panic. Not-panicking is equally safe, and does not have negative performance implication. It is irrelevant for `Stream` implementation to panic or not (because `Stream` behavior is unspecified after `None`), but panicking in `try_next` just complicates the interface: returned `Ok(None)` is reasonable assumption to have. Consider this use case: drain the queue on drop by performing app-specific cleanup of queued messages. The obvious implementation would be: ``` impl Drop for MyReceiverWrapper { fn drop(&mut self) { while let Ok(Some(m)) self.try_next() { cleanup(m); } } } ``` Without this change, I cannot even say for sure how this code need to be implemented to avoid panicking. E. g. is `is_closed` enough or some additional checks need to be performed?
1 parent 48d0096 commit 4d2ad0e

File tree

2 files changed

+30
-8
lines changed

2 files changed

+30
-8
lines changed

futures-channel/src/mpsc/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,9 +1020,6 @@ impl<T> Receiver<T> {
10201020
/// It is not recommended to call this function from inside of a future,
10211021
/// only when you've otherwise arranged to be notified when the channel is
10221022
/// no longer empty.
1023-
///
1024-
/// This function will panic if called after `try_next` or `poll_next` has
1025-
/// returned `None`.
10261023
pub fn try_next(&mut self) -> Result<Option<T>, TryRecvError> {
10271024
match self.next_message() {
10281025
Poll::Ready(msg) => {
@@ -1033,7 +1030,10 @@ impl<T> Receiver<T> {
10331030
}
10341031

10351032
fn next_message(&mut self) -> Poll<Option<T>> {
1036-
let inner = self.inner.as_mut().expect("Receiver::next_message called after `None`");
1033+
let inner = match self.inner.as_mut() {
1034+
None => return Poll::Ready(None),
1035+
Some(inner) => inner,
1036+
};
10371037
// Pop off a message
10381038
match unsafe { inner.message_queue.pop_spin() } {
10391039
Some(msg) => {
@@ -1173,9 +1173,6 @@ impl<T> UnboundedReceiver<T> {
11731173
/// * `Ok(Some(t))` when message is fetched
11741174
/// * `Ok(None)` when channel is closed and no messages left in the queue
11751175
/// * `Err(e)` when there are no messages available, but channel is not yet closed
1176-
///
1177-
/// This function will panic if called after `try_next` or `poll_next` has
1178-
/// returned `None`.
11791176
pub fn try_next(&mut self) -> Result<Option<T>, TryRecvError> {
11801177
match self.next_message() {
11811178
Poll::Ready(msg) => {
@@ -1186,7 +1183,10 @@ impl<T> UnboundedReceiver<T> {
11861183
}
11871184

11881185
fn next_message(&mut self) -> Poll<Option<T>> {
1189-
let inner = self.inner.as_mut().expect("Receiver::next_message called after `None`");
1186+
let inner = match self.inner.as_mut() {
1187+
None => return Poll::Ready(None),
1188+
Some(inner) => inner,
1189+
};
11901190
// Pop off a message
11911191
match unsafe { inner.message_queue.pop_spin() } {
11921192
Some(msg) => {

futures-channel/tests/mpsc-close.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,25 @@ fn stress_try_send_as_receiver_closes() {
276276
bg.join()
277277
.expect("background thread join");
278278
}
279+
280+
#[test]
281+
fn unbounded_try_next_after_none() {
282+
let (tx, mut rx) = mpsc::unbounded::<String>();
283+
// Drop the sender, close the channel.
284+
drop(tx);
285+
// Receive the end of channel.
286+
assert_eq!(Ok(None), rx.try_next().map_err(|_| ()));
287+
// None received, check we can call `try_next` again.
288+
assert_eq!(Ok(None), rx.try_next().map_err(|_| ()));
289+
}
290+
291+
#[test]
292+
fn bounded_try_next_after_none() {
293+
let (tx, mut rx) = mpsc::channel::<String>(17);
294+
// Drop the sender, close the channel.
295+
drop(tx);
296+
// Receive the end of channel.
297+
assert_eq!(Ok(None), rx.try_next().map_err(|_| ()));
298+
// None received, check we can call `try_next` again.
299+
assert_eq!(Ok(None), rx.try_next().map_err(|_| ()));
300+
}

0 commit comments

Comments
 (0)