Skip to content

Commit

Permalink
test(maitake-sync): test futures impl Future (#518)
Browse files Browse the repository at this point in the history
Depends on #517

As described in #516, this commit adds tests ensuring that `Future`
impls are always present for sync primitive future types when using
user-provided `ScopedRawMutex`/`RawMutex` impls. This should help
protect against future regressions.

Fixes #516
  • Loading branch information
hawkw authored Feb 6, 2025
1 parent 12c5b21 commit dd00208
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 55 deletions.
50 changes: 6 additions & 44 deletions maitake-sync/src/mutex/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use mutex_traits::ScopedRawMutex;

use super::*;
use crate::loom::{self, future};
use crate::util::test::{assert_future, NopRawMutex};
use crate::Mutex;

#[test]
Expand Down Expand Up @@ -57,49 +57,11 @@ fn basic_multi_threaded() {
});
}

struct NopRawMutex;

unsafe impl ScopedRawMutex for NopRawMutex {
fn try_with_lock<R>(&self, _: impl FnOnce() -> R) -> Option<R> {
None
}

fn with_lock<R>(&self, _: impl FnOnce() -> R) -> R {
unimplemented!("this doesn't actually do anything")
}

fn is_locked(&self) -> bool {
true
}
}

fn assert_future<F: core::future::Future>(_: F) {}

#[test]
fn lock_future_impls_future() {
loom::model(|| {
// Mutex with `DefaultMutex` as the `ScopedRawMutex` implementation
let mutex = Mutex::new(());
assert_future(mutex.lock());

// Mutex with a custom `ScopedRawMutex` implementation
let mutex = Mutex::new_with_raw_mutex((), NopRawMutex);
assert_future(mutex.lock());
})
}

#[test]
#[cfg(feature = "alloc")]
fn lock_owned_future_impls_future() {
loom::model(|| {
use alloc::sync::Arc;

// Mutex with `DefaultMutex` as the `ScopedRawMutex` implementation
let mutex = Arc::new(Mutex::new(()));
assert_future(mutex.lock_owned());
// Mutex with `DefaultMutex` as the `ScopedRawMutex` implementation
assert_future::<Lock<'_, ()>>();

// Mutex with a custom `ScopedRawMutex` implementation
let mutex = Arc::new(Mutex::new_with_raw_mutex((), NopRawMutex));
assert_future(mutex.lock_owned());
})
// Mutex with a custom `ScopedRawMutex` implementation
assert_future::<Lock<'_, (), NopRawMutex>>();
}
8 changes: 4 additions & 4 deletions maitake-sync/src/rwlock/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::*;
use crate::util;
use crate::util::test::assert_send_sync;

#[cfg(any(loom, feature = "alloc"))]
mod loom;
Expand All @@ -9,15 +9,15 @@ mod sequential;

#[test]
fn lock_is_send_sync() {
util::test::assert_send_sync::<RwLock<usize>>();
assert_send_sync::<RwLock<usize>>();
}

#[test]
fn read_guard_is_send_sync() {
util::test::assert_send_sync::<RwLockReadGuard<'_, usize>>();
assert_send_sync::<RwLockReadGuard<'_, usize>>();
}

#[test]
fn write_guard_is_send_sync() {
util::test::assert_send_sync::<RwLockWriteGuard<'_, usize>>();
assert_send_sync::<RwLockWriteGuard<'_, usize>>();
}
27 changes: 21 additions & 6 deletions maitake-sync/src/semaphore/tests.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
use super::*;
use crate::util;
use crate::util::test::{assert_future, assert_send_sync, NopRawMutex};

#[test]
fn semaphore_is_send_and_sync() {
util::test::assert_send_sync::<Semaphore>();
assert_send_sync::<Semaphore>();
}

#[test]
fn permit_is_send_and_sync() {
util::test::assert_send_sync::<Permit<'_>>();
assert_send_sync::<Permit<'_>>();
}

#[test]
fn acquire_is_send_and_sync() {
util::test::assert_send_sync::<crate::semaphore::Acquire<'_>>();
assert_send_sync::<crate::semaphore::Acquire<'_>>();
}

#[test]
fn acquire_is_future() {
// Semaphore with `DefaultRawMutex`
assert_future::<Acquire<'_>>();

// Semaphore with overridden `ScopedRawMutex`
assert_future::<Acquire<'_, NopRawMutex>>();
}

