summaryrefslogtreecommitdiff
path: root/src/rwlock
diff options
context:
space:
mode:
Diffstat (limited to 'src/rwlock')
-rw-r--r--src/rwlock/read_guard.rs2
-rw-r--r--src/rwlock/read_lock.rs40
-rw-r--r--src/rwlock/rwlock.rs166
-rw-r--r--src/rwlock/write_guard.rs4
-rw-r--r--src/rwlock/write_lock.rs19
5 files changed, 222 insertions, 9 deletions
diff --git a/src/rwlock/read_guard.rs b/src/rwlock/read_guard.rs
index e967420..d8db9b9 100644
--- a/src/rwlock/read_guard.rs
+++ b/src/rwlock/read_guard.rs
@@ -14,7 +14,7 @@ impl<'a, T: ?Sized + 'a, R: RawRwLock> Deref for RwLockReadRef<'a, T, R> {
// safety: this is the only type that can use `value`, and there's
// a reference to this type, so there cannot be any mutable
// references to this value.
- unsafe { &*self.0.value.get() }
+ unsafe { &*self.0.data.get() }
}
}
diff --git a/src/rwlock/read_lock.rs b/src/rwlock/read_lock.rs
index dbab8de..176fc01 100644
--- a/src/rwlock/read_lock.rs
+++ b/src/rwlock/read_lock.rs
@@ -6,9 +6,25 @@ use crate::key::Keyable;
use super::{ReadLock, RwLock, RwLockReadGuard, RwLockReadRef};
-impl<'a, T: ?Sized, R> Debug for ReadLock<'a, T, R> {
+impl<'a, T: ?Sized + Debug, R: RawRwLock> Debug for ReadLock<'a, T, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.write_str(&format!("ReadLock<{}>", std::any::type_name::<T>()))
+ // safety: this is just a try lock, and the value is dropped
+ // immediately after, so there's no risk of blocking ourselves
+ // or any other threads
+ if let Some(value) = unsafe { self.try_lock_no_key() } {
+ f.debug_struct("ReadLock").field("data", &&*value).finish()
+ } else {
+ struct LockedPlaceholder;
+ impl Debug for LockedPlaceholder {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str("<locked>")
+ }
+ }
+
+ f.debug_struct("ReadLock")
+ .field("data", &LockedPlaceholder)
+ .finish()
+ }
}
}
@@ -25,6 +41,16 @@ impl<'a, T: ?Sized, R> AsRef<RwLock<T, R>> for ReadLock<'a, T, R> {
}
impl<'a, T: ?Sized, R> ReadLock<'a, T, R> {
+ /// Creates a new `ReadLock` which accesses the given [`RwLock`]
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{rwlock::ReadLock, RwLock};
+ ///
+ /// let lock = RwLock::new(5);
+ /// let read_lock = ReadLock::new(&lock);
+ /// ```
#[must_use]
pub const fn new(rwlock: &'a RwLock<T, R>) -> Self {
Self(rwlock)
@@ -32,6 +58,8 @@ impl<'a, T: ?Sized, R> ReadLock<'a, T, R> {
}
impl<'a, T: ?Sized, R: RawRwLock> ReadLock<'a, T, R> {
+ /// Locks the underlying [`RwLock`] with shared read access, blocking the
+ /// current thread until it can be acquired.
pub fn lock<'s, 'key: 's, Key: Keyable + 'key>(
&'s self,
key: Key,
@@ -39,10 +67,14 @@ impl<'a, T: ?Sized, R: RawRwLock> ReadLock<'a, T, R> {
self.0.read(key)
}
+ /// Creates a shared lock without a key. Locking this without exclusive
+ /// access to the key is undefined behavior.
pub(crate) unsafe fn lock_no_key(&self) -> RwLockReadRef<'_, T, R> {
self.0.read_no_key()
}
+ /// Attempts to acquire the underlying [`RwLock`] with shared read access
+ /// without blocking.
pub fn try_lock<'s, 'key: 's, Key: Keyable + 'key>(
&'s self,
key: Key,
@@ -50,10 +82,14 @@ impl<'a, T: ?Sized, R: RawRwLock> ReadLock<'a, T, R> {
self.0.try_read(key)
}
+ /// Attempts to create an exclusive lock without a key. Locking this
+ /// without exclusive access to the key is undefined behavior.
pub(crate) unsafe fn try_lock_no_key(&self) -> Option<RwLockReadRef<'_, T, R>> {
self.0.try_read_no_key()
}
+ /// Immediately drops the guard, and consequentlyreleases the shared lock
+ /// on the underlying [`RwLock`].
pub fn unlock<'key, Key: Keyable + 'key>(guard: RwLockReadGuard<'_, 'key, T, Key, R>) -> Key {
RwLock::unlock_read(guard)
}
diff --git a/src/rwlock/rwlock.rs b/src/rwlock/rwlock.rs
index c1d1792..556d6bf 100644
--- a/src/rwlock/rwlock.rs
+++ b/src/rwlock/rwlock.rs
@@ -8,10 +8,19 @@ use crate::key::Keyable;
use super::{RwLock, RwLockReadGuard, RwLockReadRef, RwLockWriteGuard, RwLockWriteRef};
impl<T, R: RawRwLock> RwLock<T, R> {
+ /// Creates a new instance of an `RwLock<T>` which is unlocked.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::RwLock;
+ ///
+ /// let lock = RwLock::new(5);
+ /// ```
#[must_use]
- pub const fn new(value: T) -> Self {
+ pub const fn new(data: T) -> Self {
Self {
- value: UnsafeCell::new(value),
+ data: UnsafeCell::new(data),
raw: R::INIT,
}
}
@@ -59,17 +68,51 @@ impl<T: ?Sized, R> AsMut<T> for RwLock<T, R> {
impl<T, R> RwLock<T, R> {
pub fn into_inner(self) -> T {
- self.value.into_inner()
+ self.data.into_inner()
}
}
impl<T: ?Sized, R> RwLock<T, R> {
pub fn get_mut(&mut self) -> &mut T {
- self.value.get_mut()
+ self.data.get_mut()
}
}
impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
+ /// Locks this `RwLock` with shared read access, blocking the current
+ /// thread until it can be acquired.
+ ///
+ /// The calling thread will be blocked until there are no more writers
+ /// which hold the lock. There may be other readers currently inside the
+ /// lock when this method returns.
+ ///
+ /// Returns an RAII guard which will release this thread's shared access
+ /// once it is dropped.
+ ///
+ /// Because this method takes a [`ThreadKey`], it's not possible for this
+ /// method to cause a deadlock.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::Arc;
+ /// use std::thread;
+ /// use happylock::{RwLock, ThreadKey};
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let lock = Arc::new(RwLock::new(1));
+ /// let c_lock = Arc::clone(&lock);
+ ///
+ /// let n = lock.read(key);
+ /// assert_eq!(*n, 1);
+ ///
+ /// thread::spawn(move || {
+ /// let key = ThreadKey::get().unwrap();
+ /// let r = c_lock.read(key);
+ /// }).join().unwrap();
+ /// ```
+ ///
+ /// [`ThreadKey`]: `crate::ThreadKey`
pub fn read<'s, 'key: 's, Key: Keyable>(
&'s self,
key: Key,
@@ -82,6 +125,8 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
}
}
+ /// Creates a shared lock without a key. Locking this without exclusive
+ /// access to the key is undefined behavior.
pub(crate) unsafe fn read_no_key(&self) -> RwLockReadRef<'_, T, R> {
self.raw.lock_shared();
@@ -89,6 +134,26 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
RwLockReadRef(self)
}
+ /// Attempts to acquire this `RwLock` with shared read access without
+ /// blocking.
+ ///
+ /// If the access could not be granted at this time, then `None` is
+ /// returned. Otherwise, an RAII guard is returned which will release the
+ /// shared access when it is dropped.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{RwLock, ThreadKey};
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let lock = RwLock::new(1);
+ ///
+ /// match lock.try_read(key) {
+ /// Some(n) => assert_eq!(*n, 1),
+ /// None => unreachable!(),
+ /// };
+ /// ```
pub fn try_read<'s, 'key: 's, Key: Keyable>(
&'s self,
key: Key,
@@ -103,6 +168,8 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
}
}
+ /// Attempts to create a shared lock without a key. Locking this without
+ /// exclusive access to the key is undefined behavior.
pub(crate) unsafe fn try_read_no_key(&self) -> Option<RwLockReadRef<'_, T, R>> {
if self.raw.try_lock_shared() {
// safety: the lock is locked first
@@ -112,6 +179,31 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
}
}
+ /// Locks this `RwLock` with exclusive write access, blocking the current
+ /// until it can be acquired.
+ ///
+ /// This function will not return while other writers or readers currently
+ /// have access to the lock.
+ ///
+ /// Returns an RAII guard which will drop the write access of this `RwLock`
+ /// when dropped.
+ ///
+ /// Because this method takes a [`ThreadKey`], it's not possible for this
+ /// method to cause a deadlock.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{ThreadKey, RwLock};
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let lock = RwLock::new(1);
+ ///
+ /// let mut n = lock.write(key);
+ /// *n += 2;
+ /// ```
+ ///
+ /// [`ThreadKey`]: `crate::ThreadKey`
pub fn write<'s, 'key: 's, Key: Keyable>(
&'s self,
key: Key,
@@ -124,6 +216,8 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
}
}
+ /// Creates an exclusive lock without a key. Locking this without exclusive
+ /// access to the key is undefined behavior.
pub(crate) unsafe fn write_no_key(&self) -> RwLockWriteRef<'_, T, R> {
self.raw.lock_exclusive();
@@ -131,6 +225,27 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
RwLockWriteRef(self)
}
+ /// Attempts to lock this `RwLock` with exclusive write access.
+ ///
+ /// This function does not block. If the lock could not be acquired at this
+ /// time, then `None` is returned. Otherwise an RAII guard is returned
+ /// which will release the lock when it is dropped.
+ ///
+ /// This function does not provide any guarantees with respect to the
+ /// ordering of whether contentious readers or writers will acquire the
+ /// lock first.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{RwLock, ThreadKey};
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let lock = RwLock::new(1);
+ ///
+ /// let n = lock.read(key);
+ /// assert_eq!(*n, 1);
+ /// ```
pub fn try_write<'s, 'key: 's, Key: Keyable>(
&'s self,
key: Key,
@@ -145,6 +260,8 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
}
}
+ /// Attempts to create an exclusive lock without a key. Locking this
+ /// without exclusive access to the key is undefined behavior.
pub(crate) unsafe fn try_write_no_key(&self) -> Option<RwLockWriteRef<'_, T, R>> {
if self.raw.try_lock_exclusive() {
// safety: the lock is locked first
@@ -154,14 +271,36 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
}
}
+ /// Unlocks shared access on the `RwLock`. This is undefined behavior is
+ /// the data is still accessible.
pub(super) unsafe fn force_unlock_read(&self) {
self.raw.unlock_shared();
}
+ /// Unlocks exclusive access on the `RwLock`. This is undefined behavior is
+ /// the data is still accessible.
pub(super) unsafe fn force_unlock_write(&self) {
self.raw.unlock_exclusive();
}
+ /// Immediately drops the guard, and consequently releases the shared lock.
+ ///
+ /// This function is equivalent to calling [`drop`] on the guard, except
+ /// that it returns the key that was used to create it. Alternately, the
+ /// guard will be automatically dropped when it goes out of scope.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{RwLock, ThreadKey};
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let lock = RwLock::new(0);
+ ///
+ /// let mut guard = lock.read(key);
+ /// assert_eq!(*guard, 0);
+ /// let key = RwLock::unlock_read(guard);
+ /// ```
pub fn unlock_read<'key, Key: Keyable + 'key>(
guard: RwLockReadGuard<'_, 'key, T, Key, R>,
) -> Key {
@@ -171,6 +310,25 @@ impl<T: ?Sized, R: RawRwLock> RwLock<T, R> {
guard.thread_key
}
+ /// Immediately drops the guard, and consequently releases the exclusive
+ /// lock.
+ ///
+ /// This function is equivalent to calling [`drop`] on the guard, except
+ /// that it returns the key that was used to create it. Alternately, the
+ /// guard will be automatically dropped when it goes out of scope.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{RwLock, ThreadKey};
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let lock = RwLock::new(0);
+ ///
+ /// let mut guard = lock.write(key);
+ /// *guard += 20;
+ /// let key = RwLock::unlock_write(guard);
+ /// ```
pub fn unlock_write<'key, Key: Keyable + 'key>(
guard: RwLockWriteGuard<'_, 'key, T, Key, R>,
) -> Key {
diff --git a/src/rwlock/write_guard.rs b/src/rwlock/write_guard.rs
index 8f5feb4..dd168bf 100644
--- a/src/rwlock/write_guard.rs
+++ b/src/rwlock/write_guard.rs
@@ -14,7 +14,7 @@ impl<'a, T: ?Sized + 'a, R: RawRwLock> Deref for RwLockWriteRef<'a, T, R> {
// safety: this is the only type that can use `value`, and there's
// a reference to this type, so there cannot be any mutable
// references to this value.
- unsafe { &*self.0.value.get() }
+ unsafe { &*self.0.data.get() }
}
}
@@ -23,7 +23,7 @@ impl<'a, T: ?Sized + 'a, R: RawRwLock> DerefMut for RwLockWriteRef<'a, T, R> {
// safety: this is the only type that can use `value`, and we have a
// mutable reference to this type, so there cannot be any other
// references to this value.
- unsafe { &mut *self.0.value.get() }
+ unsafe { &mut *self.0.data.get() }
}
}
diff --git a/src/rwlock/write_lock.rs b/src/rwlock/write_lock.rs
index dd204f5..0275a70 100644
--- a/src/rwlock/write_lock.rs
+++ b/src/rwlock/write_lock.rs
@@ -25,6 +25,16 @@ impl<'a, T: ?Sized, R> AsRef<RwLock<T, R>> for WriteLock<'a, T, R> {
}
impl<'a, T: ?Sized, R> WriteLock<'a, T, R> {
+ /// Creates a new `WriteLock` which accesses the given [`RwLock`]
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use happylock::{rwlock::WriteLock, RwLock};
+ ///
+ /// let lock = RwLock::new(5);
+ /// let write_lock = WriteLock::new(&lock);
+ /// ```
#[must_use]
pub const fn new(rwlock: &'a RwLock<T, R>) -> Self {
Self(rwlock)
@@ -32,6 +42,8 @@ impl<'a, T: ?Sized, R> WriteLock<'a, T, R> {
}
impl<'a, T: ?Sized, R: RawRwLock> WriteLock<'a, T, R> {
+ /// Locks the underlying [`RwLock`] with exclusive write access, blocking
+ /// the current until it can be acquired.
pub fn lock<'s, 'key: 's, Key: Keyable + 'key>(
&'s self,
key: Key,
@@ -39,10 +51,13 @@ impl<'a, T: ?Sized, R: RawRwLock> WriteLock<'a, T, R> {
self.0.write(key)
}
+ /// Creates an exclusive lock without a key. Locking this without exclusive
+ /// access to the key is undefined behavior.
pub(crate) unsafe fn lock_no_key(&self) -> RwLockWriteRef<'_, T, R> {
self.0.write_no_key()
}
+ /// Attempts to lock the underlying [`RwLock`] with exclusive write access.
pub fn try_lock<'s, 'key: 's, Key: Keyable + 'key>(
&'s self,
key: Key,
@@ -50,10 +65,14 @@ impl<'a, T: ?Sized, R: RawRwLock> WriteLock<'a, T, R> {
self.0.try_write(key)
}
+ /// Attempts to create an exclusive lock without a key. Locking this
+ /// without exclusive access to the key is undefined behavior.
pub(crate) unsafe fn try_lock_no_key(&self) -> Option<RwLockWriteRef<'_, T, R>> {
self.0.try_write_no_key()
}
+ /// Immediately drops the guard, and consequently releases the exclusive
+ /// lock.
pub fn unlock<'key, Key: Keyable + 'key>(guard: RwLockWriteGuard<'_, 'key, T, Key, R>) -> Key {
RwLock::unlock_write(guard)
}