Skip to content

Commit

Permalink
feat: send/sync for node and guard
Browse files Browse the repository at this point in the history
  • Loading branch information
pedromfedricci committed Sep 10, 2024
1 parent 41d6711 commit 4f2a727
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 12 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ Or add a entry under the `[dependencies]` section in your `Cargo.toml`:
# Cargo.toml

[dependencies]
# Available features: `yield` and thread_local`.
clhlock = { version = "0.1", features = ["yield, thread_local"] }
# Available features: `yield` and `thread_local`.
clhlock = { version = "0.1", features = ["yield", "thread_local"] }
```

## Documentation
Expand All @@ -66,7 +66,7 @@ RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --open
## Waiting queue node allocations

Queue nodes are allocated in the heap, and their ownership is transparently
moved from the lock holding thread to it successor. Allocationg the nodes in
moved from the lock holding thread to its successor. Allocating the nodes in
the stack is not allowed since the CLH lock protocol does not guarantee that
a predecessor thread will be live by the time a successor access its
associated locking node. Locking operations require exclusive access to local
Expand Down
17 changes: 10 additions & 7 deletions src/inner/raw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ pub struct MutexNode<L> {
inner: NonNull<MutexNodeInner<L>>,
}

// SAFETY: Public APIs that mutate state require exclusive references.
unsafe impl<L> Send for MutexNode<L> {}
unsafe impl<L> Sync for MutexNode<L> {}

impl<L> MutexNode<L> {
/// Creates a new `MuexNode` intance from a raw pointer.
///
Expand Down Expand Up @@ -140,7 +144,7 @@ impl<L> Drop for MutexNode<L> {
// be null, since the tail is initialized with a valid allocation, and
// all tail updates point to valid, heap allocated nodes. The drop call
// is only ever run once for any value, and this instance is the only
// pointer to this heap allocated node.
// pointer to this heap allocated node at drop call time.
drop(unsafe { Box::from_raw(inner) });
}
}
Expand Down Expand Up @@ -205,7 +209,7 @@ impl<T: ?Sized, L, W> Drop for Mutex<T, L, W> {
// be null, since the tail is initialized with a valid allocation, and
// all tail updates point to valid, heap allocated nodes. The drop call
// is only ever run once for any value, and this instance is the only
// pointer to this heap allocated node.
// pointer to this heap allocated node at drop call time.
drop(unsafe { MutexNode::from_ptr(tail) });
}
}
Expand All @@ -230,16 +234,15 @@ impl<T: ?Sized + Debug, L: Lock, W: Wait> Debug for Mutex<T, L, W> {

/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
/// dropped (falls out of scope), the lock will be unlocked.
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard<'a, T: ?Sized, L: Lock, W> {
lock: &'a Mutex<T, L, W>,
head: &'a mut MutexNode<L>,
}

// Rust's `std::sync::MutexGuard` is not Send for pthread compatibility, but this
// impl is safe to be Send. Same unsafe Sync impl as `std::sync::MutexGuard`.
// unsafe impl<T: ?Sized + Send, L: Lock + Send, W: Wait> Send for MutexGuard<'_, T, L, W> {}
unsafe impl<T: ?Sized + Sync, L: Lock, W: Wait> Sync for MutexGuard<'_, T, L, W> {}
unsafe impl<T: ?Sized + Send, L: Lock, W> Send for MutexGuard<'_, T, L, W> {}
unsafe impl<T: ?Sized + Sync, L: Lock, W> Sync for MutexGuard<'_, T, L, W> {}

impl<'a, T: ?Sized, L: Lock, W> MutexGuard<'a, T, L, W> {
/// Creates a new `MutexGuard` instance.
Expand All @@ -258,8 +261,8 @@ impl<'a, T: ?Sized, L: Lock, W> MutexGuard<'a, T, L, W> {
}

impl<'a, T: ?Sized, L: Lock, W> Drop for MutexGuard<'a, T, L, W> {
#[inline]
/// Unlocks the mutex associated with this guard.
#[inline]
fn drop(&mut self) {
// SAFETY: The inner pointer always points to valid nodes allocations.
let inner = unsafe { self.head.inner.as_ref() };
Expand All @@ -270,7 +273,7 @@ impl<'a, T: ?Sized, L: Lock, W> Drop for MutexGuard<'a, T, L, W> {
// be null, since the tail is initialized with a valid allocation, and
// all tail updates point to valid, heap allocated nodes. The drop call
// is only ever run once for any value, and this instance is the only
// pointer to this heap allocated node.
// pointer to this heap allocated node at drop call time.
unsafe { MutexNode::set(self.head, prev) }
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
//! ## Waiting queue node allocations
//!
//! Queue nodes are allocated in the heap, and their ownership is transparently
//! moved from the lock holding thread to it successor. Allocationg the nodes in
//! moved from the lock holding thread to its successor. Allocating the nodes in
//! the stack is not allowed since the CLH lock protocol does not guarantee that
//! a predecessor thread will be live by the time a successor access its
//! associated locking node. Locking operations require exclusive access to local
Expand Down
10 changes: 9 additions & 1 deletion src/raw/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ pub struct MutexNode {
inner: inner::MutexNode<AtomicBool>,
}

// Same unsafe impls as `crate::inner::raw::MutexNode`.
unsafe impl Send for MutexNode {}
unsafe impl Sync for MutexNode {}

impl MutexNode {
/// Creates new `MutexNode` instance.
///
Expand Down Expand Up @@ -294,6 +298,10 @@ impl<T, R> From<T> for Mutex<T, R> {
}

impl<T: ?Sized + Debug, R: Relax> Debug for Mutex<T, R> {
/// Formats the mutex's value using the given formatter.
///
/// This will lock the mutex to do so. If the lock is already held by the
/// thread, calling this function will cause a deadlock.
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
Expand Down Expand Up @@ -352,7 +360,7 @@ pub struct MutexGuard<'a, T: ?Sized, R> {
}

// Same unsafe impls as `crate::inner::raw::MutexGuard`.
// unsafe impl<T: ?Sized + Send, R> Send for MutexGuard<'_, T, R> {}
unsafe impl<T: ?Sized + Send, R> Send for MutexGuard<'_, T, R> {}
unsafe impl<T: ?Sized + Sync, R> Sync for MutexGuard<'_, T, R> {}

#[doc(hidden)]
Expand Down

0 comments on commit 4f2a727

Please sign in to comment.