Skip to content

Commit e0dc297

Browse files
committed
Add simple async drop glue generation
Explainer: https://zetanumbers.github.io/book/async-drop-design.html rust-lang#121801
1 parent bae79d9 commit e0dc297

File tree

4 files changed

+288
-0
lines changed

4 files changed

+288
-0
lines changed

core/src/future/async_drop.rs

+271
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
#![unstable(feature = "async_drop", issue = "none")]
2+
3+
use crate::fmt;
4+
use crate::future::{Future, IntoFuture};
5+
use crate::intrinsics::discriminant_value;
6+
use crate::marker::{DiscriminantKind, PhantomPinned};
7+
use crate::mem::MaybeUninit;
8+
use crate::pin::Pin;
9+
use crate::task::{ready, Context, Poll};
10+
11+
/// Asynchronously drops a value by running `AsyncDrop::async_drop`
12+
/// on a value and its fields recursively.
13+
#[unstable(feature = "async_drop", issue = "none")]
14+
pub fn async_drop<T>(value: T) -> AsyncDropOwning<T> {
15+
AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned }
16+
}
17+
18+
/// A future returned by the [`async_drop`].
19+
#[unstable(feature = "async_drop", issue = "none")]
20+
pub struct AsyncDropOwning<T> {
21+
value: MaybeUninit<T>,
22+
dtor: Option<AsyncDropInPlace<T>>,
23+
_pinned: PhantomPinned,
24+
}
25+
26+
#[unstable(feature = "async_drop", issue = "none")]
27+
impl<T> fmt::Debug for AsyncDropOwning<T> {
28+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29+
f.debug_struct("AsyncDropOwning").finish_non_exhaustive()
30+
}
31+
}
32+
33+
#[unstable(feature = "async_drop", issue = "none")]
34+
impl<T> Future for AsyncDropOwning<T> {
35+
type Output = ();
36+
37+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
38+
// SAFETY: Self is pinned thus it is ok to store references to self
39+
unsafe {
40+
let this = self.get_unchecked_mut();
41+
let dtor = Pin::new_unchecked(
42+
this.dtor.get_or_insert_with(|| async_drop_in_place(this.value.as_mut_ptr())),
43+
);
44+
// AsyncDestuctors are idempotent so Self gets idempotency as well
45+
dtor.poll(cx)
46+
}
47+
}
48+
}
49+
50+
#[lang = "async_drop_in_place"]
51+
#[allow(unconditional_recursion)]
52+
// FIXME: Consider if `#[rustc_diagnostic_item = "ptr_drop_in_place"]` is needed?
53+
unsafe fn async_drop_in_place_raw<T: ?Sized>(
54+
to_drop: *mut T,
55+
) -> <T as AsyncDestruct>::AsyncDestructor {
56+
// Code here does not matter - this is replaced by the
57+
// real async drop glue constructor by the compiler.
58+
59+
// SAFETY: see comment above
60+
unsafe { async_drop_in_place_raw(to_drop) }
61+
}
62+
63+
/// Creates the asynchronous destructor of the pointed-to value.
64+
///
65+
/// # Safety
66+
///
67+
/// Behavior is undefined if any of the following conditions are violated:
68+
///
69+
/// * `to_drop` must be [valid](crate::ptr#safety) for both reads and writes.
70+
///
71+
/// * `to_drop` must be properly aligned, even if `T` has size 0.
72+
///
73+
/// * `to_drop` must be nonnull, even if `T` has size 0.
74+
///
75+
/// * The value `to_drop` points to must be valid for async dropping,
76+
/// which may mean it must uphold additional invariants. These
77+
/// invariants depend on the type of the value being dropped. For
78+
/// instance, when dropping a Box, the box's pointer to the heap must
79+
/// be valid.
80+
///
81+
/// * While `async_drop_in_place` is executing or the returned async
82+
/// destructor is alive, the only way to access parts of `to_drop`
83+
/// is through the `self: Pin<&mut Self>` references supplied to
84+
/// the `AsyncDrop::async_drop` methods that `async_drop_in_place`
85+
/// or `AsyncDropInPlace<T>::poll` invokes. This usually means the
86+
/// returned future stores the `to_drop` pointer and user is required
87+
/// to guarantee that dropped value doesn't move.
88+
///
89+
#[unstable(feature = "async_drop", issue = "none")]
90+
pub unsafe fn async_drop_in_place<T: ?Sized>(to_drop: *mut T) -> AsyncDropInPlace<T> {
91+
// SAFETY: `async_drop_in_place_raw` has the same safety requirements
92+
unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) }
93+
}
94+
95+
/// A future returned by the [`async_drop_in_place`].
96+
#[unstable(feature = "async_drop", issue = "none")]
97+
pub struct AsyncDropInPlace<T: ?Sized>(<T as AsyncDestruct>::AsyncDestructor);
98+
99+
#[unstable(feature = "async_drop", issue = "none")]
100+
impl<T: ?Sized> fmt::Debug for AsyncDropInPlace<T> {
101+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102+
f.debug_struct("AsyncDropInPlace").finish_non_exhaustive()
103+
}
104+
}
105+
106+
#[unstable(feature = "async_drop", issue = "none")]
107+
impl<T: ?Sized> Future for AsyncDropInPlace<T> {
108+
type Output = ();
109+
110+
#[inline(always)]
111+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
112+
// SAFETY: This code simply forwards poll call to the inner future
113+
unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }.poll(cx)
114+
}
115+
}
116+
117+
// FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as
118+
// with Drop impls
119+
/// Custom code within the asynchronous destructor.
120+
#[unstable(feature = "async_drop", issue = "none")]
121+
#[lang = "async_drop"]
122+
pub trait AsyncDrop {
123+
/// A future returned by the [`AsyncDrop::async_drop`] to be part
124+
/// of the async destructor.
125+
#[unstable(feature = "async_drop", issue = "none")]
126+
type Dropper<'a>: Future<Output = ()>
127+
where
128+
Self: 'a;
129+
130+
/// Constructs the asynchronous destructor for this type.
131+
#[unstable(feature = "async_drop", issue = "none")]
132+
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>;
133+
}
134+
135+
#[lang = "async_destruct"]
136+
#[rustc_deny_explicit_impl(implement_via_object = false)]
137+
trait AsyncDestruct {
138+
type AsyncDestructor: Future<Output = ()>;
139+
}
140+
141+
/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify
142+
/// generation of the code for `async_drop_in_place_raw`
143+
#[lang = "surface_async_drop_in_place"]
144+
async unsafe fn surface_async_drop_in_place<T: AsyncDrop + ?Sized>(ptr: *mut T) {
145+
// SAFETY: We call this from async drop `async_drop_in_place_raw`
146+
// which has the same safety requirements
147+
unsafe { <T as AsyncDrop>::async_drop(Pin::new_unchecked(&mut *ptr)).await }
148+
}
149+
150+
/// Basically calls `Drop::drop` with pointer. Used to simplify generation
151+
/// of the code for `async_drop_in_place_raw`
152+
#[allow(drop_bounds)]
153+
#[lang = "async_drop_surface_drop_in_place"]
154+
async unsafe fn surface_drop_in_place<T: Drop + ?Sized>(ptr: *mut T) {
155+
// SAFETY: We call this from async drop `async_drop_in_place_raw`
156+
// which has the same safety requirements
157+
unsafe { crate::ops::fallback_surface_drop(&mut *ptr) }
158+
}
159+
160+
/// Wraps a future to continue outputing `Poll::Ready(())` once after
161+
/// wrapped future completes by returning `Poll::Ready(())` on poll. This
162+
/// is useful for constructing async destructors to guarantee this
163+
/// "fuse" property
164+
struct Fuse<T> {
165+
inner: Option<T>,
166+
}
167+
168+
#[lang = "async_drop_fuse"]
169+
fn fuse<T>(inner: T) -> Fuse<T> {
170+
Fuse { inner: Some(inner) }
171+
}
172+
173+
impl<T> Future for Fuse<T>
174+
where
175+
T: Future<Output = ()>,
176+
{
177+
type Output = ();
178+
179+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
180+
// SAFETY: pin projection into `self.inner`
181+
unsafe {
182+
let this = self.get_unchecked_mut();
183+
if let Some(inner) = &mut this.inner {
184+
ready!(Pin::new_unchecked(inner).poll(cx));
185+
this.inner = None;
186+
}
187+
}
188+
Poll::Ready(())
189+
}
190+
}
191+
192+
/// Async destructor for arrays and slices.
193+
#[lang = "async_drop_slice"]
194+
async unsafe fn slice<T>(s: *mut [T]) {
195+
let len = s.len();
196+
let ptr = s.as_mut_ptr();
197+
for i in 0..len {
198+
// SAFETY: we iterate over elements of `s` slice
199+
unsafe { async_drop_in_place_raw(ptr.add(i)).await }
200+
}
201+
}
202+
203+
/// Construct a chain of two futures, which awaits them sequentially as
204+
/// a future.
205+
#[lang = "async_drop_chain"]
206+
async fn chain<F, G>(first: F, last: G)
207+
where
208+
F: IntoFuture<Output = ()>,
209+
G: IntoFuture<Output = ()>,
210+
{
211+
first.await;
212+
last.await;
213+
}
214+
215+
/// Basically a lazy version of `async_drop_in_place`. Returns a future
216+
/// that would call `AsyncDrop::async_drop` on a first poll.
217+
///
218+
/// # Safety
219+
///
220+
/// Same as `async_drop_in_place` except is lazy to avoid creating
221+
/// multiple mutable refernces.
222+
#[lang = "async_drop_defer"]
223+
async unsafe fn defer<T: ?Sized>(to_drop: *mut T) {
224+
// SAFETY: same safety requirements as `async_drop_in_place`
225+
unsafe { async_drop_in_place(to_drop) }.await
226+
}
227+
228+
/// If `T`'s discriminant is equal to the stored one then awaits `M`
229+
/// otherwise awaits the `O`.
230+
///
231+
/// # Safety
232+
///
233+
/// User should carefully manage returned future, since it would
234+
/// try creating an immutable referece from `this` and get pointee's
235+
/// discriminant.
236+
// FIXME(zetanumbers): Send and Sync impls
237+
#[lang = "async_drop_either"]
238+
async unsafe fn either<O: IntoFuture<Output = ()>, M: IntoFuture<Output = ()>, T>(
239+
other: O,
240+
matched: M,
241+
this: *mut T,
242+
discr: <T as DiscriminantKind>::Discriminant,
243+
) {
244+
// SAFETY: Guaranteed by the safety section of this funtion's documentation
245+
if unsafe { discriminant_value(&*this) } == discr {
246+
drop(other);
247+
matched.await
248+
} else {
249+
drop(matched);
250+
other.await
251+
}
252+
}
253+
254+
/// Used for noop async destructors. We don't use [`core::future::Ready`]
255+
/// because it panics after its second poll, which could be potentially
256+
/// bad if that would happen during the cleanup.
257+
#[derive(Clone, Copy)]
258+
struct Noop;
259+
260+
#[lang = "async_drop_noop"]
261+
fn noop() -> Noop {
262+
Noop
263+
}
264+
265+
impl Future for Noop {
266+
type Output = ();
267+
268+
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
269+
Poll::Ready(())
270+
}
271+
}

