Skip to content

Commit

Permalink
feat: add new Extender methods which don't use dynamic dispatch
Browse files Browse the repository at this point in the history
Methods fn_, fn_mut, fn_once, fn_unsync, future, future_unpin work
on any pointer type you can give them, including Box, references,
RefOnce, Arc, Rc.
  • Loading branch information
zetanumbers committed Jun 2, 2024
1 parent 5a6c897 commit 5bc5e01
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 17 deletions.
8 changes: 4 additions & 4 deletions examples/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ fn main() {

scope_lock::lock_scope(|e| {
thread::spawn({
let f = e.extend_fn_once_box(|()| {
let f = e.fn_(Box::new(|()| {
println!("hello from the first scoped thread");
// We can borrow `a` here.
dbg!(&a);
});
}));
move || f(())
});
thread::spawn({
let f = e.extend_fn_once_box(|()| {
let mut f = e.fn_mut(Box::new(|()| {
println!("hello from the second scoped thread");
// We can even mutably borrow `x` here,
// because no other threads are using it.
x += a[0] + a[2];
});
}));
move || f(())
});
println!("hello from the main thread");
Expand Down
41 changes: 41 additions & 0 deletions examples/ref_once.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use std::{mem::MaybeUninit, thread};

use scope_lock::RefOnce;

fn main() {
let mut a = vec![1, 2, 3];
let mut x = 0;

let mut slots = (MaybeUninit::uninit(), MaybeUninit::uninit());

scope_lock::lock_scope(|e| {
thread::spawn({
let f = e.fn_(RefOnce::new(
|()| {
println!("hello from the first scoped thread");
// We can borrow `a` here.
dbg!(&a);
},
&mut slots.0,
));
move || f(())
});
thread::spawn({
let mut f = e.fn_mut(RefOnce::new(
|()| {
println!("hello from the second scoped thread");
// We can even mutably borrow `x` here,
// because no other threads are using it.
x += a[0] + a[2];
},
&mut slots.1,
));
move || f(())
});
println!("hello from the main thread");
});

// After the scope, we can modify and access our variables again:
a.push(4);
assert_eq!(x, a.len());
}
8 changes: 4 additions & 4 deletions examples/references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ fn main() {

scope_lock::lock_scope(|e| {
thread::spawn({
let f = e.extend_fn(f1);
move || f.call(())
let f = e.fn_(f1);
move || f(())
});
thread::spawn({
let mut f = e.extend_fn_mut(f2);
move || f.call(())
let mut f = e.fn_mut(f2);
move || f(())
});
println!("hello from the main thread");
});
Expand Down
107 changes: 107 additions & 0 deletions src/extended/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,94 @@ use crate::pointer_like::erased_static::{fn_call, fn_call_mut, fn_call_once, fn_
use crate::pointer_like::{PointerDeref, PointerDerefMut, PointerIntoInner};
use crate::{ref_once, Extender, RefOnce};

use super::{UnsafeAssertSend, UnsafeAssertSync};

impl<'scope, 'env> Extender<'scope, 'env> {
pub fn fn_once<'extended, P, I, O>(
&'scope self,
f: P,
) -> impl FnOnce(I) -> O + Send + Sync + 'extended
where
'extended: 'scope,
P: PointerIntoInner + Send,
P::Pointee: FnOnce(I) -> O,
I: Send + 'extended,
O: Send + 'extended,
{
let reference_guard =
unsafe { mem::transmute::<Reference<'_>, Reference<'static>>(self.rc.acquire()) };
// It is sync since there's no way to interact with a reference to returned type
let f = UnsafeAssertSync(UnsafeAssertSend(unsafe { extend_fn_once_unchecked(f) }));
move |i| {
let f = f;
let out = f.0 .0(i);
drop(reference_guard);
out
}
}

pub fn fn_mut<'extended, P, I, O>(
&'scope self,
f: P,
) -> impl FnMut(I) -> O + Send + Sync + 'extended
where
'extended: 'scope,
P: PointerDerefMut + Send,
P::Pointee: FnMut(I) -> O,
I: Send + 'extended,
O: Send + 'extended,
{
let mut reference_guard =
unsafe { mem::transmute::<Reference<'_>, Reference<'static>>(self.rc.acquire()) };
// It is sync since there's no way to interact with a reference to returned type
let mut f = UnsafeAssertSync(UnsafeAssertSend(unsafe { extend_fn_mut_unchecked(f) }));
move |i| {
let _reference_guard = &mut reference_guard;
let f = &mut f;
f.0 .0(i)
}
}

pub fn fn_<'extended, P, I, O>(&'scope self, f: P) -> impl Fn(I) -> O + Send + Sync + 'extended
where
'extended: 'scope,
P: PointerDeref + Send,
P::Pointee: Fn(I) -> O + Sync,
I: Send + 'extended,
O: Send + 'extended,
{
let reference_guard =
unsafe { mem::transmute::<Reference<'_>, Reference<'static>>(self.rc.acquire()) };
let f = UnsafeAssertSync(UnsafeAssertSend(unsafe { extend_fn_unchecked(f) }));
move |i| {
let _reference_guard = &reference_guard;
let f = &f;
f.0 .0(i)
}
}

pub fn fn_unsync<'extended, P, I, O>(&'scope self, f: P) -> impl Fn(I) -> O + Send + 'extended
where
'extended: 'scope,
P: PointerDeref + Send,
P::Pointee: Fn(I) -> O,
I: Send + 'extended,
O: Send + 'extended,
{
let reference_guard =
unsafe { mem::transmute::<Reference<'_>, Reference<'static>>(self.rc.acquire()) };
let f = UnsafeAssertSend(unsafe { extend_fn_unchecked(f) });
move |i| {
let _reference_guard = &reference_guard;
let f = &f;
f.0(i)
}
}

#[deprecated(
since = "0.2.5",
note = "`extend_fn` is deprecated as it utilizes dynamic dispatch and works exclusivelly with references, use [`Extender::fn_`](#method.fn_) instead"
)]
pub fn extend_fn<F, I, O>(&'scope self, f: &'scope F) -> legacy::ExtendedFn<I, O>
where
F: Fn(I) -> O + Sync + 'scope,
Expand All @@ -27,6 +114,10 @@ impl<'scope, 'env> Extender<'scope, 'env> {
}
}

