diff --git a/src/lib.rs b/src/lib.rs index 669c082..6268acf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,10 +22,12 @@ use std::{ borrow::{Borrow, BorrowMut}, + future::Future, marker::PhantomData, mem, ops::{Deref, DerefMut}, - ptr, + pin::Pin, + ptr, task, }; use parking_lot::{RwLock, RwLockReadGuard}; @@ -170,7 +172,45 @@ impl<'scope, 'env> Extender<'scope, 'env> { } } - // TODO: Add futures + pub fn extend_future(&'scope self, f: Pin<&'scope mut F>) -> ExtendedFuture + where + F: Future + Send + 'scope, + F::Output: Send + 'scope, + { + unsafe { + ExtendedFuture { + func: mem::transmute::< + ptr::NonNull + Send + '_>, + ptr::NonNull + Send + 'static>, + >(ptr::NonNull::from(f.get_unchecked_mut())), + _reference_guard: mem::transmute::, Reference<'static>>( + self.rc.acquire(), + ), + } + } + } + + /// Extend lifetime of a future. Use [`Box::into_pin`] to pin the future. + pub fn extend_future_box( + &'scope self, + f: F, + ) -> Pin + Send>> + where + F: Future + Send + 'scope, + F::Output: Send + 'scope, + { + unsafe { + let reference_guard = + mem::transmute::, Reference<'static>>(self.rc.acquire()); + Box::into_pin(mem::transmute::< + Box + Send + 'scope>, + Box + Send>, + >(Box::new(async move { + let _reference_guard = &reference_guard; + f.await + }))) + } + } } struct ReferenceCounter<'a> { @@ -204,7 +244,7 @@ impl Drop for ReferenceGuard<'_, '_> { // TODO: Erase argument and output somehow too pub struct ExtendedFn { - // TODO: Could make a single dynamicly sized struct + // TODO: Could make a single dynamically sized struct func: ptr::NonNull O + Sync>, _reference_guard: Reference<'static>, } @@ -218,9 +258,11 @@ impl ExtendedFn { // Almost just a simple reference, so it is Send and Sync unsafe impl Send for ExtendedFn {} unsafe impl Sync for ExtendedFn {} +// FIXME: unsafe impl Send for ExtendedFnMut where I: Send, O: Send {} +// FIXME: unsafe impl Sync for ExtendedFnMut where I: Send, O: Send {} pub struct ExtendedFnMut { - // TODO: Could make a single dynamicly sized struct + // TODO: Could make a single dynamically sized struct func: ptr::NonNull O + Send>, _reference_guard: Reference<'static>, } @@ -232,11 +274,16 @@ impl ExtendedFnMut { } unsafe impl Send for ExtendedFnMut {} +// FIXME: unsafe impl Send for ExtendedFnMut where I: Send, O: Send {} #[repr(transparent)] struct Once(mem::ManuallyDrop); -// TODO: Fix clippy warning +/// Object-safe FnOnce +/// +/// # Safety +/// +/// [`ObjectSafeFnOnce::call_once`] may be called at most once. unsafe trait ObjectSafeFnOnce { type Output; @@ -244,7 +291,7 @@ unsafe trait ObjectSafeFnOnce { /// /// # Safety /// - /// Must be called at most once. + /// May be called at most once. unsafe fn call_once(&mut self, input: I) -> Self::Output; } @@ -260,8 +307,7 @@ where } // TODO: split into separate crate -// TODO: Pin support -// TODO: Copy support +// TODO: Pin support (into_pin) pub struct RefOnce<'a, T: ?Sized> { slot: &'a mut Once, } @@ -320,7 +366,7 @@ impl Drop for RefOnce<'_, T> { } pub struct ExtendedFnOnce { - // TODO: Could make a single dynamicly sized struct + // TODO: Could make a single dynamically sized struct func: ptr::NonNull + Send>, reference_guard: Reference<'static>, } @@ -340,6 +386,23 @@ impl Drop for ExtendedFnOnce { } unsafe impl Send for ExtendedFnOnce {} +// FIXME: unsafe impl Send for ExtendedFnOnce where I: Send, O: Send {} + +pub struct ExtendedFuture { + // TODO: Could make a single dynamically sized struct + func: ptr::NonNull + Send>, + _reference_guard: Reference<'static>, +} + +impl Future for ExtendedFuture { + type Output = O; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + unsafe { Pin::new_unchecked(self.get_unchecked_mut().func.as_mut()) }.poll(cx) + } +} + +unsafe impl Send for ExtendedFuture where O: Send {} // TODO: zero case test // TODO: tests from rust std::thread::scope diff --git a/tests/composed.rs b/tests/composed.rs new file mode 100644 index 0000000..9a8d26e --- /dev/null +++ b/tests/composed.rs @@ -0,0 +1,24 @@ +use std::future::Future; + +fn check_static_closure_async(_: F) +where + F: FnOnce(i32) -> O + 'static, + O: Future + 'static, +{ +} + +#[test] +fn closure_async() { + let a = 37; + scope_lock::lock_scope(|e| { + check_static_closure_async({ + let f = e.extend_fn_once_box(|b| { + e.extend_future_box(async move { + dbg!(&a); + dbg!(a + b); + }) + }); + move |b| f(b) + }); + }); +}