Skip to content

Commit 17117b5

Browse files
authored
sync: implement oneshot::Receiver::is_terminated() (#7152)
1 parent aa70f6c commit 17117b5

File tree

2 files changed

+155
-3
lines changed

2 files changed

+155
-3
lines changed

Diff for: tokio/src/sync/oneshot.rs

+61-3
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,64 @@ impl<T> Receiver<T> {
931931
}
932932
}
933933

934+
/// Checks if this receiver is terminated.
935+
///
936+
/// This function returns true if this receiver has already yielded a [`Poll::Ready`] result.
937+
/// If so, this receiver should no longer be polled.
938+
///
939+
/// # Examples
940+
///
941+
/// Sending a value and polling it.
942+
///
943+
/// ```
944+
/// use tokio::sync::oneshot;
945+
///
946+
/// use std::task::Poll;
947+
///
948+
/// #[tokio::main]
949+
/// async fn main() {
950+
/// let (tx, mut rx) = oneshot::channel();
951+
///
952+
/// // A receiver is not terminated when it is initialized.
953+
/// assert!(!rx.is_terminated());
954+
///
955+
/// // A receiver is not terminated it is polled and is still pending.
956+
/// let poll = futures::poll!(&mut rx);
957+
/// assert_eq!(poll, Poll::Pending);
958+
/// assert!(!rx.is_terminated());
959+
///
960+
/// // A receiver is not terminated if a value has been sent, but not yet read.
961+
/// tx.send(0).unwrap();
962+
/// assert!(!rx.is_terminated());
963+
///
964+
/// // A receiver *is* terminated after it has been polled and yielded a value.
965+
/// assert_eq!((&mut rx).await, Ok(0));
966+
/// assert!(rx.is_terminated());
967+
/// }
968+
/// ```
969+
///
970+
/// Dropping the sender.
971+
///
972+
/// ```
973+
/// use tokio::sync::oneshot;
974+
///
975+
/// #[tokio::main]
976+
/// async fn main() {
977+
/// let (tx, mut rx) = oneshot::channel::<()>();
978+
///
979+
/// // A receiver is not immediately terminated when the sender is dropped.
980+
/// drop(tx);
981+
/// assert!(!rx.is_terminated());
982+
///
983+
/// // A receiver *is* terminated after it has been polled and yielded an error.
984+
/// let _ = (&mut rx).await.unwrap_err();
985+
/// assert!(rx.is_terminated());
986+
/// }
987+
/// ```
988+
pub fn is_terminated(&self) -> bool {
989+
self.inner.is_none()
990+
}
991+
934992
/// Attempts to receive a value.
935993
///
936994
/// If a pending value exists in the channel, it is returned. If no value
@@ -1106,18 +1164,18 @@ impl<T> Future for Receiver<T> {
11061164

11071165
let ret = if let Some(inner) = self.as_ref().get_ref().inner.as_ref() {
11081166
#[cfg(all(tokio_unstable, feature = "tracing"))]
1109-
let res = ready!(trace_poll_op!("poll_recv", inner.poll_recv(cx)))?;
1167+
let res = ready!(trace_poll_op!("poll_recv", inner.poll_recv(cx))).map_err(Into::into);
11101168

11111169
#[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
1112-
let res = ready!(inner.poll_recv(cx))?;
1170+
let res = ready!(inner.poll_recv(cx)).map_err(Into::into);
11131171

11141172
res
11151173
} else {
11161174
panic!("called after complete");
11171175
};
11181176

11191177
self.inner = None;
1120-
Ready(Ok(ret))
1178+
Ready(ret)
11211179
}
11221180
}
11231181

Diff for: tokio/tests/sync_oneshot.rs

+94
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,97 @@ fn sender_changes_task() {
292292

293293
assert_ready!(task2.enter(|cx, _| tx.poll_closed(cx)));
294294
}
295+
296+
#[test]
297+
fn receiver_is_terminated_send() {
298+
let (tx, mut rx) = oneshot::channel::<i32>();
299+
300+
assert!(
301+
!rx.is_terminated(),
302+
"channel is NOT terminated before value is sent"
303+
);
304+
tx.send(17).unwrap();
305+
assert!(
306+
!rx.is_terminated(),
307+
"channel is NOT terminated after value is sent"
308+
);
309+
310+
let mut task = task::spawn(());
311+
let poll = task.enter(|cx, _| Pin::new(&mut rx).poll(cx));
312+
assert_ready_eq!(poll, Ok(17));
313+
314+
assert!(
315+
rx.is_terminated(),
316+
"channel IS terminated after value is read"
317+
);
318+
}
319+
320+
#[test]
321+
fn receiver_is_terminated_try_recv() {
322+
let (tx, mut rx) = oneshot::channel::<i32>();
323+
324+
assert!(
325+
!rx.is_terminated(),
326+
"channel is NOT terminated before value is sent"
327+
);
328+
tx.send(17).unwrap();
329+
assert!(
330+
!rx.is_terminated(),
331+
"channel is NOT terminated after value is sent"
332+
);
333+
334+
let value = rx.try_recv().expect("value is waiting");
335+
assert_eq!(value, 17);
336+
337+
assert!(
338+
rx.is_terminated(),
339+
"channel IS terminated after value is read"
340+
);
341+
}
342+
343+
#[test]
344+
fn receiver_is_terminated_drop() {
345+
let (tx, mut rx) = oneshot::channel::<i32>();
346+
347+
assert!(
348+
!rx.is_terminated(),
349+
"channel is NOT terminated before sender is dropped"
350+
);
351+
drop(tx);
352+
assert!(
353+
!rx.is_terminated(),
354+
"channel is NOT terminated after sender is dropped"
355+
);
356+
357+
let mut task = task::spawn(());
358+
let poll = task.enter(|cx, _| Pin::new(&mut rx).poll(cx));
359+
assert_ready_err!(poll);
360+
361+
assert!(
362+
rx.is_terminated(),
363+
"channel IS terminated after value is read"
364+
);
365+
}
366+
367+
#[test]
368+
fn receiver_is_terminated_rx_close() {
369+
let (_tx, mut rx) = oneshot::channel::<i32>();
370+
assert!(
371+
!rx.is_terminated(),
372+
"channel is NOT terminated before closing"
373+
);
374+
rx.close();
375+
assert!(
376+
!rx.is_terminated(),
377+
"channel is NOT terminated before closing"
378+
);
379+
380+
let mut task = task::spawn(());
381+
let poll = task.enter(|cx, _| Pin::new(&mut rx).poll(cx));
382+
assert_ready_err!(poll);
383+
384+
assert!(
385+
rx.is_terminated(),
386+
"channel IS terminated after value is read"
387+
);
388+
}

0 commit comments

Comments
 (0)