Skip to content

Commit

Permalink
test(maitake-sync): test that futures are !Unpin
Browse files Browse the repository at this point in the history
Futures which use intrusive wait nodes must not be `Unpin`. This commit
adds doctests that will fail if this is not the case. Note that these
tests must be doctests, since only doctests may be marked as
`compile_fail`,
  • Loading branch information
hawkw committed May 28, 2024
1 parent bab3d4f commit e456145
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 0 deletions.
15 changes: 15 additions & 0 deletions maitake-sync/src/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,21 @@ pub struct MutexGuard<'a, T: ?Sized> {
/// A [future] returned by the [`Mutex::lock`] method.
///
/// [future]: core::future::Future
///
/// # Notes
///
/// This future is `!Unpin`, as it is unsafe to [`core::mem::forget`] a
/// `Lock` future once it has been polled. For instance, the following code
/// must not compile:
///
///```compile_fail
/// use maitake_sync::mutex::Lock;
///
/// // Calls to this function should only compile if `T` is `Unpin`.
/// fn assert_unpin<T: Unpin>() {}
///
/// assert_unpin::<Lock<'_, ()>>();
/// ```
#[must_use = "futures do nothing unless `.await`ed or `poll`ed"]
#[pin_project]
#[derive(Debug)]
Expand Down
30 changes: 30 additions & 0 deletions maitake-sync/src/semaphore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,21 @@ pub struct Permit<'sem> {
}

/// The future returned by the [`Semaphore::acquire`] method.
///
/// # Notes
///
/// This future is `!Unpin`, as it is unsafe to [`core::mem::forget`] an
/// `Acquire` future once it has been polled. For instance, the following code
/// must not compile:
///
///```compile_fail
/// use maitake_sync::semaphore::Acquire;
///
/// // Calls to this function should only compile if `T` is `Unpin`.
/// fn assert_unpin<T: Unpin>() {}
///
/// assert_unpin::<Acquire<'_>>();
/// ```
#[derive(Debug)]
#[pin_project(PinnedDrop)]
#[must_use = "futures do nothing unless `.await`ed or `poll`ed"]
Expand Down Expand Up @@ -811,6 +826,21 @@ feature! {
/// [`Arc`] reference to the [`Semaphore`], allowing the returned future to
/// live for the `'static` lifetime, and returns an [`OwnedPermit`] (rather
/// than a [`Permit`]), which is also valid for the `'static` lifetime.
///
/// # Notes
///
/// This future is `!Unpin`, as it is unsafe to [`core::mem::forget`] an
/// `AcquireOwned` future once it has been polled. For instance, the
/// following code must not compile:
///
///```compile_fail
/// use maitake_sync::semaphore::AcquireOwned;
///
/// // Calls to this function should only compile if `T` is `Unpin`.
/// fn assert_unpin<T: Unpin>() {}
///
/// assert_unpin::<AcquireOwned<'_>>();
/// ```
#[derive(Debug)]
#[pin_project(PinnedDrop)]
#[must_use = "futures do nothing unless `.await`ed or `poll`ed"]
Expand Down
29 changes: 29 additions & 0 deletions maitake-sync/src/wait_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,21 @@ impl<K: PartialEq, V> Debug for WaitMap<K, V> {
///
/// This future is fused, so once it has completed, any future calls to poll
/// will immediately return [`Poll::Ready`].
///
/// # Notes
///
/// This future is `!Unpin`, as it is unsafe to [`core::mem::forget`] a
/// `Wait` future once it has been polled. For instance, the following code
/// must not compile:
///
///```compile_fail
/// use maitake_sync::wait_map::Wait;
///
/// // Calls to this function should only compile if `T` is `Unpin`.
/// fn assert_unpin<T: Unpin>() {}
///
/// assert_unpin::<Wait<'_, usize, ()>>();
/// ```
#[derive(Debug)]
#[pin_project(PinnedDrop)]
#[must_use = "futures do nothing unless `.await`ed or `poll`ed"]
Expand Down Expand Up @@ -951,6 +966,20 @@ feature! {
///
/// This future is fused, so once it has completed, any future calls to poll
/// will immediately return [`Poll::Ready`].
///
/// # Notes
///
/// This future is `!Unpin`, as it is unsafe to [`core::mem::forget`] a
/// `Wait` future once it has been polled. For instance, the following code
/// must not compile:
///
///```compile_fail
/// use maitake_sync::wait_map::WaitOwned;
///
/// // Calls to this function should only compile if `T` is `Unpin`.
/// fn assert_unpin<T: Unpin>() {}
///
/// assert_unpin::<WaitOwned<'_, usize, ()>>();
#[derive(Debug)]
#[pin_project(PinnedDrop)]
pub struct WaitOwned<K: PartialEq, V> {
Expand Down
30 changes: 30 additions & 0 deletions maitake-sync/src/wait_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,21 @@ pub struct WaitQueue {
///
/// This future is fused, so once it has completed, any future calls to poll
/// will immediately return [`Poll::Ready`].
///
/// # Notes
///
/// This future is `!Unpin`, as it is unsafe to [`core::mem::forget`] a `Wait`
/// future once it has been polled. For instance, the following code must not
/// compile:
///
///```compile_fail
/// use maitake_sync::wait_queue::Wait;
///
/// // Calls to this function should only compile if `T` is `Unpin`.
/// fn assert_unpin<T: Unpin>() {}
///
/// assert_unpin::<Wait<'_>>();
/// ```
#[derive(Debug)]
#[pin_project(PinnedDrop)]
#[must_use = "futures do nothing unless `.await`ed or `poll`ed"]
Expand Down Expand Up @@ -1220,6 +1235,21 @@ feature! {
///
/// This future is fused, so once it has completed, any future calls to poll
/// will immediately return [`Poll::Ready`].
///
/// # Notes
///
/// This future is `!Unpin`, as it is unsafe to [`core::mem::forget`] a
/// `WaitOwned` future once it has been polled. For instance, the following
/// code must not compile:
///
///```compile_fail
/// use maitake_sync::wait_queue::WaitOwned;
///
/// // Calls to this function should only compile if `T` is `Unpin`.
/// fn assert_unpin<T: Unpin>() {}
///
/// assert_unpin::<WaitOwned<'_>>();
/// ```
#[derive(Debug)]
#[pin_project(PinnedDrop)]
pub struct WaitOwned {
Expand Down

0 comments on commit e456145

Please sign in to comment.