#[deprecated(
since = "0.2.5",
note = "`extend_fn_box` is deprecated as it utilizes dynamic dispatch and requires allocation, use [`Extender::fn_`](#method.fn_) instead"
)]
pub fn extend_fn_box<F, I, O>(&'scope self, f: F) -> Box<dyn Fn(I) -> O + Send + Sync>
where
F: Fn(I) -> O + Send + Sync + 'scope,
Expand All @@ -46,6 +137,10 @@ impl<'scope, 'env> Extender<'scope, 'env> {
}
}

#[deprecated(
since = "0.2.5",
note = "`extend_fn_mut` is deprecated as it utilizes dynamic dispatch and works exclusivelly with mutable references, use [`Extender::fn_mut`](#method.fn_mut) instead"
)]
pub fn extend_fn_mut<F, I, O>(&'scope self, f: &'scope mut F) -> legacy::ExtendedFnMut<I, O>
where
F: FnMut(I) -> O + Send + 'scope,
Expand All @@ -65,6 +160,10 @@ impl<'scope, 'env> Extender<'scope, 'env> {
}
}

#[deprecated(
since = "0.2.5",
note = "`extend_fn_mut_box` is deprecated as it utilizes dynamic dispatch and requires allocation, use [`Extender::fn_mut`](#method.fn_mut) instead"
)]
pub fn extend_fn_mut_box<F, I, O>(&'scope self, mut f: F) -> Box<dyn FnMut(I) -> O + Send>
where
F: FnMut(I) -> O + Send + 'scope,
Expand All @@ -83,6 +182,10 @@ impl<'scope, 'env> Extender<'scope, 'env> {
}
}

#[deprecated(
since = "0.2.5",
note = "`extend_fn_once` is deprecated as it utilizes dynamic dispatch and works exclusivelly with `RefOnce`, use [`Extender::fn_once`](#method.fn_once) instead"
)]
pub fn extend_fn_once<F, I, O>(
&'scope self,
f: RefOnce<'scope, F>,
Expand All @@ -105,6 +208,10 @@ impl<'scope, 'env> Extender<'scope, 'env> {
}
}

