wip

rewrote futures-core and futures-compat

reference to AtomicValueGuard is stored in RawWaker.data, custom
bcsc::Future clone of core::future::Future is needed to ensure
incompatibility between the two systems since the custom RawWaker is
a "broken" waker that panics when used normally

+171 -181
+1 -5
Cargo.lock
··· 38 38 dependencies = [ 39 39 "futures-combinators", 40 40 "futures-compat", 41 - "futures-core", 42 41 "futures-derive", 43 42 "futures-util", 44 43 ] ··· 47 46 name = "futures-combinators" 48 47 version = "0.0.2" 49 48 dependencies = [ 50 - "futures-core", 51 49 "futures-util", 52 50 ] 53 51 ··· 56 54 version = "0.0.2" 57 55 dependencies = [ 58 56 "futures-core", 57 + "lifetime-guard", 59 58 ] 60 59 61 60 [[package]] ··· 74 73 [[package]] 75 74 name = "futures-util" 76 75 version = "0.0.2" 77 - dependencies = [ 78 - "futures-core", 79 - ] 80 76 81 77 [[package]] 82 78 name = "generator"
+1 -1
Cargo.toml
··· 1 1 [workspace] 2 2 resolver = "3" 3 - members = [ "futures", "futures-core", "futures-combinators", "futures-compat", "futures-derive", "futures-util", "lifetime-guard"] 3 + members = [ "futures", "futures-combinators", "futures-compat", "futures-core", "futures-derive", "futures-util", "lifetime-guard"] 4 4 5 5 [workspace.package] 6 6 version = "0.0.2"
-1
futures-combinators/Cargo.toml
··· 9 9 homepage.workspace = true 10 10 11 11 [dependencies] 12 - futures-core = { workspace = true } 13 12 futures-util = { workspace = true }
+8 -8
futures-combinators/src/join.rs
··· 1 - use futures_core::{ScopedFuture, Wake}; 1 + use futures_comp 2 2 use futures_util::WakeStore; 3 3 use futures_util::{MaybeDone, maybe_done}; 4 4 use std::{cell::Cell, task::Poll}; ··· 8 8 /// 9 9 /// Awaits multiple futures simultaneously, returning the output of the futures 10 10 /// in the same container type they were created once all complete. 11 - pub trait Join<'scope> { 11 + pub trait Join { 12 12 /// The resulting output type. 13 13 type Output; 14 14 15 15 /// The [`ScopedFuture`] implementation returned by this method. 16 - type Future: ScopedFuture<'scope, Output = Self::Output>; 16 + type Future: Future<Output = Self::Output>; 17 17 18 18 /// Waits for multiple futures to complete. 19 19 /// ··· 27 27 pub trait JoinExt<'scope> { 28 28 fn along_with<Fut>(self, other: Fut) -> Join2<'scope, Self, Fut> 29 29 where 30 - Self: Sized + ScopedFuture<'scope>, 31 - Fut: ScopedFuture<'scope>, 30 + Self: Sized + Future<'scope>, 31 + Fut: Future<'scope>, 32 32 { 33 33 (self, other).join() 34 34 } 35 35 } 36 36 37 - impl<'scope, T> JoinExt<'scope> for T where T: ScopedFuture<'scope> {} 37 + impl<'scope, T> JoinExt<'scope> for T where T: Future<'scope> {} 38 38 39 39 macro_rules! impl_join_tuple { 40 40 ($namespace:ident $StructName:ident $($F:ident)+) => { ··· 153 153 let x1 = Cell::new(0); 154 154 let x2 = Cell::new(0); 155 155 let f1 = poll_fn(|wake| { 156 - wake.wake(); 156 + wake.register(); 157 157 x1.set(x1.get() + 1); 158 158 if x1.get() == 4 { 159 159 Poll::Ready(x1.get()) ··· 162 162 } 163 163 }); 164 164 let f2 = poll_fn(|wake| { 165 - wake.wake(); 165 + wake.register(); 166 166 x2.set(x2.get() + 1); 167 167 if x2.get() == 5 { 168 168 Poll::Ready(x2.get())
+1
futures-compat/Cargo.toml
··· 10 10 11 11 [dependencies] 12 12 futures-core = { workspace = true } 13 + lifetime-guard = { workspace = true }
+49 -117
futures-compat/src/lib.rs
··· 1 - //! Any interaction between the real Future ecosystem and ScopedFuture 2 - //! ecosystem is strictly unsound 3 - //! 4 - //! ScopedFutures cannot poll Futures because they can't guarantee they 5 - //! will outlive *const () ptrs they supply to the futures, leading to 6 - //! dangling pointers if the futures register the waker with something that 7 - //! assumes assumes it is valid for 'static and then the ScopedFuture goes 8 - //! out of scope 9 - //! 10 - //! Futures cannot poll ScopedFutures because they cannot guarantee their 11 - //! Waker will be valid for 'scope (due to lack of real borrowing), leading to 12 - //! unsoundness if a ScopedFuture internally registers the waker with something 13 - //! that expects it to live for 'scope, and then the ScopedFutureWrapper is 14 - //! dropped 15 - //! 16 - //! This code is not for the faint of heart. Read at your own risk. 1 + //! Any interaction between an executor/reactor intended for task::Future 2 + //! with an executor/reactor intended for bcsc::Future is strictly unsound. 17 3 18 4 use std::{ 19 - cell::UnsafeCell, 20 - marker::PhantomData, 21 - mem, 22 5 pin::Pin, 23 - task::{Context, Poll, Waker}, 6 + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, 24 7 }; 25 8 26 - use futures_core::{ScopedFuture, Wake}; 9 + use lifetime_guard::atomic_guard::AtomicValueGuard; 10 + 11 + static EVIL_VTABLE: RawWakerVTable = RawWakerVTable::new( 12 + |_| panic!("wtf"), 13 + |_| panic!("wtf"), 14 + |_| panic!("wtf"), 15 + |_| panic!("wtf"), 16 + ); 27 17 28 - /// RawWaker: fat ptr (*const (), &'static RawWakerVTable) 29 - /// &'scope dyn Wake fat ptr: (&'scope (), &'scope WakeVTable) 18 + /// Coerces a pinned `AtomicValueGuard` reference to a `Waker` for use in 19 + /// `core::future::Future` 30 20 /// 31 - /// can transmute between them, but the waker will be completely invalid! 21 + /// Any usage or storage of the resulting `Waker` is undefined behavior. 22 + pub unsafe fn guard_to_waker(guard: Pin<&AtomicValueGuard<fn()>>) -> Waker { 23 + unsafe { 24 + Waker::from_raw(RawWaker::new( 25 + guard.get_ref() as *const AtomicValueGuard<fn()> as *const (), 26 + &EVIL_VTABLE, 27 + )) 28 + } 29 + } 32 30 33 - /// wraps an internal ScopedFuture, implements Future 34 - pub struct ScopedFutureWrapper<'scope, F: ScopedFuture<'scope>> { 35 - inner: UnsafeCell<F>, 36 - marker: PhantomData<&'scope ()>, 31 + /// Coerces a `Waker` into a pinned `AtomicValueGuard` reference. 32 + /// 33 + /// This should only be used to undo the work of `guard_to_waker`. 34 + pub unsafe fn waker_to_guard<'a>( 35 + waker: Waker, 36 + ) -> Pin<&'a AtomicValueGuard<fn()>> { 37 + unsafe { 38 + Pin::new_unchecked(&*(waker.data() as *const AtomicValueGuard<fn()>)) 39 + } 37 40 } 38 41 39 - impl<'scope, F: ScopedFuture<'scope> + 'scope> Future 40 - for ScopedFutureWrapper<'scope, F> 41 - { 42 + /// wraps `core::future::Future` in impl of `bcsc:Future` 43 + #[repr(transparent)] 44 + pub struct NormalFutureWrapper<F: core::future::Future>(F); 45 + 46 + impl<F: core::future::Future> futures_core::Future for NormalFutureWrapper<F> { 42 47 type Output = F::Output; 43 48 44 49 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { 45 - // # Safety 46 - // 47 - // Transmutes `Waker` into `&'scope dyn Wake`. 48 - // This is possible because Waker (internally just RawWaker) contains 49 - // (*const (), &'static RawWakerVTable), and the fat ptr `&dyn Wake` 50 - // internally is (*const (), *const WakeVTable). 51 - // 52 - // For this to be sound, the input waker from `cx` must be an invalid 53 - // waker (using the waker as intended would be UB) that has the form of 54 - // a `&dyn Wake` fat ptr, as generated in `UnscopedFutureWrapper`. 55 - // 56 - // This is only sound because it is paired with the transmute in 57 - // `UnscopedFutureWrapper` 58 - // 59 - // This conversion is necessary to piggyback off rustc's expansion 60 - // of `async` blocks into state machines implementing `Future`. 61 - // 62 - // The unpinning is safe because inner (a `ScopedFuture`) cannot be 63 - // moved after a self reference is established on its first `poll`. 64 - // 65 - // The use of the `UnsafeCell` is sound and necessary to get around 66 - // the afformentioned immutable self reference (since `Future::poll`) 67 - // requires a `&mut Self`. It is sound because we never take a 68 - // `&mut self.inner`. 69 - unsafe { 70 - let this = self.get_unchecked_mut(); 71 - let wake: &'scope dyn Wake = mem::transmute::< 72 - Waker, 73 - &'scope dyn Wake, 74 - >(cx.waker().to_owned()); 75 - (&*this.inner.get()).poll(wake) 76 - } 50 + unsafe { self.map_unchecked_mut(|this| &mut this.0).poll(cx) } 77 51 } 78 52 } 79 53 80 - impl<'scope, F: ScopedFuture<'scope> + 'scope> ScopedFutureWrapper<'scope, F> { 81 - pub unsafe fn from_scoped(f: F) -> Self { 82 - Self { 83 - inner: f.into(), 84 - marker: PhantomData, 85 - } 54 + impl<F: core::future::Future> NormalFutureWrapper<F> { 55 + pub unsafe fn from_std_future(future: F) -> Self { 56 + Self(future) 86 57 } 87 58 } 88 59 89 - /// wraps an internal Future, implements ScopedFuture 90 - /// this is fundamentally unsafe and relies on the future not registering its waker 91 - /// in any reactor that lives beyond this wrapper, otherwise there will be a dangling pointer 92 - /// 93 - /// it is safe to use only with the #[async_scoped] macro, which guarantees that, internally, every futures is a ScopedFutureWrapper 94 - pub struct UnscopedFutureWrapper<'scope, F: Future> { 95 - inner: UnsafeCell<F>, 96 - marker: PhantomData<&'scope ()>, 97 - } 60 + /// wraps custom `bcsc::Future` in impl of `core::future::Future` 61 + #[repr(transparent)] 62 + pub struct CustomFutureWrapper<F: futures_core::Future>(F); 98 63 99 - impl<'scope, F: Future + 'scope> ScopedFuture<'scope> 100 - for UnscopedFutureWrapper<'scope, F> 101 - { 64 + impl<F: futures_core::Future> core::future::Future for CustomFutureWrapper<F> { 102 65 type Output = F::Output; 103 66 104 - fn poll( 105 - self: &'scope Self, 106 - wake: &'scope dyn Wake<'scope>, 107 - ) -> Poll<Self::Output> { 108 - // # Safety 109 - // 110 - // Transmutes `&'scope dyn Wake` into a Waker. 111 - // This is possible because Waker (internally just RawWaker) contains 112 - // (*const (), &'static RawWakerVTable), and the fat ptr `&dyn Wake` 113 - // internally is (*const (), *const WakeVTable). 114 - // 115 - // Using the resulting Waker is UB, which is why UnscopedFutureWrapper 116 - // can only be used in pair with ScopedFutureWrapper, which transmutes 117 - // the invalid `Waker` back to a `&dyn Wake`. 118 - // 119 - // This conversion is necessary to piggyback off rustc's expansion 120 - // of `async` blocks into state machines implementing `Future`. 121 - let waker: Waker = 122 - unsafe { mem::transmute::<&'scope dyn Wake<'scope>, Waker>(wake) }; 123 - // # Safety 124 - // 125 - // Once any ScopedFuture is first polled and stores a waker, it becomes 126 - // immutable and immovable because it has an immutable self reference. 127 - // 128 - // The Pin::new_unchecked is necessary to be compatible with 129 - // `task::Future` 130 - let pinned_future = 131 - unsafe { Pin::new_unchecked(&mut *self.inner.get()) }; 132 - 133 - pinned_future.poll(&mut Context::from_waker(&waker)) 67 + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { 68 + unsafe { self.map_unchecked_mut(|this| &mut this.0).poll(cx) } 134 69 } 135 70 } 136 71 137 - impl<'scope, F: Future + 'scope> UnscopedFutureWrapper<'scope, F> { 138 - pub unsafe fn from_future(f: F) -> Self { 139 - Self { 140 - inner: f.into(), 141 - marker: PhantomData, 142 - } 72 + impl<F: futures_core::Future> CustomFutureWrapper<F> { 73 + pub unsafe fn from_custom_future(future: F) -> Self { 74 + Self(future) 143 75 } 144 76 }
+111 -41
futures-core/src/lib.rs
··· 1 - use std::{pin::Pin, task::Poll}; 1 + //! Redefinitions of task::Future to be incompatible with them 2 2 3 - mod task; 3 + use std::{ 4 + ops, 5 + pin::Pin, 6 + task::{Context, Poll}, 7 + }; 4 8 5 - pub use crate::task::Wake; 6 - 7 - /// ScopedFuture represents a unit of asynchronous computation that must be 8 - /// polled by an external actor. 9 + /// A future represents an asynchronous computation obtained by use of `async`. 9 10 /// 10 - /// Implementations access a context (`cx: &'scope dyn Wake`) to signal 11 - /// they are ready to resume execution. 12 - /// 13 - /// A notable difference between `bcsc::ScopedFuture` and `core::task::Future` 14 - /// is the latter cannot safetly ran as a task by an executor without having a 15 - /// 'static lifetime. This is because there is no way for the compiler to 16 - /// guarantee the task doesn't outlive any data, as the executor is free to 17 - /// cancel it (or refuse to) whenever it wants. 11 + /// This future assumes a nonstandard Context, which is incompatible with 12 + /// executors or reactors made for `core::future::Future`. In the interest of 13 + /// safety, it has a dedicated type. 18 14 /// 19 - /// Additionally, because raw/unsafe implementations of `core::task::Waker` 20 - /// effectively do lifetime-erasure, stack-allocated futures cannot prevent 21 - /// unsound behavior from wakers outliving them (even `Forget` would not 22 - /// entirely fix this due to the api). 15 + /// A future is a value that might not have finished computing yet. This kind of 16 + /// "asynchronous value" makes it possible for a thread to continue doing useful 17 + /// work while it waits for the value to become available. 23 18 /// 24 - /// In order to avoid unsound behavior, executors must either use Weak<Wake> 25 - /// for safetly losing access to tasks or enforce tasks being stored in 26 - /// `static` pools of memory. 19 + /// # The `poll` method 27 20 /// 28 - /// `ScopedFuture` instead leverages the borrow checker to allow for (less 29 - /// powerful) stack based async execution. 21 + /// The core method of future, `poll`, *attempts* to resolve the future into a 22 + /// final value. This method does not block if the value is not ready. Instead, 23 + /// the current task is scheduled to be woken up when it's possible to make 24 + /// further progress by `poll`ing again. The `context` passed to the `poll` 25 + /// method can provide a [`Waker`], which is a handle for waking up the current 26 + /// task. 30 27 /// 31 - /// some more: 32 - /// what occurs in `core::task::Future::poll()` is that the ref to a cx.waker 33 - /// is cloned and stored by a reactor via some method. 28 + /// When using a future, you generally won't call `poll` directly, but instead 29 + /// `.await` the value. 34 30 /// 35 - /// The waker is no longer tied to the actual future's lifetime, making it 36 - /// unsound to not have either static tasks or reference counting. 37 - /// To avoid this, we want to use a &'scope waker instead, with 1 waker / task. 31 + /// [`Waker`]: crate::task::Waker 38 32 #[must_use = "futures do nothing unless you `.await` or poll them"] 39 33 #[diagnostic::on_unimplemented( 40 - message = "`{Self}` is not a `ScopedFuture`", 41 - label = "`{Self}` is not a `ScopedFuture`", 42 - note = "If you are trying to await a `task::Future` from within a `ScopedFuture`, note that the systems are incompatible." 34 + label = "`{Self}` is not a `bcsc::Future`", 35 + message = "`{Self}` is not a `bcsc::Future`", 36 + note = "If you are trying to await a `core::future::Future` from within a `bcsc::Future`, note that the systems are incompatible." 43 37 )] 44 - pub trait ScopedFuture<'scope> { 38 + pub trait Future { 39 + /// The type of value produced on completion. 45 40 type Output; 46 41 47 - /// as soon as poll is called, the struct becomes self-referential, 48 - /// effectively pinned until dropped (or forgotten....D; ) 49 - fn poll<'events>( 50 - self: Pin<&mut Self>, 51 - wake: &'events dyn Wake<'scope>, 52 - ) -> Poll<Self::Output> 53 - where 54 - 'events: 'scope; 42 + /// Attempts to resolve the future to a final value, registering 43 + /// the current task for wakeup if the value is not yet available. 44 + /// 45 + /// # Return value 46 + /// 47 + /// This function returns: 48 + /// 49 + /// - [`Poll::Pending`] if the future is not ready yet 50 + /// - [`Poll::Ready(val)`] with the result `val` of this future if it 51 + /// finished successfully. 52 + /// 53 + /// Once a future has finished, clients should not `poll` it again. 54 + /// 55 + /// When a future is not ready yet, `poll` returns `Poll::Pending` and 56 + /// stores a clone of the [`Waker`] copied from the current [`Context`]. 57 + /// This [`Waker`] is then woken once the future can make progress. 58 + /// For example, a future waiting for a socket to become 59 + /// readable would call `.clone()` on the [`Waker`] and store it. 60 + /// When a signal arrives elsewhere indicating that the socket is readable, 61 + /// [`Waker::wake`] is called and the socket future's task is awoken. 62 + /// Once a task has been woken up, it should attempt to `poll` the future 63 + /// again, which may or may not produce a final value. 64 + /// 65 + /// Note that on multiple calls to `poll`, only the [`Waker`] from the 66 + /// [`Context`] passed to the most recent call should be scheduled to 67 + /// receive a wakeup. 68 + /// 69 + /// # Runtime characteristics 70 + /// 71 + /// Futures alone are *inert*; they must be *actively* `poll`ed to make 72 + /// progress, meaning that each time the current task is woken up, it should 73 + /// actively re-`poll` pending futures that it still has an interest in. 74 + /// 75 + /// The `poll` function is not called repeatedly in a tight loop -- instead, 76 + /// it should only be called when the future indicates that it is ready to 77 + /// make progress (by calling `wake()`). If you're familiar with the 78 + /// `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures 79 + /// typically do *not* suffer the same problems of "all wakeups must poll 80 + /// all events"; they are more like `epoll(4)`. 81 + /// 82 + /// An implementation of `poll` should strive to return quickly, and should 83 + /// not block. Returning quickly prevents unnecessarily clogging up 84 + /// threads or event loops. If it is known ahead of time that a call to 85 + /// `poll` may end up taking a while, the work should be offloaded to a 86 + /// thread pool (or something similar) to ensure that `poll` can return 87 + /// quickly. 88 + /// 89 + /// # Panics 90 + /// 91 + /// Once a future has completed (returned `Ready` from `poll`), calling its 92 + /// `poll` method again may panic, block forever, or cause other kinds of 93 + /// problems; the `Future` trait places no requirements on the effects of 94 + /// such a call. However, as the `poll` method is not marked `unsafe`, 95 + /// Rust's usual rules apply: calls must never cause undefined behavior 96 + /// (memory corruption, incorrect use of `unsafe` functions, or the like), 97 + /// regardless of the future's state. 98 + /// 99 + /// [`Poll::Ready(val)`]: Poll::Ready 100 + /// [`Waker`]: crate::task::Waker 101 + /// [`Waker::wake`]: crate::task::Waker::wake 102 + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; 103 + } 104 + 105 + impl<F: ?Sized + Future + Unpin> Future for &mut F { 106 + type Output = F::Output; 107 + 108 + fn poll( 109 + mut self: Pin<&mut Self>, 110 + cx: &mut Context<'_>, 111 + ) -> Poll<Self::Output> { 112 + F::poll(Pin::new(&mut **self), cx) 113 + } 114 + } 115 + 116 + impl<P> Future for Pin<P> 117 + where 118 + P: ops::DerefMut<Target: Future>, 119 + { 120 + type Output = <<P as ops::Deref>::Target as Future>::Output; 121 + 122 + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { 123 + <P::Target as Future>::poll(self.as_deref_mut(), cx) 124 + } 55 125 }
-6
futures-core/src/task.rs
··· 1 - // Task: Wake 2 - // 3 - // Wake must not outlive event loop/storage 4 - pub trait Wake<'events> { 5 - fn wake(&self); 6 - }
-1
futures-util/Cargo.toml
··· 9 9 homepage.workspace = true 10 10 11 11 [dependencies] 12 - futures-core = { workspace = true }
-1
futures/Cargo.toml
··· 9 9 homepage.workspace = true 10 10 11 11 [dependencies] 12 - futures-core = { workspace = true } 13 12 futures-combinators = { workspace = true } 14 13 futures-compat = { workspace = true } 15 14 futures-derive = { workspace = true }