+144
futures-core/src/compat.rs
+144
futures-core/src/compat.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
+
use crate::{ScopedFuture, Wake};
17
+
use std::{
18
+
cell::UnsafeCell,
19
+
marker::PhantomData,
20
+
mem,
21
+
pin::Pin,
22
+
task::{Context, Poll, Waker},
23
+
};
24
+
25
+
/// RawWaker: fat ptr (*const (), &'static RawWakerVTable)
26
+
/// &'scope dyn Wake fat ptr: (&'scope (), &'scope WakeVTable)
27
+
///
28
+
/// can transmute between them, but the waker will be completely invalid!
29
+
30
+
/// wraps an internal ScopedFuture, implements Future
31
+
pub struct ScopedFutureWrapper<'scope, F: ScopedFuture<'scope> + 'scope> {
32
+
inner: UnsafeCell<F>,
33
+
marker: PhantomData<&'scope ()>,
34
+
}
35
+
36
+
impl<'scope, F: ScopedFuture<'scope> + 'scope> Future
37
+
for ScopedFutureWrapper<'scope, F>
38
+
{
39
+
type Output = F::Output;
40
+
41
+
fn poll(
42
+
self: std::pin::Pin<&mut Self>,
43
+
cx: &mut std::task::Context<'_>,
44
+
) -> 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
+
}
77
+
}
78
+
}
79
+
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
+
}
86
+
}
87
+
}
88
+
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
+
}
98
+
99
+
impl<'scope, F: Future> ScopedFuture<'scope>
100
+
for UnscopedFutureWrapper<'scope, F>
101
+
{
102
+
type Output = F::Output;
103
+
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))
134
+
}
135
+
}
136
+
137
+
impl<'scope, F: Future> UnscopedFutureWrapper<'scope, F> {
138
+
pub unsafe fn from_future(f: F) -> Self {
139
+
Self {
140
+
inner: f.into(),
141
+
marker: PhantomData,
142
+
}
143
+
}
144
+
}
+10
-2
futures-core/src/lib.rs
+10
-2
futures-core/src/lib.rs
···
1
-
use std::task::{Poll, RawWaker};
1
+
use std::{
2
+
cell::UnsafeCell,
3
+
marker::PhantomData,
4
+
mem,
5
+
pin::Pin,
6
+
task::{Context, Poll, RawWaker, Waker},
7
+
};
8
+
9
+
pub mod compat;
2
10
3
11
/// A task that can be woken.
4
12
///
···
11
19
/// ScopedFuture represents a unit of asynchronous computation that must be
12
20
/// polled by an external actor.
13
21
///
14
-
/// Implementations access a context (`cx: &'scope mut dyn Wake`) to signal
22
+
/// Implementations access a context (`cx: &'scope dyn Wake`) to signal
15
23
/// they are ready to resume execution.
16
24
///
17
25
/// A notable difference between `bcsc::ScopedFuture` and `core::task::Future`
+86
futures-core/src/task.rs
+86
futures-core/src/task.rs
···
1
+
use crate::wake::ScopedWaker;
2
+
3
+
/// A task that can be woken.
4
+
///
5
+
/// This acts as a handle for a reactor to indicate when a `ScopedFuture` is
6
+
/// once again ready to be polled.
7
+
pub trait Wake<'scope> {
8
+
fn wake(&self);
9
+
}
10
+
11
+
impl<'scope> From<&'scope dyn Wake<'scope>> for ScopedWaker<'scope> {
12
+
fn from(value: &'scope dyn Wake) -> Self {}
13
+
}
14
+
15
+
fn raw_waker<'scope, W: Wake<'scope> + Send + Sync>(
16
+
waker: &'scope dyn Wake,
17
+
) -> RawWaker {
18
+
// Increment the reference count of the arc to clone it.
19
+
20
+
//
21
+
22
+
// The #[inline(always)] is to ensure that raw_waker and clone_waker are
23
+
24
+
// always generated in the same code generation unit as one another, and
25
+
26
+
// therefore that the structurally identical const-promoted RawWakerVTable
27
+
28
+
// within both functions is deduplicated at LLVM IR code generation time.
29
+
30
+
// This allows optimizing Waker::will_wake to a single pointer comparison of
31
+
32
+
// the vtable pointers, rather than comparing all four function pointers
33
+
34
+
// within the vtables.
35
+
36
+
#[inline(always)]
37
+
38
+
unsafe fn clone_waker<W: Wake + Send + Sync + 'static>(
39
+
waker: *const (),
40
+
) -> RawWaker {
41
+
unsafe { Arc::increment_strong_count(waker as *const W) };
42
+
43
+
RawWaker::new(
44
+
waker,
45
+
&RawWakerVTable::new(
46
+
clone_waker::<W>,
47
+
wake::<W>,
48
+
wake_by_ref::<W>,
49
+
drop_waker::<W>,
50
+
),
51
+
)
52
+
}
53
+
54
+
// Wake by value, moving the Arc into the Wake::wake function
55
+
56
+
unsafe fn wake<W: Wake + Send + Sync + 'static>(waker: *const ()) {
57
+
let waker = unsafe { Arc::from_raw(waker as *const W) };
58
+
59
+
<W as Wake>::wake(waker);
60
+
}
61
+
62
+
// Wake by reference, wrap the waker in ManuallyDrop to avoid dropping it
63
+
64
+
unsafe fn wake_by_ref<W: Wake + Send + Sync + 'static>(waker: *const ()) {
65
+
let waker =
66
+
unsafe { ManuallyDrop::new(Arc::from_raw(waker as *const W)) };
67
+
68
+
<W as Wake>::wake_by_ref(&waker);
69
+
}
70
+
71
+
// Decrement the reference count of the Arc on drop
72
+
73
+
unsafe fn drop_waker<W: Wake + Send + Sync + 'static>(waker: *const ()) {
74
+
unsafe { Arc::decrement_strong_count(waker as *const W) };
75
+
}
76
+
77
+
RawWaker::new(
78
+
Arc::into_raw(waker) as *const (),
79
+
&RawWakerVTable::new(
80
+
clone_waker::<W>,
81
+
wake::<W>,
82
+
wake_by_ref::<W>,
83
+
drop_waker::<W>,
84
+
),
85
+
)
86
+
}