core/src/future/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
use crate::ptr::NonNull;
1313
use crate::task::Context;
1414

15+
#[cfg(not(bootstrap))]
16+
mod async_drop;
1517
mod future;
1618
mod into_future;
1719
mod join;
@@ -36,6 +38,10 @@ pub use ready::{ready, Ready};
3638
#[stable(feature = "future_poll_fn", since = "1.64.0")]
3739
pub use poll_fn::{poll_fn, PollFn};
3840

41+
#[cfg(not(bootstrap))]
42+
#[unstable(feature = "async_drop", issue = "none")]
43+
pub use async_drop::{async_drop, async_drop_in_place, AsyncDrop, AsyncDropInPlace};
44+
3945
/// This type is needed because:
4046
///
4147
/// a) Coroutines cannot implement `for<'a, 'b> Coroutine<&'a mut Context<'b>>`, so we need to pass

core/src/ops/drop.rs

+8
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,11 @@ pub trait Drop {
238238
#[stable(feature = "rust1", since = "1.0.0")]
239239
fn drop(&mut self);
240240
}
241+
242+
/// Fallback function to call surface level `Drop::drop` function
243+
#[cfg(not(bootstrap))]
244+
#[allow(drop_bounds)]
245+
#[lang = "fallback_surface_drop"]
246+
pub(crate) fn fallback_surface_drop<T: Drop + ?Sized>(x: &mut T) {
247+
<T as Drop>::drop(x)
248+
}

core/src/ops/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ pub use self::deref::Receiver;
174174
#[stable(feature = "rust1", since = "1.0.0")]
175175
pub use self::drop::Drop;
176176

177+
#[cfg(not(bootstrap))]
178+
pub(crate) use self::drop::fallback_surface_drop;
179+
177180
#[stable(feature = "rust1", since = "1.0.0")]
178181
pub use self::function::{Fn, FnMut, FnOnce};
179182

0 commit comments

Comments
 (0)