diff options
| -rw-r--r-- | Cargo.toml | 6 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | examples/basic.rs | 5 | ||||
| -rw-r--r-- | examples/double_mutex.rs | 7 | ||||
| -rw-r--r-- | examples/list.rs | 7 | ||||
| -rw-r--r-- | src/key.rs | 25 | ||||
| -rw-r--r-- | src/lib.rs | 3 | ||||
| -rw-r--r-- | src/lockable.rs | 4 | ||||
| -rw-r--r-- | src/mutex.rs | 76 |
9 files changed, 38 insertions, 101 deletions
@@ -9,4 +9,8 @@ license = "Unlicense" [dependencies] thread_local = "1" -once_cell = "1"
\ No newline at end of file +once_cell = "1" +antidote = "1" +lock_api = "0.4" +parking_lot = "0.12" +spin = "0.9" @@ -80,12 +80,12 @@ There might be some promise in trying to prevent circular wait. There could be a It would be nice 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. -Currently, the mutex is implemented using a spinlock. We need to not do that. We could use parking lot, or mutexes from the operating system. +Currently, the mutex is implemented using a spinlock. We need to not do that. We could use parking lot, or the standard library. A more fair system for getting sets locks would help, but I have no clue what that looks like. -A read-write lock would be very useful here, and maybe condvars? +A read-write lock would be very useful here, and maybe other primitives such as condvars and once? -Personally, I don't like mutex poisoning, but maybe it can be worked into the library if you're into that sort of thing. +Personally, I don't like mutex poisoning, but maybe it can be worked into the library if you're into that sort of thing. For now, that can be implemented using the `poison` crate. More types might be lockable using a `LockGuard`.
\ No newline at end of file diff --git a/examples/basic.rs b/examples/basic.rs index 87e7a60..4ff85f8 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,11 +1,10 @@ use std::thread; -use happylock::mutex::{Mutex, SpinLock}; -use happylock::ThreadKey; +use happylock::{Mutex, ThreadKey}; const N: usize = 10; -static DATA: SpinLock<i32> = Mutex::new(0); +static DATA: Mutex<i32> = Mutex::new(0); fn main() { for _ in 0..N { diff --git a/examples/double_mutex.rs b/examples/double_mutex.rs index 18460e4..1469f45 100644 --- a/examples/double_mutex.rs +++ b/examples/double_mutex.rs @@ -1,12 +1,11 @@ use std::thread; -use happylock::mutex::{Mutex, SpinLock}; -use happylock::{LockGuard, ThreadKey}; +use happylock::{LockGuard, Mutex, ThreadKey}; const N: usize = 10; -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()); fn main() { for _ in 0..N { diff --git a/examples/list.rs b/examples/list.rs index 448f70a..f9b8db8 100644 --- a/examples/list.rs +++ b/examples/list.rs @@ -1,11 +1,10 @@ use std::thread; -use happylock::mutex::{Mutex, SpinLock}; -use happylock::{LockGuard, ThreadKey}; +use happylock::{LockGuard, Mutex, ThreadKey}; const N: usize = 10; -static DATA: [SpinLock<usize>; 6] = [ +static DATA: [Mutex<usize>; 6] = [ Mutex::new(0), Mutex::new(1), Mutex::new(2), @@ -14,7 +13,7 @@ static DATA: [SpinLock<usize>; 6] = [ Mutex::new(5), ]; -static SEED: SpinLock<u32> = Mutex::new(42); +static SEED: Mutex<u32> = Mutex::new(42); fn random(key: &mut ThreadKey) -> usize { let mut seed = SEED.lock(key); @@ -22,12 +22,6 @@ static KEY: Lazy<ThreadLocal<AtomicLock>> = Lazy::new(ThreadLocal::new); /// [`ThreadKey::lock`]. If the `ThreadKey` is dropped, it can be reobtained. pub type ThreadKey = Key<'static>; -/// A dumb lock that's just a wrapper for an [`AtomicBool`]. -#[derive(Debug, Default)] -pub struct AtomicLock { - is_locked: AtomicBool, -} - pub struct Key<'a> { phantom: PhantomData<*const ()>, // implement !Send and !Sync lock: &'a AtomicLock, @@ -76,20 +70,13 @@ impl ThreadKey { } } -impl AtomicLock { - /// Create a new unlocked `AtomicLock`. - #[must_use] - pub const fn new() -> Self { - Self { - is_locked: AtomicBool::new(false), - } - } - - /// Checks whether this `Lock` is currently locked. - pub fn is_locked(&self) -> bool { - self.is_locked.load(Ordering::Relaxed) - } +/// A dumb lock that's just a wrapper for an [`AtomicBool`]. +#[derive(Debug, Default)] +struct AtomicLock { + is_locked: AtomicBool, +} +impl AtomicLock { /// Attempt to lock the `AtomicLock`. /// /// If the lock is already locked, then this'll return false. If it is @@ -11,4 +11,5 @@ pub mod mutex; pub use guard::LockGuard; pub use key::{Key, ThreadKey}; pub use lockable::Lockable; -pub use mutex::Mutex; +pub use mutex::ParkingMutex as Mutex; +pub use mutex::SpinLock; diff --git a/src/lockable.rs b/src/lockable.rs index cda4e13..5e6d614 100644 --- a/src/lockable.rs +++ b/src/lockable.rs @@ -1,10 +1,12 @@ use std::mem::MaybeUninit; -use crate::mutex::{Mutex, MutexRef, RawMutex}; +use crate::mutex::{Mutex, MutexRef}; +use lock_api::RawMutex; mod sealed { #[allow(clippy::wildcard_imports)] use super::*; + pub trait Sealed {} impl<'a, T, R: RawMutex + 'a> Sealed for Mutex<T, R> {} impl<T: Sealed> Sealed for &T {} diff --git a/src/mutex.rs b/src/mutex.rs index 28e1786..f395055 100644 --- a/src/mutex.rs +++ b/src/mutex.rs @@ -1,72 +1,18 @@ use std::cell::UnsafeCell; use std::ops::{Deref, DerefMut}; -use crate::key::{AtomicLock, Keyable}; +use lock_api::RawMutex; -/// A spinning mutex -pub type SpinLock<T> = Mutex<T, RawSpin>; - -/// Implements a raw C-like mutex. -/// -/// # Safety -/// -/// It cannot be possible to lock the mutex when it is already locked. -pub unsafe trait RawMutex { - /// The initial value for an unlocked mutex - const INIT: Self; - - /// Lock the mutex, blocking until the lock is acquired - fn lock(&self); - - /// Attempt to lock the mutex without blocking. - /// - /// Returns `true` if successful, `false` otherwise. - fn try_lock(&self) -> bool; - - /// Checks whether the mutex is currently locked or not - fn is_locked(&self) -> bool; - - /// Unlock the mutex. - /// - /// # Safety - /// - /// The lock must be acquired in the current context. - unsafe fn unlock(&self); -} +use crate::key::Keyable; -/// A raw mutex which just spins -pub struct RawSpin { - lock: AtomicLock, -} - -unsafe impl RawMutex for RawSpin { - const INIT: Self = Self { - lock: AtomicLock::new(), - }; - - fn lock(&self) { - loop { - std::hint::spin_loop(); - - if let Some(key) = self.lock.try_lock() { - std::mem::forget(key); - return; - } - } - } - - fn try_lock(&self) -> bool { - self.lock.try_lock().is_some() - } +/// A spinning mutex +pub type SpinLock<T> = Mutex<T, spin::Mutex<()>>; - fn is_locked(&self) -> bool { - self.lock.is_locked() - } +/// A parking lot mutex +pub type ParkingMutex<T> = Mutex<T, parking_lot::RawMutex>; - unsafe fn unlock(&self) { - self.lock.force_unlock(); - } -} +/// A standard library mutex +pub type StdMutex<T> = Mutex<T, antidote::Mutex<()>>; /// A mutual exclusion primitive useful for protecting shared data, which /// cannot deadlock. @@ -82,7 +28,7 @@ unsafe impl RawMutex for RawSpin { /// /// [`lock`]: `Mutex::lock` /// [`try_lock`]: `Mutex::try_lock` -pub struct Mutex<T: ?Sized, R> { +pub struct Mutex<T: ?Sized, R: RawMutex> { raw: R, value: UnsafeCell<T>, } @@ -125,7 +71,7 @@ impl<'a, T: ?Sized + 'a, R: RawMutex> DerefMut for MutexRef<'a, T, R> { /// /// [`lock`]: `Mutex::lock` /// [`try_lock`]: `Mutex::try_lock` -pub struct MutexGuard<'a, T: ?Sized + 'a, Key: Keyable, R: RawMutex = RawSpin> { +pub struct MutexGuard<'a, T: ?Sized + 'a, Key: Keyable, R: RawMutex> { mutex: MutexRef<'a, T, R>, _thread_key: Key, } @@ -290,5 +236,5 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> { } } -unsafe impl<R: Send, T: ?Sized + Send> Send for Mutex<T, R> {} +unsafe impl<R: RawMutex + Send, T: ?Sized + Send> Send for Mutex<T, R> {} unsafe impl<R: RawMutex + Sync, T: ?Sized + Send> Sync for Mutex<T, R> {} |