#[deprecated(
since = "0.2.5",
note = "`extend_fn_once_box` is deprecated as it utilizes dynamic dispatch and requires allocation, use [`Extender::fn_once`](#method.fn_once) instead"
)]
pub fn extend_fn_once_box<F, I, O>(&'scope self, f: F) -> Box<dyn FnOnce(I) -> O + Send>
where
F: FnOnce(I) -> O + Send + 'scope,
Expand Down
78 changes: 78 additions & 0 deletions src/extended/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,80 @@ use crate::pointer_like::{PointerDerefMut, PointerPinUnforgotten};
use crate::Extender;

impl<'scope, 'env> Extender<'scope, 'env> {
pub fn future<'extended, P, O>(
&'scope self,
f: P,
) -> impl Future<Output = O> + Send + Sync + 'extended
where
'extended: 'scope,
P: PointerPinUnforgotten + Send + 'scope,
P::Pointee: Future<Output = O>,
O: Send + 'extended,
{
let reference_guard =
unsafe { mem::transmute::<Reference<'_>, Reference<'static>>(self.rc.acquire()) };

struct Fut<T> {
inner: T,
_reference_guard: Reference<'static>,
}
unsafe impl<T> Send for Fut<T> {}
unsafe impl<T> Sync for Fut<T> {}
impl<T: Future> Future for Fut<T> {
type Output = T::Output;

fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner).poll(cx) }
}
}

// It is sync since there's no way to interact with a reference to returned type
let f = Fut {
inner: unsafe { extend_future_unchecked(f) },
_reference_guard: reference_guard,
};
f
}

pub fn future_unpin<'extended, P, O>(
&'scope self,
f: P,
) -> impl Future<Output = O> + Send + Sync + Unpin + 'extended
where
'extended: 'scope,
P: PointerDerefMut + Send + 'scope,
P::Pointee: Future<Output = O> + Unpin,
O: Send + 'extended,
{
let reference_guard =
unsafe { mem::transmute::<Reference<'_>, Reference<'static>>(self.rc.acquire()) };

struct Fut<T> {
inner: T,
_reference_guard: Reference<'static>,
}
unsafe impl<T> Send for Fut<T> {}
unsafe impl<T> Sync for Fut<T> {}
impl<T: Future + Unpin> Future for Fut<T> {
type Output = T::Output;

fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
Pin::new(&mut self.get_mut().inner).poll(cx)
}
}

// It is sync since there's no way to interact with a reference to returned type
let f = Fut {
inner: unsafe { extend_future_unpin_unchecked(f) },
_reference_guard: reference_guard,
};
f
}

#[deprecated(
since = "0.2.5",
note = "`extend_future` is deprecated as it utilizes dynamic dispatch and works exclusivelly on pinned mutable references, use [`Extender::future`](#method.future) instead"
)]
pub fn extend_future<F>(
&'scope self,
f: Pin<&'scope mut F>,
Expand All @@ -33,6 +107,10 @@ impl<'scope, 'env> Extender<'scope, 'env> {
}

/// Extend lifetime of a future.
#[deprecated(
since = "0.2.5",
note = "`extend_future_box` is deprecated as it utilizes dynamic dispatch and requires allocation, use [`Extender::future`](#method.future) instead"
)]
pub fn extend_future_box<F>(
&'scope self,
f: F,
Expand Down
6 changes: 6 additions & 0 deletions src/extended/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,9 @@ impl Drop for ReferenceGuard<'_, '_> {
mem::forget(self.extender.rc.counter.write());
}
}

struct UnsafeAssertSync<T>(T);
unsafe impl<T> Sync for UnsafeAssertSync<T> {}

struct UnsafeAssertSend<T>(T);
unsafe impl<T> Send for UnsafeAssertSend<T> {}
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
//!
//! ## Examples
//!
//! Using boxes (requires allocation)
//! Using boxes (requires allocation):
//!
//! ```
#![doc = include_str!("../examples/boxed.rs")]
//! ```
//!
//! Using references
//! Using [`RefOnce`]:
//!
//! ```
#![doc = include_str!("../examples/references.rs")]
#![doc = include_str!("../examples/ref_once.rs")]
//! ```
#![warn(
unsafe_op_in_unsafe_fn,
Expand Down
Loading

0 comments on commit 5bc5e01

Please sign in to comment.