Skip to content

Commit 8a6bb70

Browse files
committed
AsyncDrop implementation using shim codegen of async_drop_in_place::{closure}, scoped async drop added.
1 parent 5f5e35e commit 8a6bb70

File tree

5 files changed

+92
-324
lines changed

5 files changed

+92
-324
lines changed

core/src/future/async_drop.rs

+39-272
Original file line numberDiff line numberDiff line change
@@ -1,284 +1,51 @@
11
#![unstable(feature = "async_drop", issue = "126482")]
22

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::{Context, Poll, ready};
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 = "126482")]
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 = "126482")]
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 = "126482")]
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 = "126482")]
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-
}
3+
#[allow(unused_imports)]
4+
use core::future::Future;
495

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-
}
6+
#[allow(unused_imports)]
7+
use crate::pin::Pin;
8+
#[allow(unused_imports)]
9+
use crate::task::{Context, Poll};
6210

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.
11+
/// Async version of Drop trait.
7212
///
73-
/// * `to_drop` must be nonnull, even if `T` has size 0.
13+
/// When a value is no longer needed, Rust will run a "destructor" on that value.
14+
/// The most common way that a value is no longer needed is when it goes out of
15+
/// scope. Destructors may still run in other circumstances, but we're going to
16+
/// focus on scope for the examples here. To learn about some of those other cases,
17+
/// please see [the reference] section on destructors.
7418
///
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.
19+
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
8020
///
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.
21+
/// ## `Copy` and ([`Drop`]|`AsyncDrop`) are exclusive
8822
///
89-
#[unstable(feature = "async_drop", issue = "126482")]
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 = "126482")]
97-
pub struct AsyncDropInPlace<T: ?Sized>(<T as AsyncDestruct>::AsyncDestructor);
98-
99-
#[unstable(feature = "async_drop", issue = "126482")]
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 = "126482")]
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.
23+
/// You cannot implement both [`Copy`] and ([`Drop`]|`AsyncDrop`) on the same type. Types that
24+
/// are `Copy` get implicitly duplicated by the compiler, making it very
25+
/// hard to predict when, and how often destructors will be executed. As such,
26+
/// these types cannot have destructors.
27+
#[cfg(not(bootstrap))]
12028
#[unstable(feature = "async_drop", issue = "126482")]
12129
#[lang = "async_drop"]
12230
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 = "126482")]
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 = "126482")]
132-
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>;
133-
}
134-
135-
#[lang = "async_destruct"]
136-
#[rustc_deny_explicit_impl]
137-
#[rustc_do_not_implement_via_object]
138-
trait AsyncDestruct {
139-
type AsyncDestructor: Future<Output = ()>;
140-
}
141-
142-
/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify
143-
/// generation of the code for `async_drop_in_place_raw`
144-
#[lang = "surface_async_drop_in_place"]
145-
async unsafe fn surface_async_drop_in_place<T: AsyncDrop + ?Sized>(ptr: *mut T) {
146-
// SAFETY: We call this from async drop `async_drop_in_place_raw`
147-
// which has the same safety requirements
148-
unsafe { <T as AsyncDrop>::async_drop(Pin::new_unchecked(&mut *ptr)).await }
149-
}
150-
151-
/// Basically calls `Drop::drop` with pointer. Used to simplify generation
152-
/// of the code for `async_drop_in_place_raw`
153-
#[allow(drop_bounds)]
154-
#[lang = "async_drop_surface_drop_in_place"]
155-
async unsafe fn surface_drop_in_place<T: Drop + ?Sized>(ptr: *mut T) {
156-
// SAFETY: We call this from async drop `async_drop_in_place_raw`
157-
// which has the same safety requirements
158-
unsafe { crate::ops::fallback_surface_drop(&mut *ptr) }
159-
}
160-
161-
/// Wraps a future to continue outputting `Poll::Ready(())` once after
162-
/// wrapped future completes by returning `Poll::Ready(())` on poll. This
163-
/// is useful for constructing async destructors to guarantee this
164-
/// "fuse" property
165-
//
166-
// FIXME: Consider optimizing combinators to not have to use fuse in majority
167-
// of cases, perhaps by adding `#[(rustc_)idempotent(_future)]` attribute for
168-
// async functions and blocks with the unit return type. However current layout
169-
// optimizations currently encode `None` case into the async block's discriminant.
170-
struct Fuse<T> {
171-
inner: Option<T>,
172-
}
173-
174-
#[lang = "async_drop_fuse"]
175-
fn fuse<T>(inner: T) -> Fuse<T> {
176-
Fuse { inner: Some(inner) }
177-
}
178-
179-
impl<T> Future for Fuse<T>
180-
where
181-
T: Future<Output = ()>,
182-
{
183-
type Output = ();
184-
185-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
186-
// SAFETY: pin projection into `self.inner`
187-
unsafe {
188-
let this = self.get_unchecked_mut();
189-
if let Some(inner) = &mut this.inner {
190-
ready!(Pin::new_unchecked(inner).poll(cx));
191-
this.inner = None;
192-
}
193-
}
194-
Poll::Ready(())
195-
}
196-
}
197-
198-
/// Async destructor for arrays and slices.
199-
#[lang = "async_drop_slice"]
200-
async unsafe fn slice<T>(s: *mut [T]) {
201-
let len = s.len();
202-
let ptr = s.as_mut_ptr();
203-
for i in 0..len {
204-
// SAFETY: we iterate over elements of `s` slice
205-
unsafe { async_drop_in_place_raw(ptr.add(i)).await }
206-
}
207-
}
208-
209-
/// Constructs a chain of two futures, which awaits them sequentially as
210-
/// a future.
211-
#[lang = "async_drop_chain"]
212-
async fn chain<F, G>(first: F, last: G)
213-
where
214-
F: IntoFuture<Output = ()>,
215-
G: IntoFuture<Output = ()>,
216-
{
217-
first.await;
218-
last.await;
219-
}
220-
221-
/// Basically a lazy version of `async_drop_in_place`. Returns a future
222-
/// that would call `AsyncDrop::async_drop` on a first poll.
223-
///
224-
/// # Safety
225-
///
226-
/// Same as `async_drop_in_place` except is lazy to avoid creating
227-
/// multiple mutable references.
228-
#[lang = "async_drop_defer"]
229-
async unsafe fn defer<T: ?Sized>(to_drop: *mut T) {
230-
// SAFETY: same safety requirements as `async_drop_in_place`
231-
unsafe { async_drop_in_place(to_drop) }.await
232-
}
233-
234-
/// If `T`'s discriminant is equal to the stored one then awaits `M`
235-
/// otherwise awaits the `O`.
236-
///
237-
/// # Safety
238-
///
239-
/// Users should carefully manage the returned future, since it would
240-
/// try creating an immutable reference from `this` and get pointee's
241-
/// discriminant.
242-
// FIXME(zetanumbers): Send and Sync impls
243-
#[lang = "async_drop_either"]
244-
async unsafe fn either<O: IntoFuture<Output = ()>, M: IntoFuture<Output = ()>, T>(
245-
other: O,
246-
matched: M,
247-
this: *mut T,
248-
discr: <T as DiscriminantKind>::Discriminant,
249-
) {
250-
// SAFETY: Guaranteed by the safety section of this funtion's documentation
251-
if unsafe { discriminant_value(&*this) } == discr {
252-
drop(other);
253-
matched.await
254-
} else {
255-
drop(matched);
256-
other.await
257-
}
258-
}
259-
260-
#[lang = "async_drop_deferred_drop_in_place"]
261-
async unsafe fn deferred_drop_in_place<T>(to_drop: *mut T) {
262-
// SAFETY: same safety requirements as with drop_in_place (implied by
263-
// function's name)
264-
unsafe { crate::ptr::drop_in_place(to_drop) }
265-
}
266-
267-
/// Used for noop async destructors. We don't use [`core::future::Ready`]
268-
/// because it panics after its second poll, which could be potentially
269-
/// bad if that would happen during the cleanup.
270-
#[derive(Clone, Copy)]
271-
struct Noop;
272-
273-
#[lang = "async_drop_noop"]
274-
fn noop() -> Noop {
275-
Noop
276-
}
277-
278-
impl Future for Noop {
279-
type Output = ();
280-
281-
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
282-
Poll::Ready(())
283-
}
31+
/// Executes the async destructor for this type.
32+
///
33+
/// This method is called implicitly when the value goes out of scope,
34+
/// and cannot be called explicitly.
35+
///
36+
/// When this method has been called, `self` has not yet been deallocated.
37+
/// That only happens after the method is over.
38+
///
39+
/// # Panics
40+
#[allow(async_fn_in_trait)]
41+
async fn drop(self: Pin<&mut Self>);
42+
}
43+
44+
/// Async drop.
45+
#[cfg(not(bootstrap))]
46+
#[unstable(feature = "async_drop", issue = "126482")]
47+
#[lang = "async_drop_in_place"]
48+
pub async unsafe fn async_drop_in_place<T: ?Sized>(_to_drop: *mut T) {
49+
// Code here does not matter - this is replaced by the
50+
// real implementation by the compiler.
28451
}

