diff --git a/futures-util/src/compat/compat01as03.rs b/futures-util/src/compat/compat01as03.rs index d63d73c7dc..a48518264f 100644 --- a/futures-util/src/compat/compat01as03.rs +++ b/futures-util/src/compat/compat01as03.rs @@ -9,6 +9,7 @@ use futures_core::{future::Future as Future03, stream::Stream as Stream03, task #[cfg(feature = "sink")] use futures_sink::Sink as Sink03; use std::boxed::Box; +use std::cell::UnsafeCell; use std::pin::Pin; use std::task::Context; @@ -319,33 +320,45 @@ where } } -struct NotifyWaker(task03::Waker); +#[repr(transparent)] +struct NotifyWaker(UnsafeCell); #[derive(Clone)] struct WakerToHandle<'a>(&'a task03::Waker); impl From> for NotifyHandle01 { fn from(handle: WakerToHandle<'_>) -> Self { - let ptr = Box::new(NotifyWaker(handle.0.clone())); + let waker_ptr: Box = Box::new(handle.0.clone()); + // NotifyWaker is a transparent (pointer compatible) wrapper for + // task03::Waker (and wrapping in UnsafeCell is fine). + let ptr: *mut NotifyWaker = Box::into_raw(waker_ptr) as *mut NotifyWaker; - unsafe { Self::new(Box::into_raw(ptr)) } + unsafe { Self::new(ptr) } } } impl Notify01 for NotifyWaker { fn notify(&self, _: usize) { - self.0.wake_by_ref(); + unsafe { &*self.0.get() }.wake_by_ref(); } } +unsafe impl Send for NotifyWaker {} +unsafe impl Sync for NotifyWaker {} + unsafe impl UnsafeNotify01 for NotifyWaker { unsafe fn clone_raw(&self) -> NotifyHandle01 { - WakerToHandle(&self.0).into() + WakerToHandle(unsafe { &*self.0.get() }).into() } unsafe fn drop_raw(&self) { - let ptr: *const dyn UnsafeNotify01 = self; - drop(unsafe { Box::from_raw(ptr as *mut dyn UnsafeNotify01) }); + /* UnsafeNotify01::drop_raw says this should receive `*mut Self`, + * but that isn't dyn compatible. + * miri is unhappy when a `*mut` is created from a `&` reference, + * so need to go through `UnsafeCell`. + */ + let waker: *mut task03::Waker = self.0.get(); + drop(unsafe { Box::from_raw(waker) }); } } diff --git a/futures/tests/compat_miri.rs b/futures/tests/compat_miri.rs new file mode 100644 index 0000000000..b5ccaa4612 --- /dev/null +++ b/futures/tests/compat_miri.rs @@ -0,0 +1,14 @@ +#![cfg(feature = "compat")] +use futures::compat::Future01CompatExt; +use futures::executor::block_on; +use futures::future::TryFutureExt; + +#[test] +// from https://github.com/rust-lang/futures-rs/issues/2514 +fn miri_compat_two_way() { + let fut = async { Ok(()) as Result<(), ()> }; + let fut = Box::pin(fut); + let fut = fut.compat(); // 03as01 + let fut = fut.compat(); // 01as03 + block_on(fut).unwrap(); +}