Skip to content

Commit c8f3919

Browse files
authored
Clarify catch_unwind behavior of RemoteHandle. (#2080)
1 parent c743c53 commit c8f3919

File tree

1 file changed

+16
-3
lines changed

1 file changed

+16
-3
lines changed

futures-util/src/future/future/remote_handle.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@ use {
2222
/// The handle to a remote future returned by
2323
/// [`remote_handle`](crate::future::FutureExt::remote_handle). When you drop this,
2424
/// the remote future will be woken up to be dropped by the executor.
25+
///
26+
/// ## Unwind safety
27+
///
28+
/// When the remote future panics, [Remote] will catch the unwind and transfer it to
29+
/// the thread where `RemoteHandle` is being awaited. This is good for the common
30+
/// case where [Remote] is spawned on a threadpool. It is unlikely that other code
31+
/// in the executor working thread shares mutable data with the spawned future and we
32+
/// preserve the executor from losing its working threads.
33+
///
34+
/// If you run the future locally and send the handle of to be awaited elsewhere, you
35+
/// must be careful with regard to unwind safety because the thread in which the future
36+
/// is polled will keep running after the panic and the thread running the [RemoteHandle]
37+
/// will unwind.
2538
#[must_use = "futures do nothing unless you `.await` or poll them"]
2639
#[derive(Debug)]
2740
pub struct RemoteHandle<T> {
@@ -45,7 +58,9 @@ impl<T: Send + 'static> Future for RemoteHandle<T> {
4558
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
4659
match ready!(self.rx.poll_unpin(cx)) {
4760
Ok(Ok(output)) => Poll::Ready(output),
61+
// the remote future panicked.
4862
Ok(Err(e)) => panic::resume_unwind(e),
63+
// The oneshot sender was dropped.
4964
Err(e) => panic::resume_unwind(Box::new(e)),
5065
}
5166
}
@@ -101,9 +116,7 @@ pub(super) fn remote_handle<Fut: Future>(future: Fut) -> (Remote<Fut>, RemoteHan
101116
let (tx, rx) = oneshot::channel();
102117
let keep_running = Arc::new(AtomicBool::new(false));
103118

104-
// AssertUnwindSafe is used here because `Send + 'static` is basically
105-
// an alias for an implementation of the `UnwindSafe` trait but we can't
106-
// express that in the standard library right now.
119+
// Unwind Safety: See the docs for RemoteHandle.
107120
let wrapped = Remote {
108121
future: AssertUnwindSafe(future).catch_unwind(),
109122
tx: Some(tx),

0 commit comments

Comments
 (0)