Skip to content

Commit 1adf67f

Browse files
authored
Add shuttle-parking_lot (#232)
* Add shuttle-parking_lot * Refactor parking_lot mutex and rwlock * Add parking_lot README
1 parent 03fc84c commit 1adf67f

File tree

9 files changed

+566
-1
lines changed

9 files changed

+566
-1
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ members = [
1111
"wrappers/tokio/impls/tokio-util",
1212
"wrappers/tokio/wrappers/shuttle-tokio",
1313
"wrappers/tokio/wrappers/shuttle-tokio-stream",
14+
"wrappers/parking_lot",
1415
]
1516

1617
resolver = "2"

wrappers/parking_lot/Cargo.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "shuttle-parking_lot"
3+
version = "0.12.5"
4+
edition = "2024"
5+
6+
[features]
7+
shuttle = [ "dep:shuttle-parking_lot-impl" ]
8+
9+
arc_lock = [ "parking_lot/arc_lock", "shuttle-parking_lot-impl?/arc_lock" ]
10+
deadlock_detection = [ "parking_lot/deadlock_detection", "shuttle-parking_lot-impl?/deadlock_detection" ]
11+
hardware-lock-elision = [ "parking_lot/hardware-lock-elision", "shuttle-parking_lot-impl?/hardware-lock-elision" ]
12+
nightly = [ "parking_lot/nightly", "shuttle-parking_lot-impl?/nightly" ]
13+
owning_ref = [ "parking_lot/owning_ref", "shuttle-parking_lot-impl?/owning_ref" ]
14+
send_guard = [ "parking_lot/send_guard", "shuttle-parking_lot-impl?/send_guard" ]
15+
serde = [ "parking_lot/serde", "shuttle-parking_lot-impl?/serde" ]
16+
17+
[dependencies]
18+
cfg-if = "1.0"
19+
parking_lot = "0.12"
20+
shuttle-parking_lot-impl = { path = "./parking_lot_impl", version = "0.1.0", optional = true }

wrappers/parking_lot/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Shuttle support for `parking_lot`
2+
3+
This folder contains the implementation and wrapper that enables testing of [parking_lot](https://crates.io/crates/parking_lot) applications with Shuttle.
4+
5+
## How to use
6+
7+
To use it, add the following in your Cargo.toml:
8+
9+
```
10+
[features]
11+
shuttle = [
12+
"parking_lot/shuttle",
13+
]
14+
15+
[dependencies]
16+
parking_lot = { package = "shuttle-parking_lot", version = "VERSION_NUMBER" }
17+
```
18+
19+
The code will then behave as before when the `shuttle` feature flag is not provided, and will run with Shuttle-compatible primitives when the `shuttle` feature flag is provided.
20+
21+
## Limitations
22+
23+
Shuttle's parking_lot functionality is currently limited to a subset of the `Mutex` and `RwLock` primitives. If your project needs functionality which is not currently supported, please file an issue or, better yet, open a PR to contribute the functionality.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "shuttle-parking_lot-impl"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[features]
7+
arc_lock = []
8+
deadlock_detection = []
9+
hardware-lock-elision = []
10+
nightly = []
11+
owning_ref = []
12+
send_guard = []
13+
serde = []
14+
15+
[dependencies]
16+
shuttle = { path = "../../../shuttle", version = "*"}
17+
tracing = { version = "0.1.36", default-features = false, features = ["std"] }
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! This crate contains Shuttle's internal implementations of the `parking_lot` crate.
2+
//! Do not depend on this crate directly. Use the `shuttle-parking_lot` crate, which conditionally
3+
//! exposes these implementations with the `shuttle` feature or the original crate without it.
4+
//!
5+
//! [`Shuttle`]: <https://crates.io/crates/shuttle>
6+
//!
7+
//! [`parking_lot`]: <https://crates.io/crates/parking_lot>
8+
9+
mod mutex;
10+
mod rwlock;
11+
12+
pub use mutex::*;
13+
pub use rwlock::*;
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//! An asynchronous mutual exclusion primitive.
2+
3+
// This implementation is adapted from the one in shuttle-tokio
4+
5+
use shuttle::future::batch_semaphore::{BatchSemaphore, Fairness, TryAcquireError};
6+
use std::cell::UnsafeCell;
7+
use std::error::Error;
8+
use std::fmt::{self, Debug, Display};
9+
use std::ops::{Deref, DerefMut};
10+
use std::thread;
11+
use tracing::trace;
12+
13+
/// An asynchronous mutex
14+
pub struct Mutex<T: ?Sized> {
15+
semaphore: BatchSemaphore,
16+
inner: UnsafeCell<T>,
17+
}
18+
19+
/// A handle to a held `Mutex`. The guard can be held across any `.await` point
20+
/// as it is [`Send`].
21+
pub struct MutexGuard<'a, T: ?Sized> {
22+
mutex: &'a Mutex<T>,
23+
}
24+
25+
impl<T: ?Sized + Display> Display for MutexGuard<'_, T> {
26+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27+
Display::fmt(&**self, f)
28+
}
29+
}
30+
31+
/// Error returned from the [`Mutex::try_lock`], `RwLock::try_read` and
32+
/// `RwLock::try_write` functions.
33+
#[derive(Debug)]
34+
pub struct TryLockError(pub(super) ());
35+
36+
impl Display for TryLockError {
37+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
38+
write!(fmt, "operation would block")
39+
}
40+
}
41+
42+
impl Error for TryLockError {}
43+
44+
// As long as T: Send, it's fine to send and share Mutex<T> between threads.
45+
// If T was not Send, sending and sharing a Mutex<T> would be bad, since you can
46+
// access T through Mutex<T>.
47+
unsafe impl<T> Send for Mutex<T> where T: ?Sized + Send {}
48+
unsafe impl<T> Sync for Mutex<T> where T: ?Sized + Send {}
49+
unsafe impl<T> Sync for MutexGuard<'_, T> where T: ?Sized + Send + Sync {}
50+
51+
impl<T> Mutex<T> {
52+
/// Creates a new lock in an unlocked state ready for use.
53+
pub const fn new(t: T) -> Self {
54+
Self {
55+
semaphore: BatchSemaphore::const_new(1, Fairness::StrictlyFair),
56+
inner: UnsafeCell::new(t),
57+
}
58+
}
59+
60+
/// Consumes the mutex, returning the underlying data.
61+
pub fn into_inner(self) -> T {
62+
self.inner.into_inner()
63+
}
64+
}
65+
66+
impl<T: ?Sized> Mutex<T> {
67+
fn acquire(&self) {
68+
trace!("acquiring parking_lot lock {:p}", self);
69+
self.semaphore.acquire_blocking(1).unwrap_or_else(|_| {
70+
// The semaphore was closed. but, we never explicitly close it, and
71+
// we own it exclusively, which means that this can never happen.
72+
if !thread::panicking() {
73+
unreachable!()
74+
}
75+
});
76+
trace!("acquired parking_lot lock {:p}", self);
77+
}
78+
79+
/// Acquires a mutex, blocking the current thread until it is able to do so.
80+
pub fn lock(&self) -> MutexGuard<'_, T> {
81+
self.acquire();
82+
83+
MutexGuard { mutex: self }
84+
}
85+
86+
fn try_acquire(&self) -> Result<(), TryAcquireError> {
87+
self.semaphore.try_acquire(1)
88+
}
89+
90+
/// Attempts to acquire this lock.
91+
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
92+
match self.try_acquire() {
93+
Ok(()) => Some(MutexGuard { mutex: self }),
94+
Err(_) => None,
95+
}
96+
}
97+
}
98+
99+
impl<T: ?Sized + Debug> Debug for Mutex<T> {
100+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101+
// SAFETY: Shuttle is running single-threaded, only we are able to access `inner` at the time of this call.
102+
Debug::fmt(&unsafe { &*self.inner.get() }, f)
103+
}
104+
}
105+
106+
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
107+
type Target = T;
108+
109+
fn deref(&self) -> &Self::Target {
110+
unsafe { &*self.mutex.inner.get() }
111+
}
112+
}
113+
114+
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
115+
fn deref_mut(&mut self) -> &mut Self::Target {
116+
unsafe { &mut *self.mutex.inner.get() }
117+
}
118+
}
119+
120+
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
121+
fn drop(&mut self) {
122+
trace!("releasing lock {:p}", self);
123+
self.mutex.semaphore.release(1);
124+
}
125+
}
126+
127+
impl<T: ?Sized + Debug> Debug for MutexGuard<'_, T> {
128+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129+
Debug::fmt(&self.mutex, f)
130+
}
131+
}
132+
133+
impl<T> From<T> for Mutex<T> {
134+
fn from(s: T) -> Self {
135+
Self::new(s)
136+
}
137+
}
138+
139+
impl<T> Default for Mutex<T>
140+
where
141+
T: Default,
142+
{
143+
fn default() -> Self {
144+
Self::new(T::default())
145+
}
146+
}

0 commit comments

Comments
 (0)