summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rw-r--r--examples/double_mutex.rs8
-rw-r--r--examples/list.rs32
-rw-r--r--src/collection.rs104
-rw-r--r--src/guard.rs60
-rw-r--r--src/lib.rs4
-rw-r--r--src/lockable.rs252
-rw-r--r--src/rwlock.rs4
8 files changed, 237 insertions, 237 deletions
diff --git a/README.md b/README.md
index a35e1f6..edb03e8 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ This library prevents #4, by requiring that all of the resources that a thread n
## Example
```rust
-let data: SpinLock<i32> = Mutex::new(0);
+let data: Mutex<i32> = Mutex::new(0);
for _ in 0..N {
thread::spawn(move || {
@@ -44,8 +44,8 @@ Unlocking a mutex requires a `ThreadKey` or a mutable reference to `ThreadKey`.
To lock multiple mutexes at a time, create a `LockGuard`.
```rust
-static DATA_1: SpinLock<i32> = Mutex::new(0);
-static DATA_2: SpinLock<String> = Mutex::new(String::new());
+static DATA_1: Mutex<i32> = Mutex::new(0);
+static DATA_2: Mutex<String> = Mutex::new(String::new());
for _ in 0..N {
thread::spawn(move || {
@@ -78,9 +78,9 @@ There might be some promise in trying to prevent circular wait. There could be a
Although this library is able to successfully prevent deadlocks, livelocks may still be an issue. Imagine thread 1 gets resource 1, thread 2 gets resource 2, thread 1 realizes it can't get resource 2, thread 2 realizes it can't get resource 1, thread 1 drops resource 1, thread 2 drops resource 2, and then repeat forever. In practice, this situation probably wouldn't last forever. But it would be nice if this could be prevented somehow.
-I want to try to get this working without the standard library. There are a few problems with this though. For instance, this crate uses `thread_local` to allow other threads to have their own keys. Also, the only practical type of mutex that would work is a spinlock. Although, more could be implemented using the `RawMutex` trait.
+I want to try to get this working without the standard library. There are a few problems with this though. For instance, this crate uses `thread_local` to allow other threads to have their own keys. Also, the only practical type of mutex that would work is a spinlock. Although, more could be implemented using the `RawMutex` trait. The `LockCollection` requires memory allocation at this time in order to check for duplicate locks.
-Theoretically, it's possible to include the same mutex in a list twice, preventing the entire lock from being obtained. And this is technically a deadlock. A pretty easy to prevent deadlock, but a deadlock nonetheless. This is difficult to prevent, but could maybe be done by giving each mutex an ID, and then ensuring that the same ID doesn't appear twice in a list. This is an O(n^2) operation, and using an `AtomicUsize` to make the IDs would mean that creating a mutex isn't `const`.
+Theoretically, it's possible to include the same mutex in a list twice, preventing the entire lock from being obtained. And this is technically a deadlock. A pretty easy to prevent deadlock, but a deadlock nonetheless. This is difficult to prevent, but could maybe be done by giving each mutex an ID, and then ensuring that the same ID doesn't appear twice in a list. This is an O(n^2) operation, and using an `AtomicUsize` to make the IDs would mean that creating a mutex isn't `const`. The `AtomicUsize` issue might be solvable by instead using the pointer address of the mutex. We could also try checking which thread is holding the mutex before locking it, and if it's the same thread, we'd panic. A reentrant lock sounds promising, but it would only work for readonly guards. It's worth noting that this problem wouldn't apply to lock sequences, because those must have ownership over the locks.
More types might be lockable using a `LockGuard`. In addition, some sort of `DynamicLock` type might be useful so that, for example, a `Mutex<usize>` and an `RwLock<usize>` could be unlocked at the same time inside of a `Vec<DynamicLock<usize>>`. Although, this wouldn't solve the problem of needing a `Mutex<usize>` and a `Mutex<String>` at the same time. This would be better solved usin the existing tuple system.
diff --git a/examples/double_mutex.rs b/examples/double_mutex.rs
index d1a939c..df11b7a 100644
--- a/examples/double_mutex.rs
+++ b/examples/double_mutex.rs
@@ -1,6 +1,6 @@
use std::thread;
-use happylock::{LockGuard, Mutex, ThreadKey};
+use happylock::{LockCollection, Mutex, ThreadKey};
const N: usize = 10;
@@ -13,7 +13,8 @@ fn main() {
let th = thread::spawn(move || {
let key = ThreadKey::lock().unwrap();
let data = (&DATA_1, &DATA_2);
- let mut guard = LockGuard::lock(&data, key);
+ let lock = LockCollection::new(data).unwrap();
+ let mut guard = lock.lock(key);
*guard.1 = (100 - *guard.0).to_string();
*guard.0 += 1;
});
@@ -26,7 +27,8 @@ fn main() {
let key = ThreadKey::lock().unwrap();
let data = (&DATA_1, &DATA_2);
- let data = LockGuard::lock(&data, key);
+ let data = LockCollection::new(data).unwrap();
+ let data = data.lock(key);
println!("{}", *data.0);
println!("{}", *data.1);
}
diff --git a/examples/list.rs b/examples/list.rs
index 3903260..368a633 100644
--- a/examples/list.rs
+++ b/examples/list.rs
@@ -1,6 +1,6 @@
use std::thread;
-use happylock::{LockGuard, Mutex, ThreadKey};
+use happylock::{LockCollection, Mutex, ThreadKey};
const N: usize = 10;
@@ -30,16 +30,23 @@ fn main() {
for _ in 0..N {
let th = thread::spawn(move || {
let mut key = ThreadKey::lock().unwrap();
- let mut data = Vec::new();
- for _ in 0..3 {
- let rand = random(&mut key);
- data.push(&DATA[rand % 6]);
- }
+ loop {
+ let mut data = Vec::new();
+ for _ in 0..3 {
+ let rand = random(&mut key);
+ data.push(&DATA[rand % 6]);
+ }
+
+ let Some(lock) = LockCollection::new(data) else {
+ continue;
+ };
+ let mut guard = lock.lock(&mut key);
+ *guard[0] += *guard[1];
+ *guard[1] += *guard[2];
+ *guard[2] += *guard[0];
- let mut guard = LockGuard::lock(&data, key);
- *guard[0] += *guard[1];
- *guard[1] += *guard[2];
- *guard[2] += *guard[0];
+ return;
+ }
});
threads.push(th);
}
@@ -48,8 +55,9 @@ fn main() {
_ = th.join();
}
- let mut key = ThreadKey::lock().unwrap();
- let data = LockGuard::lock(&DATA, &mut key);
+ let key = ThreadKey::lock().unwrap();
+ let data = LockCollection::new(&DATA).unwrap();
+ let data = data.lock(key);
for val in &*data {
println!("{}", **val);
}
diff --git a/src/collection.rs b/src/collection.rs
new file mode 100644
index 0000000..809f340
--- /dev/null
+++ b/src/collection.rs
@@ -0,0 +1,104 @@
+use std::{
+ marker::PhantomData,
+ ops::{Deref, DerefMut},
+};
+
+use crate::{key::Keyable, lockable::Lockable};
+
+fn contains_duplicates(l: &[usize]) -> bool {
+ for i in 0..l.len() {
+ for j in 0..l.len() {
+ if i != j && l[i] == l[j] {
+ return true;
+ }
+ }
+ }
+
+ false
+}
+
+pub struct LockCollection<L> {
+ collection: L,
+}
+
+/// A guard for a generic [`Lockable`] type.
+pub struct LockGuard<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable + 'key> {
+ guard: L::Output,
+ key: Key,
+ _phantom: PhantomData<&'key ()>,
+}
+
+impl<L> LockCollection<L> {
+ /// Creates a new collections of locks.
+ ///
+ /// # Safety
+ ///
+ /// This results in undefined behavior if any locks are presented twice
+ /// within this collection.
+ pub const unsafe fn new_unchecked(collection: L) -> Self {
+ Self { collection }
+ }
+}
+
+impl<'a, L: Lockable<'a>> LockCollection<L> {
+ /// Creates a new collection of locks.
+ ///
+ /// This returns `None` if any locks are found twice in the given collection.
+ pub fn new(collection: L) -> Option<Self> {
+ let ptrs = collection.get_ptrs();
+ if contains_duplicates(&ptrs) {
+ return None;
+ }
+
+ Some(Self { collection })
+ }
+
+ /// Locks the lockable type and returns a guard that can be used to access
+ /// the underlying data.
+ pub fn lock<'key: 'a, Key: Keyable + 'key>(&'a self, key: Key) -> LockGuard<'a, 'key, L, Key> {
+ LockGuard {
+ // safety: we have the thread's key
+ guard: unsafe { self.collection.lock() },
+ key,
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Attempts to lock the guard without blocking.
+ ///
+ /// If successful, this method returns a guard that can be used to access
+ /// the data. Otherwise, `None` is returned.
+ pub fn try_lock<'key: 'a, Key: Keyable + 'key>(
+ &'a self,
+ key: Key,
+ ) -> Option<LockGuard<'a, 'key, L, Key>> {
+ // safety: we have the thread's key
+ unsafe { self.collection.try_lock() }.map(|guard| LockGuard {
+ guard,
+ key,
+ _phantom: PhantomData,
+ })
+ }
+
+ /// Unlocks the underlying lockable data type, returning the key that's
+ /// associated with it.
+ #[allow(clippy::missing_const_for_fn)]
+ pub fn unlock<'key: 'a, Key: Keyable + 'key>(guard: LockGuard<'a, 'key, L, Key>) -> Key {
+ drop(guard.guard);
+ guard.key
+ }
+}
+
+impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> Deref for LockGuard<'a, 'key, L, Key> {
+ type Target = L::Output;
+
+ fn deref(&self) -> &Self::Target {
+ &self.guard
+ }
+}
+
+impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> DerefMut for LockGuard<'a, 'key, L, Key> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.guard
+ }
+}
diff --git a/src/guard.rs b/src/guard.rs
deleted file mode 100644
index 12021e2..0000000
--- a/src/guard.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-use std::{
- marker::PhantomData,
- ops::{Deref, DerefMut},
-};
-
-use crate::{key::Keyable, lockable::Lockable};
-
-/// A guard for a generic [`Lockable`] type.
-pub struct LockGuard<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable + 'key> {
- guard: L::Output,
- key: Key,
- _phantom: PhantomData<&'key ()>,
-}
-
-impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> LockGuard<'a, 'key, L, Key> {
- /// Locks the lockable type and returns a guard that can be used to access
- /// the underlying data.
- pub fn lock(lock: &'a L, key: Key) -> Self {
- Self {
- // safety: we have the thread's key
- guard: unsafe { lock.lock() },
- key,
- _phantom: PhantomData,
- }
- }
-
- /// Attempts to lock the guard without blocking. If successful, this method
- /// returns a guard that can be used to access the data. Otherwise, the key
- /// is given back as an error.
- pub fn try_lock(lock: &'a L, key: Key) -> Option<Self> {
- // safety: we have the thread's key
- unsafe { lock.try_lock() }.map(|guard| Self {
- guard,
- key,
- _phantom: PhantomData,
- })
- }
-
- /// Unlocks the underlying lockable data type, returning the key that's
- /// associated with it.
- #[allow(clippy::missing_const_for_fn)]
- pub fn unlock(guard: Self) -> Key {
- L::unlock(guard.guard);
- guard.key
- }
-}
-
-impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> Deref for LockGuard<'a, 'key, L, Key> {
- type Target = L::Output;
-
- fn deref(&self) -> &Self::Target {
- &self.guard
- }
-}
-
-impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> DerefMut for LockGuard<'a, 'key, L, Key> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.guard
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 3897615..25b6d03 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,14 +4,14 @@
#![allow(clippy::declare_interior_mutable_const)]
#![allow(clippy::semicolon_if_nothing_returned)]
-mod guard;
+mod collection;
mod key;
mod lockable;
pub mod mutex;
pub mod rwlock;
-pub use guard::LockGuard;
+pub use collection::LockCollection;
pub use key::{Key, ThreadKey};
pub use lockable::Lockable;
pub use mutex::SpinLock;
diff --git a/src/lockable.rs b/src/lockable.rs
index 050f856..ddcc1c8 100644
--- a/src/lockable.rs
+++ b/src/lockable.rs
@@ -34,11 +34,16 @@ mod sealed {
/// # Safety
///
/// A deadlock must never occur. The `unlock` method must correctly unlock the
-/// data.
+/// data. The `get_ptrs` method must be implemented correctly. The `Output`
+/// must be unlocked when it is dropped.
pub unsafe trait Lockable<'a>: sealed::Sealed {
/// The output of the lock
type Output;
+ /// Returns a list of all pointers to locks. This is used to ensure that
+ /// the same lock isn't included twice
+ fn get_ptrs(&self) -> Vec<usize>;
+
/// Blocks until the lock is acquired
///
/// # Safety
@@ -63,14 +68,15 @@ pub unsafe trait Lockable<'a>: sealed::Sealed {
///
/// [`ThreadKey`]: `crate::key::ThreadKey`
unsafe fn try_lock(&'a self) -> Option<Self::Output>;
-
- /// Release the lock
- fn unlock(guard: Self::Output);
}
unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &T {
type Output = T::Output;
+ fn get_ptrs(&self) -> Vec<usize> {
+ (*self).get_ptrs()
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
(*self).lock()
}
@@ -78,16 +84,15 @@ unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &T {
unsafe fn try_lock(&'a self) -> Option<Self::Output> {
(*self).try_lock()
}
-
- #[allow(clippy::semicolon_if_nothing_returned)]
- fn unlock(guard: Self::Output) {
- T::unlock(guard)
- }
}
unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &mut T {
type Output = T::Output;
+ fn get_ptrs(&self) -> Vec<usize> {
+ (**self).get_ptrs()
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
(**self).lock()
}
@@ -95,16 +100,15 @@ unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for &mut T {
unsafe fn try_lock(&'a self) -> Option<Self::Output> {
(**self).try_lock()
}
-
- #[allow(clippy::semicolon_if_nothing_returned)]
- fn unlock(guard: Self::Output) {
- T::unlock(guard)
- }
}
unsafe impl<'a, T: 'a, R: RawMutex + 'a> Lockable<'a> for Mutex<T, R> {
type Output = MutexRef<'a, T, R>;
+ fn get_ptrs(&self) -> Vec<usize> {
+ vec![self as *const Self as usize]
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
self.lock_no_key()
}
@@ -112,15 +116,15 @@ unsafe impl<'a, T: 'a, R: RawMutex + 'a> Lockable<'a> for Mutex<T, R> {
unsafe fn try_lock(&'a self) -> Option<Self::Output> {
self.try_lock_no_key()
}
-
- fn unlock(guard: Self::Output) {
- drop(guard);
- }
}
unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for RwLock<T, R> {
type Output = RwLockWriteRef<'a, T, R>;
+ fn get_ptrs(&self) -> Vec<usize> {
+ vec![self as *const Self as usize]
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
self.write_no_key()
}
@@ -128,15 +132,15 @@ unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for RwLock<T, R> {
unsafe fn try_lock(&'a self) -> Option<Self::Output> {
self.try_write_no_key()
}
-
- fn unlock(guard: Self::Output) {
- drop(guard);
- }
}
unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for ReadLock<'a, T, R> {
type Output = RwLockReadRef<'a, T, R>;
+ fn get_ptrs(&self) -> Vec<usize> {
+ vec![self.0 as *const RwLock<T, R> as usize]
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
self.lock_no_key()
}
@@ -144,15 +148,15 @@ unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for ReadLock<'a, T, R> {
unsafe fn try_lock(&'a self) -> Option<Self::Output> {
self.try_lock_no_key()
}
-
- fn unlock(guard: Self::Output) {
- drop(guard);
- }
}
unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for WriteLock<'a, T, R> {
type Output = RwLockWriteRef<'a, T, R>;
+ fn get_ptrs(&self) -> Vec<usize> {
+ vec![self.0 as *const RwLock<T, R> as usize]
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
self.lock_no_key()
}
@@ -160,15 +164,17 @@ unsafe impl<'a, T: 'a, R: RawRwLock + 'a> Lockable<'a> for WriteLock<'a, T, R> {
unsafe fn try_lock(&'a self) -> Option<Self::Output> {
self.try_lock_no_key()
}
-
- fn unlock(guard: Self::Output) {
- drop(guard);
- }
}
unsafe impl<'a, A: Lockable<'a>> Lockable<'a> for (A,) {
type Output = (A::Output,);
+ fn get_ptrs(&self) -> Vec<usize> {
+ let mut ptrs = Vec::with_capacity(1);
+ ptrs.append(&mut self.0.get_ptrs());
+ ptrs
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
(self.0.lock(),)
}
@@ -176,20 +182,22 @@ unsafe impl<'a, A: Lockable<'a>> Lockable<'a> for (A,) {
unsafe fn try_lock(&'a self) -> Option<Self::Output> {
self.0.try_lock().map(|a| (a,))
}
-
- fn unlock(guard: Self::Output) {
- A::unlock(guard.0);
- }
}
unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>> Lockable<'a> for (A, B) {
type Output = (A::Output, B::Output);
+ fn get_ptrs(&self) -> Vec<usize> {
+ let mut ptrs = Vec::with_capacity(2);
+ ptrs.append(&mut self.0.get_ptrs());
+ ptrs.append(&mut self.1.get_ptrs());
+ ptrs
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
loop {
let lock0 = self.0.lock();
let Some(lock1) = self.1.try_lock() else {
- A::unlock(lock0);
continue;
};
@@ -202,32 +210,31 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>> Lockable<'a> for (A, B) {
return None;
};
let Some(lock1) = self.1.try_lock() else {
- A::unlock(lock0);
return None;
};
Some((lock0, lock1))
}
-
- fn unlock(guard: Self::Output) {
- A::unlock(guard.0);
- B::unlock(guard.1);
- }
}
unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>> Lockable<'a> for (A, B, C) {
type Output = (A::Output, B::Output, C::Output);
+ fn get_ptrs(&self) -> Vec<usize> {
+ let mut ptrs = Vec::with_capacity(3);
+ ptrs.append(&mut self.0.get_ptrs());
+ ptrs.append(&mut self.1.get_ptrs());
+ ptrs.append(&mut self.2.get_ptrs());
+ ptrs
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
loop {
let lock0 = self.0.lock();
let Some(lock1) = self.1.try_lock() else {
- A::unlock(lock0);
continue;
};
let Some(lock2) = self.2.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
continue;
};
@@ -240,23 +247,14 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>> Lockable<'a>
return None;
};
let Some(lock1) = self.1.try_lock() else {
- A::unlock(lock0);
return None;
};
let Some(lock2) = self.2.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
return None;
};
Some((lock0, lock1, lock2))
}
-
- fn unlock(guard: Self::Output) {
- A::unlock(guard.0);
- B::unlock(guard.1);
- C::unlock(guard.2);
- }
}
unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<'a>> Lockable<'a>
@@ -264,22 +262,25 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<'
{
type Output = (A::Output, B::Output, C::Output, D::Output);
+ fn get_ptrs(&self) -> Vec<usize> {
+ let mut ptrs = Vec::with_capacity(4);
+ ptrs.append(&mut self.0.get_ptrs());
+ ptrs.append(&mut self.1.get_ptrs());
+ ptrs.append(&mut self.2.get_ptrs());
+ ptrs.append(&mut self.3.get_ptrs());
+ ptrs
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
loop {
let lock0 = self.0.lock();
let Some(lock1) = self.1.try_lock() else {
- A::unlock(lock0);
continue;
};
let Some(lock2) = self.2.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
continue;
};
let Some(lock3) = self.3.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
continue;
};
@@ -292,30 +293,17 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<'
return None;
};
let Some(lock1) = self.1.try_lock() else {
- A::unlock(lock0);
return None;
};
let Some(lock2) = self.2.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
return None;
};
let Some(lock3) = self.3.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
return None;
};
Some((lock0, lock1, lock2, lock3))
}
-
- fn unlock(guard: Self::Output) {
- A::unlock(guard.0);
- B::unlock(guard.1);
- C::unlock(guard.2);
- D::unlock(guard.3);
- }
}
unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<'a>, E: Lockable<'a>>
@@ -323,29 +311,29 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<'
{
type Output = (A::Output, B::Output, C::Output, D::Output, E::Output);
+ fn get_ptrs(&self) -> Vec<usize> {
+ let mut ptrs = Vec::with_capacity(5);
+ ptrs.append(&mut self.0.get_ptrs());
+ ptrs.append(&mut self.1.get_ptrs());
+ ptrs.append(&mut self.2.get_ptrs());
+ ptrs.append(&mut self.3.get_ptrs());
+ ptrs.append(&mut self.4.get_ptrs());
+ ptrs
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
loop {
let lock0 = self.0.lock();
let Some(lock1) = self.1.try_lock() else {
- A::unlock(lock0);
continue;
};
let Some(lock2) = self.2.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
continue;
};
let Some(lock3) = self.3.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
continue;
};
let Some(lock4) = self.4.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
- D::unlock(lock3);
continue;
};
@@ -358,38 +346,20 @@ unsafe impl<'a, A: Lockable<'a>, B: Lockable<'a>, C: Lockable<'a>, D: Lockable<'
return None;
};
let Some(lock1) = self.1.try_lock() else {
- A::unlock(lock0);
return None;
};
let Some(lock2) = self.2.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
return None;
};
let Some(lock3) = self.3.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
return None;
};
let Some(lock4) = self.4.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
- D::unlock(lock3);
return None;
};
Some((lock0, lock1, lock2, lock3, lock4))
}
-
- fn unlock(guard: Self::Output) {
- A::unlock(guard.0);
- B::unlock(guard.1);
- C::unlock(guard.2);
- D::unlock(guard.3);
- E::unlock(guard.4);
- }
}
unsafe impl<
@@ -411,37 +381,33 @@ unsafe impl<
F::Output,
);
+ fn get_ptrs(&self) -> Vec<usize> {
+ let mut ptrs = Vec::with_capacity(6);
+ ptrs.append(&mut self.0.get_ptrs());
+ ptrs.append(&mut self.1.get_ptrs());
+ ptrs.append(&mut self.2.get_ptrs());
+ ptrs.append(&mut self.3.get_ptrs());
+ ptrs.append(&mut self.4.get_ptrs());
+ ptrs.append(&mut self.5.get_ptrs());
+ ptrs
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
loop {
let lock0 = self.0.lock();
let Some(lock1) = self.1.try_lock() else {
- A::unlock(lock0);
continue;
};
let Some(lock2) = self.2.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
continue;
};
let Some(lock3) = self.3.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
continue;
};
let Some(lock4) = self.4.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
- D::unlock(lock3);
continue;
};
let Some(lock5) = self.5.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
- D::unlock(lock3);
- E::unlock(lock4);
continue;
};
@@ -454,52 +420,36 @@ unsafe impl<
return None;
};
let Some(lock1) = self.1.try_lock() else {
- A::unlock(lock0);
return None;
};
let Some(lock2) = self.2.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
return None;
};
let Some(lock3) = self.3.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
return None;
};
let Some(lock4) = self.4.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
- D::unlock(lock3);
return None;
};
let Some(lock5) = self.5.try_lock() else {
- A::unlock(lock0);
- B::unlock(lock1);
- C::unlock(lock2);
- D::unlock(lock3);
- E::unlock(lock4);
return None;
};
Some((lock0, lock1, lock2, lock3, lock4, lock5))
}
-
- fn unlock(guard: Self::Output) {
- A::unlock(guard.0);
- B::unlock(guard.1);
- C::unlock(guard.2);
- D::unlock(guard.3);
- E::unlock(guard.4);
- F::unlock(guard.5);
- }
}
unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] {
type Output = [T::Output; N];
+ fn get_ptrs(&self) -> Vec<usize> {
+ let mut ptrs = Vec::with_capacity(N);
+ for lock in self {
+ ptrs.append(&mut lock.get_ptrs());
+ }
+ ptrs
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
unsafe fn unlock_partial<'a, T: Lockable<'a>, const N: usize>(
guards: [MaybeUninit<T::Output>; N],
@@ -509,7 +459,7 @@ unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] {
if i == upto {
break;
}
- T::unlock(guard.assume_init());
+ drop(guard.assume_init());
}
}
@@ -542,7 +492,7 @@ unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] {
if i == upto {
break;
}
- T::unlock(guard.assume_init());
+ drop(guard.assume_init());
}
}
@@ -558,15 +508,19 @@ unsafe impl<'a, T: Lockable<'a>, const N: usize> Lockable<'a> for [T; N] {
Some(outputs.map(|mu| mu.assume_init()))
}
-
- fn unlock(guard: Self::Output) {
- guard.map(T::unlock);
- }
}
unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Vec<T> {
type Output = Vec<T::Output>;
+ fn get_ptrs(&self) -> Vec<usize> {
+ let mut ptrs = Vec::with_capacity(self.len());
+ for lock in self {
+ ptrs.append(&mut lock.get_ptrs());
+ }
+ ptrs
+ }
+
unsafe fn lock(&'a self) -> Self::Output {
'outer: loop {
let mut outputs = Vec::with_capacity(self.len());
@@ -579,7 +533,6 @@ unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Vec<T> {
if let Some(guard) = lock.try_lock() {
outputs.push(guard);
} else {
- Self::unlock(outputs);
continue 'outer;
};
}
@@ -594,17 +547,10 @@ unsafe impl<'a, T: Lockable<'a>> Lockable<'a> for Vec<T> {
if let Some(guard) = lock.try_lock() {
outputs.push(guard);
} else {
- Self::unlock(outputs);
return None;
};
}
Some(outputs)
}
-
- fn unlock(guard: Self::Output) {
- for guard in guard {
- T::unlock(guard);
- }
- }
}
diff --git a/src/rwlock.rs b/src/rwlock.rs
index 16ad3c3..722ca2f 100644
--- a/src/rwlock.rs
+++ b/src/rwlock.rs
@@ -17,9 +17,9 @@ pub struct RwLock<T: ?Sized, R> {
value: UnsafeCell<T>,
}
-pub struct ReadLock<'a, T: ?Sized, R>(&'a RwLock<T, R>);
+pub struct ReadLock<'a, T: ?Sized, R>(pub(crate) &'a RwLock<T, R>);
-pub struct WriteLock<'a, T: ?Sized, R>(&'a RwLock<T, R>);
+pub struct WriteLock<'a, T: ?Sized, R>(pub(crate) &'a RwLock<T, R>);
pub struct RwLockReadRef<'a, T: ?Sized, R: RawRwLock>(&'a RwLock<T, R>);