rustasync / runtime

Empowering everyone to build asynchronous software

Home Page:https://docs.rs/runtime

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question: why is delay not unwind safe?

xmclark opened this issue · comments

A fairly minimal example:

use std::time::Duration;
use futures::FutureExt;

#[runtime::main]
async fn main() {
    let _ = runtime::time::Delay::new(Duration::from_millis(100)).catch_unwind().await;
}

Output:

error[E0277]: the type `(dyn runtime_raw::time::Delay<Output = std::time::Instant> + 'static)` may not be safely transferred across an unwind boundary

I can mitigate the problem by wrapping the future in AssertUnwindSafe but that is a smell to me and I don't understand the consequences of doing that. Still trying to wrap my head around the new async syntax and unwind stuff.

I think I was able to answer my own question:

The Pin<Box<dyn Delay>>> was hiding more interesting error messages. With a small change to the example: awaiting the underlying futures_timer::Delay reveals a reference to an AtomicWaker which is !RefUnwindSafe due to an UnsafeCell. Because a contained type is UnwindSafe, so is runtime Delay.

use std::time::Duration;
use futures::FutureExt;
use futures_timer::{Delay as AsyncDelay};

#[runtime::main]
async fn main() {
    AsyncDelay::new(Duration::from_millis(100)).catch_unwind().await;
}

error:

error[E0277]: the type `std::cell::UnsafeCell<std::option::Option<std::task::Waker>>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
 --> src/main.rs:8:49
  |
8 |     AsyncDelay::new(Duration::from_millis(100)).catch_unwind().await;
  |                                                 ^^^^^^^^^^^^ `std::cell::UnsafeCell<std::option::Option<std::task::Waker>>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
  |
  = help: within `futures_timer::arc_list::Node<futures_timer::ScheduledTimer>`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<std::option::Option<std::task::Waker>>`
  = note: required because it appears within the type `futures_core::task::__internal::atomic_waker::AtomicWaker`
  = note: required because it appears within the type `futures_timer::ScheduledTimer`
  = note: required because it appears within the type `futures_timer::arc_list::Node<futures_timer::ScheduledTimer>`
  = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `std::sync::Arc<futures_timer::arc_list::Node<futures_timer::ScheduledTimer>>`
  = note: required because it appears within the type `std::option::Option<std::sync::Arc<futures_timer::arc_list::Node<futures_timer::ScheduledTimer>>>`
  = note: required because it appears within the type `futures_timer::delay::Delay`
commented

@xmclark glad you were able to figure it out; it was interesting to read along!