#[cfg(feature = "alloc")]
Expand All @@ -22,12 +31,18 @@ mod owned {

#[test]
fn owned_permit_is_send_and_sync() {
util::test::assert_send_sync::<OwnedPermit>();
assert_send_sync::<OwnedPermit>();
}

#[test]
fn acquire_owned_is_send_and_sync() {
util::test::assert_send_sync::<AcquireOwned>();
assert_send_sync::<AcquireOwned>();
}

#[test]
fn acquire_owned_is_future() {
assert_future::<AcquireOwned>();
assert_future::<AcquireOwned<NopRawMutex>>();
}
}

Expand Down
23 changes: 23 additions & 0 deletions maitake-sync/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,4 +263,27 @@ pub(crate) mod test {
#[allow(dead_code)]
pub(crate) fn assert_sync<T: Sync>() {}
pub(crate) fn assert_send_sync<T: Send + Sync>() {}

pub(crate) fn assert_future<F: core::future::Future>() {}

pub(crate) struct NopRawMutex;

unsafe impl mutex_traits::RawMutex for NopRawMutex {
type GuardMarker = ();
fn lock(&self) {
unimplemented!("don't actually try to lock this thing")
}

fn is_locked(&self) -> bool {
unimplemented!("don't actually try to lock this thing")
}

fn try_lock(&self) -> bool {
unimplemented!("don't actually try to lock this thing")
}

unsafe fn unlock(&self) {
unimplemented!("don't actually try to lock this thing")
}
}
}
19 changes: 19 additions & 0 deletions maitake-sync/src/wait_map/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
use super::*;
use crate::util::test::{assert_future, NopRawMutex};
#[cfg(all(not(loom), feature = "alloc"))]
mod alloc_tests;
#[cfg(any(loom, feature = "alloc"))]
mod loom;

#[test]
fn wait_future_is_future() {
// WaitMap with default mutex.
assert_future::<Wait<'_, i32, i32>>();

// WaitMap with overridden `ScopedRawMutex`.
assert_future::<Wait<'_, i32, i32, NopRawMutex>>();
}

#[test]
fn subscribe_future_is_future() {
// WaitMap with default mutex.
assert_future::<Subscribe<'_, '_, i32, i32>>();

// WaitMap with overridden `ScopedRawMutex`.
assert_future::<Subscribe<'_, '_, i32, i32, NopRawMutex>>();
}
10 changes: 10 additions & 0 deletions maitake-sync/src/wait_map/tests/alloc_tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::super::*;
use crate::loom::sync::Arc;
use crate::util::test::{assert_future, NopRawMutex};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use futures::{future::poll_fn, pin_mut, select_biased, FutureExt};
use tokio_test::{assert_pending, assert_ready, assert_ready_err, task};
Expand Down Expand Up @@ -402,3 +403,12 @@ fn drop_wake_bailed() {
assert_eq!(KEY_DROPS.load(Ordering::Relaxed), TASKS);
assert_eq!(VAL_DROPS.load(Ordering::Relaxed), TASKS);
}

#[test]
fn wait_owned_future_is_future() {
// WaitMap with default mutex.
assert_future::<WaitOwned<i32, i32>>();

// WaitMap with overridden `ScopedRawMutex`.
assert_future::<WaitOwned<i32, i32, NopRawMutex>>();
}
10 changes: 9 additions & 1 deletion maitake-sync/src/wait_queue/tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
#[cfg(any(loom, feature = "alloc"))]
use super::*;
use crate::util::test::{assert_future, NopRawMutex};

#[cfg(all(not(loom), feature = "alloc"))]
mod alloc_tests;

#[cfg(any(loom, feature = "alloc"))]
mod loom;

#[test]
fn wait_future_is_future() {
// WaitQueue with default raw mutex
assert_future::<Wait<'_>>();
// WaitQueue with overridden raw mutex
assert_future::<Wait<'_, NopRawMutex>>();
}
10 changes: 10 additions & 0 deletions maitake-sync/src/wait_queue/tests/alloc_tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::*;
use crate::loom::sync::Arc;
use crate::util::test::{assert_future, NopRawMutex};
use core::sync::atomic::{AtomicUsize, Ordering};
use tokio_test::{assert_pending, assert_ready, assert_ready_ok, task};

Expand Down Expand Up @@ -169,3 +170,12 @@ fn subscribe_consumes_wakeup() {
let mut future2 = task::spawn(q.wait());
future2.enter(|_, f| assert_pending!(f.subscribe()));
}

#[test]
fn wait_owned_future_is_future() {
// WaitQueue with default raw mutex
assert_future::<WaitOwned>();

// WaitQueue with overridden raw mutex
assert_future::<WaitOwned<NopRawMutex>>();
}

0 comments on commit dd00208

Please sign in to comment.