core/src/future/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ mod pending;
2020
mod poll_fn;
2121
mod ready;
2222

23+
#[cfg(not(bootstrap))]
2324
#[unstable(feature = "async_drop", issue = "126482")]
24-
pub use async_drop::{AsyncDrop, AsyncDropInPlace, async_drop, async_drop_in_place};
25+
pub use async_drop::{AsyncDrop, async_drop_in_place};
2526
#[stable(feature = "into_future", since = "1.64.0")]
2627
pub use into_future::IntoFuture;
2728
#[stable(feature = "future_readiness_fns", since = "1.48.0")]

core/src/ops/drop.rs

-7
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,3 @@ pub trait Drop {
240240
#[stable(feature = "rust1", since = "1.0.0")]
241241
fn drop(&mut self);
242242
}
243-
244-
/// Fallback function to call surface level `Drop::drop` function
245-
#[allow(drop_bounds)]
246-
#[lang = "fallback_surface_drop"]
247-
pub(crate) fn fallback_surface_drop<T: Drop + ?Sized>(x: &mut T) {
248-
<T as Drop>::drop(x)
249-
}

core/src/ops/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ pub use self::deref::Receiver;
176176
pub use self::deref::{Deref, DerefMut};
177177
#[stable(feature = "rust1", since = "1.0.0")]
178178
pub use self::drop::Drop;
179-
pub(crate) use self::drop::fallback_surface_drop;
180179
#[stable(feature = "rust1", since = "1.0.0")]
181180
pub use self::function::{Fn, FnMut, FnOnce};
182181
#[stable(feature = "rust1", since = "1.0.0")]

0 commit comments

Comments
 (0)