diff --git a/Cargo.lock b/Cargo.lock index 60e010f..579b07f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,21 +39,48 @@ dependencies = [ "futures-combinators", "futures-compat", "futures-derive", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core 0.3.31", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", "futures-util", ] +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core 0.3.31", + "futures-sink", +] + [[package]] name = "futures-combinators" version = "0.0.2" dependencies = [ - "futures-util", + "futures 0.3.31", + "futures-compat", + "futures-core 0.0.2", + "lifetime-guard", ] [[package]] name = "futures-compat" version = "0.0.2" dependencies = [ - "futures-core", + "futures-core 0.0.2", "lifetime-guard", ] @@ -61,6 +88,12 @@ dependencies = [ name = "futures-core" version = "0.0.2" +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + [[package]] name = "futures-derive" version = "0.0.2" @@ -70,9 +103,63 @@ dependencies = [ "syn", ] +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core 0.3.31", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + [[package]] name = "futures-util" -version = "0.0.2" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core 0.3.31", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] [[package]] name = "generator" @@ -170,6 +257,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "proc-macro2" version = "1.0.95" @@ -259,6 +352,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + [[package]] name = "smallvec" version = "1.15.1" diff --git a/Cargo.toml b/Cargo.toml index 1649ad9..ba92fbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "3" -members = [ "futures", "futures-combinators", "futures-compat", "futures-core", "futures-derive", "futures-util", "lifetime-guard"] +members = [ "futures", "futures-combinators", "futures-compat", "futures-core", "futures-derive", "lifetime-guard"] [workspace.package] version = "0.0.2" @@ -12,10 +12,11 @@ repository = "https://github.com/AngleSideAngle/bcsc" homepage = "https://github.com/AngleSideAngle/bcsc" [workspace.dependencies] -futures = { path = "futures", version = "0.0.2" } +# futures = { path = "futures", version = "0.0.2" } futures-combinators = { path = "futures-combinators", version = "0.0.2" } futures-compat = { path = "futures-compat", version = "0.0.2" } futures-core = { path = "futures-core", version = "0.0.2" } futures-derive = { path = "futures-derive", version = "0.0.2" } -futures-util = { path = "futures-util", version = "0.0.2" } lifetime-guard = { path = "lifetime-guard", version = "0.0.2" } +# TODO probably vendor in +futures = "0.3" diff --git a/futures-combinators/Cargo.toml b/futures-combinators/Cargo.toml index 2943e7b..048fbb5 100644 --- a/futures-combinators/Cargo.toml +++ b/futures-combinators/Cargo.toml @@ -9,4 +9,7 @@ repository.workspace = true homepage.workspace = true [dependencies] -futures-util = { workspace = true } +futures-core = { workspace = true } +futures-compat = { workspace = true } +lifetime-guard = { workspace = true } +futures = { workspace = true } diff --git a/futures-combinators/src/join.rs b/futures-combinators/src/join.rs index 639e0f0..8f6c1e8 100644 --- a/futures-combinators/src/join.rs +++ b/futures-combinators/src/join.rs @@ -1,7 +1,11 @@ -use futures_comp -use futures_util::WakeStore; -use futures_util::{MaybeDone, maybe_done}; -use std::{cell::Cell, task::Poll}; +use crate::wake::WakeArray; +use futures::future::FusedFuture; +use futures::future::MaybeDone; +use futures::future::maybe_done; +use futures_compat::BespokeFutureWrapper; +use std::pin::Pin; +use std::task::Context; +use std::task::Poll; /// from [futures-concurrency](https://github.com/yoshuawuyts/futures-concurrency/tree/main) /// Wait for all futures to complete. @@ -13,7 +17,7 @@ pub trait Join { type Output; /// The [`ScopedFuture`] implementation returned by this method. - type Future: Future; + type Future: futures_core::Future; /// Waits for multiple futures to complete. /// @@ -24,68 +28,69 @@ pub trait Join { fn join(self) -> Self::Future; } -pub trait JoinExt<'scope> { - fn along_with(self, other: Fut) -> Join2<'scope, Self, Fut> +pub trait JoinExt { + fn along_with(self, other: Fut) -> Join2 where - Self: Sized + Future<'scope>, - Fut: Future<'scope>, + Self: Sized + futures_core::Future, + Fut: futures_core::Future, { (self, other).join() } } -impl<'scope, T> JoinExt<'scope> for T where T: Future<'scope> {} +impl JoinExt for T where T: futures_core::Future {} macro_rules! impl_join_tuple { ($namespace:ident $StructName:ident $($F:ident)+) => { mod $namespace { - use super::*; - - #[allow(non_snake_case)] - pub struct Wakers<'scope> { - $(pub $F: WakeStore<'scope>,)* - } - - #[allow(non_snake_case)] - pub struct WakerRefs<'scope> { - $(pub $F: Cell>>,)* - } + #[repr(u8)] + pub(super) enum Indexes { $($F,)+ } + pub(super) const LEN: usize = [$(Indexes::$F,)+].len(); } #[allow(non_snake_case)] #[must_use = "futures do nothing unless you `.await` or poll them"] - pub struct $StructName<'scope, $($F: ScopedFuture<'scope>),+> { - $($F: MaybeDone<'scope, $F>,)* - wakers: $namespace::Wakers<'scope>, - refs: $namespace::WakerRefs<'scope>, + pub struct $StructName<$($F: futures_core::Future),+> { + $($F: MaybeDone>,)* + wake_array: WakeArray<{$namespace::LEN}>, } - impl<'scope, $($F: ScopedFuture<'scope>),+> ScopedFuture<'scope> - for $StructName<'scope, $($F),+> + impl<$($F: futures_core::Future),+> futures_core::Future for $StructName<$($F),+> { type Output = ($($F::Output),+); - fn poll(&'scope self, wake: &'scope dyn Wake<'scope>) -> Poll { - let mut ready = true; + #[allow(non_snake_case)] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + let wake_array = unsafe { Pin::new_unchecked(&this.wake_array) }; $( - self.wakers.$F.set_parent(wake); - self.refs.$F.replace(Some(&self.wakers.$F)); + // TODO debug_assert_matches is nightly https://github.com/rust-lang/rust/issues/82775 + debug_assert!(!matches!(this.$F, MaybeDone::Gone), "do not poll futures after they return Poll::Ready"); + let mut $F = unsafe { Pin::new_unchecked(&mut this.$F) }; + )+ - if !self.$F.is_done() { - ready &= if self.wakers.$F.take_ready() { - // By polling the future, we create our self-referential structure for lifetime `'scope`. - // - // SAFETY: - // `unwrap_unchecked` is safe because we just inserted `Some` into `refs.$F`, - // so it is guaranteed to be `Some`. - self.$F - .poll(unsafe { (&self.refs.$F.get()).unwrap_unchecked() }) - .is_ready() - } else { - false - }; - } + // extract reference to ValueGuard from Context + // this is safe because futures_core::Future are isolated + // from core::future::Future impls and guaranteed to have + // their cx.wakers represented in the nonstandard format + unsafe { wake_array.register_parent(futures_compat::waker_to_guard(cx.waker())) } + + let mut ready = true; + + $( + let index = $namespace::Indexes::$F as usize; + // cx to feed children + let waker = unsafe { futures_compat::guard_to_waker(wake_array.child_guard_ptr(index).unwrap_unchecked()) }; + let mut child_cx = Context::from_waker(&waker); + + // ready if MaybeDone is Done or just completed (converted to Done) + // unsafe / against Future api contract to poll after Gone/Future is finished + ready &= if unsafe { wake_array.take_woken(index).unwrap_unchecked() } { + $F.as_mut().poll(&mut child_cx).is_ready() + } else { + $F.is_terminated() + }; )+ if ready { @@ -96,7 +101,7 @@ macro_rules! impl_join_tuple { // Once a future is not `MaybeDoneState::Future`, it transitions to `Done`, // so we know the result of `take_output` must be `Some`. unsafe { - self.$F.take_output().unwrap_unchecked() + $F.take_output().unwrap_unchecked() }, )* )) @@ -106,22 +111,17 @@ macro_rules! impl_join_tuple { } } - impl<'scope, $($F: ScopedFuture<'scope>),+> Join<'scope> for ($($F),+) { + impl<$($F: futures_core::Future),+> Join for ($($F),+) { type Output = ($($F::Output),*); - type Future = $StructName<'scope, $($F),+>; + type Future = $StructName<$($F),+>; #[allow(non_snake_case)] fn join(self) -> Self::Future { let ($($F),+) = self; $StructName { - $($F: maybe_done($F),)* - wakers: $namespace::Wakers { - $($F: WakeStore::new(),)* - }, - refs: $namespace::WakerRefs { - $($F: Option::None.into(),)* - }, + $($F: maybe_done(unsafe { futures_compat::bespoke_future_to_std($F) }),)* + wake_array: WakeArray::new(), } } } @@ -144,56 +144,74 @@ impl_join_tuple!(join12 Join12 A B C D E F G H I J K L); mod tests { #![no_std] - use futures_util::{noop_wake, poll_fn}; + use futures_core::{Future, Wake}; + use lifetime_guard::guard::ValueGuard; + + use crate::wake::{DummyWaker, wake_bespoke_waker}; use super::*; + use std::cell::Cell; + use std::future::poll_fn; + use std::pin; + use std::ptr::NonNull; #[test] fn counters() { - let x1 = Cell::new(0); - let x2 = Cell::new(0); - let f1 = poll_fn(|wake| { - wake.register(); - x1.set(x1.get() + 1); - if x1.get() == 4 { - Poll::Ready(x1.get()) + let mut x1 = 0; + let mut x2 = 0; + let f1 = poll_fn(|cx| { + unsafe { wake_bespoke_waker(cx.waker()) }; + x1 += 1; + if x1 == 4 { + Poll::Ready(x1) } else { Poll::Pending } }); - let f2 = poll_fn(|wake| { - wake.register(); - x2.set(x2.get() + 1); - if x2.get() == 5 { - Poll::Ready(x2.get()) + let f2 = poll_fn(|cx| { + unsafe { wake_bespoke_waker(cx.waker()) }; + x2 += 1; + if x2 == 5 { + Poll::Ready(x2) } else { Poll::Pending } }); - let dummy_waker = noop_wake(); - let join = (f1, f2).join(); - for _ in 0..4 { - assert_eq!(join.poll(&dummy_waker), Poll::Pending); + let guard = pin::pin!(ValueGuard::new(NonNull::new( + &mut DummyWaker as *mut dyn Wake, + ))); + let waker = unsafe { futures_compat::guard_to_waker(guard.as_ref()) }; + let mut cx = Context::from_waker(&waker); + let mut join = unsafe { + ( + futures_compat::std_future_to_bespoke(f1), + futures_compat::std_future_to_bespoke(f2), + ) } - assert_eq!(join.poll(&dummy_waker), Poll::Ready((4, 5))); - } - - #[test] - fn never_wake() { - let f1 = poll_fn(|_| Poll::::Pending); - let f2 = poll_fn(|_| Poll::::Pending); - let dummy_waker = noop_wake(); - let join = (f1, f2).join(); - for _ in 0..10 { - assert_eq!(join.poll(&dummy_waker), Poll::Pending); + .join(); + let mut pinned = unsafe { Pin::new_unchecked(&mut join) }; + for _ in 0..4 { + assert_eq!(pinned.as_mut().poll(&mut cx), Poll::Pending); } + assert_eq!(pinned.poll(&mut cx), Poll::Ready((4, 5))); } - #[test] - fn basic() { - let f1 = poll_fn(|_| Poll::Ready(1)); - let f2 = poll_fn(|_| Poll::Ready(2)); - let dummy_waker = noop_wake(); - assert_eq!(f1.along_with(f2).poll(&dummy_waker), Poll::Ready((1, 2))); - } + // #[test] + // fn never_wake() { + // let f1 = poll_fn(|_| Poll::::Pending); + // let f2 = poll_fn(|_| Poll::::Pending); + // let dummy_waker = noop_wake(); + // let join = (f1, f2).join(); + // for _ in 0..10 { + // assert_eq!(join.poll(&dummy_waker), Poll::Pending); + // } + // } + + // #[test] + // fn basic() { + // let f1 = poll_fn(|_| Poll::Ready(1)); + // let f2 = poll_fn(|_| Poll::Ready(2)); + // let dummy_waker = noop_wake(); + // assert_eq!(f1.along_with(f2).poll(&dummy_waker), Poll::Ready((1, 2))); + // } } diff --git a/futures-combinators/src/lib.rs b/futures-combinators/src/lib.rs index 187be26..88f3c83 100644 --- a/futures-combinators/src/lib.rs +++ b/futures-combinators/src/lib.rs @@ -1,5 +1,6 @@ -mod join; -mod race; +pub mod join; +// pub mod race; +mod wake; use join::*; -use race::*; +// use race::*; diff --git a/futures-combinators/src/wake.rs b/futures-combinators/src/wake.rs new file mode 100644 index 0000000..841df6e --- /dev/null +++ b/futures-combinators/src/wake.rs @@ -0,0 +1,113 @@ +use std::{ + array, cell::Cell, marker::PhantomPinned, pin::Pin, ptr::NonNull, + task::Context, +}; + +use futures_compat::WakePtr; +use futures_core::Wake; +use lifetime_guard::{guard::RefGuard, guard::ValueGuard}; + +pub struct WakeArray { + parent: RefGuard, + children: [ValueGuard; N], + stores: [WakeStore; N], + marker: PhantomPinned, +} + +impl WakeArray { + pub fn new() -> Self { + Self { + parent: RefGuard::new(), + children: array::from_fn(|_| ValueGuard::new(None)), + stores: array::from_fn(|_| WakeStore::new()), + marker: PhantomPinned, + } + } + + pub fn register_parent( + self: Pin<&Self>, + parent: Pin<&ValueGuard>, + ) { + unsafe { Pin::new_unchecked(&self.parent) }.register(parent); + } + + /// Returns pinned reference to child ValueGuard + /// returns None if n is not in 0..N + pub fn child_guard_ptr( + self: Pin<&Self>, + index: usize, + ) -> Option>> { + // TODO remove bounds checking, break api when https://github.com/rust-lang/rust/issues/123646 + if index >= N { + return None; + } + + let wake_store = unsafe { + NonNull::new_unchecked(self.stores.get(index).unwrap_unchecked() + as *const dyn Wake + as *mut dyn Wake) + }; + let child_guard = + unsafe { self.get_ref().children.get(index).unwrap_unchecked() }; + + child_guard.set(Some(wake_store)); + + Some(unsafe { Pin::new_unchecked(child_guard) }) + } + + pub fn take_woken(self: Pin<&Self>, index: usize) -> Option { + self.stores.get(index).map(|store| store.take_woken()) + } +} + +pub struct WakeStore { + wake_parent: Cell>>>, + activated: Cell, +} + +impl WakeStore { + pub fn new() -> Self { + Self { + wake_parent: Cell::new(None), + activated: Cell::new(false), + } + } + + pub fn set_parent(&self, parent: &RefGuard) { + self.wake_parent.set(Some(parent.into())); + } + + pub fn take_woken(&self) -> bool { + self.activated.replace(false) + } +} + +impl Wake for WakeStore { + fn wake(&self) { + self.activated.set(true); + if let Some(parent) = self + .wake_parent + .get() + .map(|guard_ptr| unsafe { &*guard_ptr.as_ptr() }) + .and_then(|guard| guard.get()) + .flatten() + { + unsafe { &*parent.as_ptr() }.wake(); + } + } +} + +pub unsafe fn wake_bespoke_waker(waker: &std::task::Waker) { + unsafe { + let guard = futures_compat::waker_to_guard(waker); + if let Some(wake) = guard.get() { + (*wake.as_ptr()).wake(); + } + } +} + +pub struct DummyWaker; + +impl Wake for DummyWaker { + fn wake(&self) {} +} diff --git a/futures-compat/src/lib.rs b/futures-compat/src/lib.rs index 92ca5f9..ceea754 100644 --- a/futures-compat/src/lib.rs +++ b/futures-compat/src/lib.rs @@ -3,10 +3,12 @@ use std::{ pin::Pin, + ptr::NonNull, task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, }; -use lifetime_guard::atomic_guard::AtomicValueGuard; +use futures_core::Wake; +use lifetime_guard::{atomic_guard::AtomicValueGuard, guard::ValueGuard}; static EVIL_VTABLE: RawWakerVTable = RawWakerVTable::new( |_| panic!("wtf"), @@ -15,14 +17,26 @@ static EVIL_VTABLE: RawWakerVTable = RawWakerVTable::new( |_| panic!("wtf"), ); -/// Coerces a pinned `AtomicValueGuard` reference to a `Waker` for use in +pub type WakePtr = Option>; + +/// Coerces a pinned `ValueGuard` reference to a `Waker` for use in /// `core::future::Future` /// /// Any usage or storage of the resulting `Waker` is undefined behavior. -pub unsafe fn guard_to_waker(guard: Pin<&AtomicValueGuard>) -> Waker { +pub unsafe fn guard_to_waker(guard: Pin<&ValueGuard>) -> Waker { + unsafe { + Waker::from_raw(RawWaker::new( + guard.get_ref() as *const ValueGuard as *const (), + &EVIL_VTABLE, + )) + } +} +pub unsafe fn atomic_guard_to_waker( + guard: Pin<&AtomicValueGuard>, +) -> Waker { unsafe { Waker::from_raw(RawWaker::new( - guard.get_ref() as *const AtomicValueGuard as *const (), + guard.get_ref() as *const AtomicValueGuard as *const (), &EVIL_VTABLE, )) } @@ -32,13 +46,33 @@ pub unsafe fn guard_to_waker(guard: Pin<&AtomicValueGuard>) -> Waker { /// /// This should only be used to undo the work of `guard_to_waker`. pub unsafe fn waker_to_guard<'a>( - waker: Waker, -) -> Pin<&'a AtomicValueGuard> { + waker: &Waker, +) -> Pin<&'a ValueGuard> { + unsafe { + Pin::new_unchecked(&*(waker.data() as *const ValueGuard)) + } +} +pub unsafe fn waker_to_atomic_guard<'a>( + waker: &Waker, +) -> Pin<&'a AtomicValueGuard> { unsafe { - Pin::new_unchecked(&*(waker.data() as *const AtomicValueGuard)) + Pin::new_unchecked(&*(waker.data() as *const AtomicValueGuard)) } } +// TODO should probably return impl futures_core::Future, same for next fn +pub unsafe fn std_future_to_bespoke( + future: F, +) -> NormalFutureWrapper { + NormalFutureWrapper(future) +} + +pub unsafe fn bespoke_future_to_std( + future: F, +) -> BespokeFutureWrapper { + BespokeFutureWrapper(future) +} + /// wraps `core::future::Future` in impl of `bcsc:Future` #[repr(transparent)] pub struct NormalFutureWrapper(F); @@ -51,26 +85,14 @@ impl futures_core::Future for NormalFutureWrapper { } } -impl NormalFutureWrapper { - pub unsafe fn from_std_future(future: F) -> Self { - Self(future) - } -} - /// wraps custom `bcsc::Future` in impl of `core::future::Future` #[repr(transparent)] -pub struct CustomFutureWrapper(F); +pub struct BespokeFutureWrapper(F); -impl core::future::Future for CustomFutureWrapper { +impl core::future::Future for BespokeFutureWrapper { type Output = F::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { unsafe { self.map_unchecked_mut(|this| &mut this.0).poll(cx) } } } - -impl CustomFutureWrapper { - pub unsafe fn from_custom_future(future: F) -> Self { - Self(future) - } -} diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index 1243cf5..2630116 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -123,3 +123,8 @@ where ::poll(self.as_deref_mut(), cx) } } + +/// temporary trait until Fn::call is stabilized +pub trait Wake { + fn wake(&self); +} diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml deleted file mode 100644 index 23388cf..0000000 --- a/futures-util/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "futures-util" -version.workspace = true -rust-version.workspace = true -edition.workspace = true -license.workspace = true -authors.workspace = true -repository.workspace = true -homepage.workspace = true - -[dependencies] diff --git a/futures-util/src/ext.rs b/futures-util/src/ext.rs deleted file mode 100644 index 4137a37..0000000 --- a/futures-util/src/ext.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub trait FutureExt: Future { - fn along_with(self, other: impl Future) - where - Self: Sized, - { - // Join2 - } - // fn then(self, other: impl Future) - // where - // Self: Sized, - // { - - // } -} diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs deleted file mode 100644 index e3cf6a4..0000000 --- a/futures-util/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -mod ext; -mod maybe_done; -mod poll_fn; -mod wakers; - -use futures_core::ScopedFuture; -pub use maybe_done::*; -pub use poll_fn::poll_fn; -pub use wakers::*; - -// Just a helper function to ensure the futures we're returning all have the -// right implementations. -pub(crate) fn assert_future<'scope, T, F>(future: F) -> F -where - F: ScopedFuture<'scope, Output = T>, -{ - future -} diff --git a/futures-util/src/maybe_done.rs b/futures-util/src/maybe_done.rs deleted file mode 100644 index 03a2a88..0000000 --- a/futures-util/src/maybe_done.rs +++ /dev/null @@ -1,147 +0,0 @@ -//! Definition of the MaybeDone combinator - -use futures_core::{ScopedFuture, Wake}; - -use super::assert_future; -use std::{ - cell::UnsafeCell, - task::{Poll, ready}, -}; - -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MaybeDone<'scope, Fut: ScopedFuture<'scope>> { - state: UnsafeCell>, -} - -/// A future that may have completed. -/// -/// This is created by the [`maybe_done()`] function. -#[derive(Debug)] -pub enum MaybeDoneState<'scope, Fut: ScopedFuture<'scope>> { - /// A not-yet-completed future - Future(Fut), - /// The output of the completed future - Done(Fut::Output), - /// The empty variant after the result of a [`MaybeDone`] has been - /// taken using the [`take_output`](MaybeDone::take_output) method. - Gone, -} - -/// Wraps a future into a `MaybeDone` -/// -/// -pub fn maybe_done<'scope, Fut: ScopedFuture<'scope>>( - future: Fut, -) -> MaybeDone<'scope, Fut> { - assert_future::<(), _>(MaybeDone { - state: MaybeDoneState::Future(future).into(), - }) -} - -impl<'scope, Fut: ScopedFuture<'scope>> MaybeDone<'scope, Fut> { - /// Attempt to take the output of a `MaybeDone` without driving it - /// towards completion. - #[inline] - pub fn take_output(&self) -> Option { - match unsafe { &*self.state.get() } { - MaybeDoneState::Done(_) => {} - MaybeDoneState::Future(_) | MaybeDoneState::Gone => return None, - } - match unsafe { self.state.get().replace(MaybeDoneState::Gone) } { - MaybeDoneState::Done(output) => Some(output), - _ => unreachable!(), - } - } - - /// Returns an immutable reference to the internal state of this future - /// - /// # Safety - /// You must not hold this reference past any use of any other methods of this struct - pub unsafe fn get_state(&self) -> &MaybeDoneState<'scope, Fut> { - unsafe { &*self.state.get() } - } - - pub fn is_done(&self) -> bool { - match unsafe { &*self.state.get() } { - MaybeDoneState::Future(_) => false, - MaybeDoneState::Done(_) | MaybeDoneState::Gone => true, - } - } -} - -impl<'scope, Fut: ScopedFuture<'scope>> ScopedFuture<'scope> - for MaybeDone<'scope, Fut> -{ - type Output = (); - - fn poll(&'scope self, cx: &'scope dyn Wake<'scope>) -> Poll { - match unsafe { &*self.state.get() } { - MaybeDoneState::Future(f) => { - let res = ready!(f.poll(cx)); - // this is fine because no immutable references currently exist - unsafe { self.state.get().replace(MaybeDoneState::Done(res)) }; - } - MaybeDoneState::Done(_) => {} - MaybeDoneState::Gone => { - panic!("MaybeDone polled after value taken") - } - } - Poll::Ready(()) - } -} - -#[cfg(test)] -mod tests { - use std::cell::Cell; - - use crate::poll_fn; - - use crate::noop_wake; - - use super::*; - - #[test] - fn immediate_return() { - let immediate = poll_fn(|_| Poll::Ready(1)); - let future = maybe_done(immediate); - let wake = noop_wake(); - match unsafe { future.get_state() } { - MaybeDoneState::Future(_) => {} - MaybeDoneState::Done(_) | MaybeDoneState::Gone => { - panic!("should be MaybeDoneState::Future") - } - } - assert_eq!(future.poll(&wake), Poll::Ready(())); - match unsafe { future.get_state() } { - MaybeDoneState::Done(_) => {} - MaybeDoneState::Future(_) | MaybeDoneState::Gone => { - panic!("should be MaybeDoneState::Done") - } - } - assert_eq!(future.take_output(), Some(1)); - assert_eq!(future.take_output(), None); - } - - #[test] - fn normal() { - let x = Cell::new(0); - let poll = poll_fn(|wake| { - wake.wake(); - x.set(x.get() + 1); - if x.get() == 4 { - Poll::Ready(x.get()) - } else { - Poll::Pending - } - }); - let future = maybe_done(poll); - let noop = noop_wake(); - for _ in 0..3 { - assert_eq!(future.poll(&noop), Poll::Pending); - assert_eq!(future.take_output(), None); - } - assert_eq!(future.poll(&noop), Poll::Ready(())); - assert_eq!(future.poll(&noop), Poll::Ready(())); - assert_eq!(future.take_output(), Some(4)); - } -} diff --git a/futures-util/src/poll_fn.rs b/futures-util/src/poll_fn.rs deleted file mode 100644 index aa69d01..0000000 --- a/futures-util/src/poll_fn.rs +++ /dev/null @@ -1,39 +0,0 @@ -use core::fmt; -use std::task::Poll; - -use futures_core::{ScopedFuture, Wake}; - -use crate::assert_future; - -/// Future for the [`poll_fn`] function. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct PollFn { - f: F, -} - -/// Creates a new future wrapping around a function returning [`Poll`]. -/// -/// Polling the returned future delegates to the wrapped function. -pub fn poll_fn<'scope, T, F>(f: F) -> PollFn -where - F: Fn(&'scope dyn Wake) -> Poll, -{ - assert_future::(PollFn { f }) -} - -impl fmt::Debug for PollFn { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PollFn").finish() - } -} - -impl<'scope, T, F> ScopedFuture<'scope> for PollFn -where - F: Fn(&'scope dyn Wake) -> Poll, -{ - type Output = T; - - fn poll(&self, wake: &'scope dyn Wake) -> Poll { - (&self.f)(wake) - } -} diff --git a/futures-util/src/wakers.rs b/futures-util/src/wakers.rs deleted file mode 100644 index ecbc1e2..0000000 --- a/futures-util/src/wakers.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::cell::Cell; - -use futures_core::Wake; - -pub struct WakeStore<'scope> { - parent: Cell>>, - ready: Cell, -} - -impl<'scope> WakeStore<'scope> { - pub fn new() -> Self { - Self { - parent: Option::None.into(), - ready: true.into(), - } - } - - pub fn set_parent(&self, parent: &'scope dyn Wake<'scope>) { - self.parent.replace(Some(parent)); - } - - pub fn take_ready(&self) -> bool { - self.ready.replace(false) - } -} - -impl<'scope> Wake<'scope> for WakeStore<'scope> { - fn wake(&self) { - self.ready.replace(true); - if let Some(parent) = &self.parent.get() { - parent.wake(); - } - } -} - -pub struct FnWake(F); - -impl<'scope, F: Fn()> Wake<'scope> for FnWake { - fn wake(&self) { - self.0() - } -} - -impl<'scope, F: Fn()> From for FnWake { - fn from(value: F) -> Self { - FnWake(value) - } -} - -pub fn noop_wake() -> FnWake { - FnWake(|| {}) -} diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 837374f..63a8d0a 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -12,4 +12,3 @@ homepage.workspace = true futures-combinators = { workspace = true } futures-compat = { workspace = true } futures-derive = { workspace = true } -futures-util = { workspace = true } diff --git a/lifetime-guard/src/lib.rs b/lifetime-guard/src/lib.rs index 482569d..8a583d1 100644 --- a/lifetime-guard/src/lib.rs +++ b/lifetime-guard/src/lib.rs @@ -1,6 +1,7 @@ #![doc = include_str!("../README.md")] #![no_std] +// pub mod base; pub mod guard; #[cfg(feature = "atomics")]