pub struct Mutex<T: ?Sized, R> { /* private fields */ }
Expand description
A mutual exclusion primitive useful for protecting shared data.
This mutex will block threads waiting for the lock to become available. The
mutex can also be statically initialized or created via a new
constructor. Each mutex has a type parameter which represents the data that
it is protecting. The data can only be accessed through closure parameters
provided by lock_then
, lock_with_then
, try_lock_then
and
try_lock_with_then
that guarantees that the data is only ever accessed
when the mutex is locked.
§Examples
use std::sync::Arc;
use std::thread;
use std::sync::mpsc::channel;
use mcslock::raw::{self, MutexNode};
use mcslock::relax::Spin;
type Mutex<T> = raw::Mutex<T, Spin>;
const N: usize = 10;
// Spawn a few threads to increment a shared variable (non-atomically), and
// let the main thread know once all increments are done.
//
// Here we're using an Arc to share memory among threads, and the data inside
// the Arc is protected with a mutex.
let data = Arc::new(Mutex::new(0));
let (tx, rx) = channel();
for _ in 0..N {
let (data, tx) = (data.clone(), tx.clone());
thread::spawn(move || {
// A queue node must be mutably accessible.
let mut node = MutexNode::new();
// The shared state can only be accessed once the lock is held.
// Our non-atomic increment is safe because we're the only thread
// which can access the shared state when the lock is held.
//
// We unwrap() the return value to assert that we are not expecting
// threads to ever fail while holding the lock.
data.lock_with_then(&mut node, |data| {
*data += 1;
if *data == N {
tx.send(()).unwrap();
}
// The lock is unlocked here at the end of the closure scope.
});
});
}
rx.recv().unwrap();
Implementations§
Source§impl<T, R> Mutex<T, R>
impl<T, R> Mutex<T, R>
Sourcepub const fn new(value: T) -> Self
pub const fn new(value: T) -> Self
Creates a new mutex in an unlocked state ready for use.
§Examples
use mcslock::raw;
use mcslock::relax::Spin;
type Mutex<T> = raw::Mutex<T, Spin>;
const MUTEX: Mutex<i32> = Mutex::new(0);
let mutex = Mutex::new(0);
Sourcepub fn into_inner(self) -> T
pub fn into_inner(self) -> T
Consumes this mutex, returning the underlying data.
§Examples
use mcslock::raw;
use mcslock::relax::Spin;
type Mutex<T> = raw::Mutex<T, Spin>;
let mutex = Mutex::new(0);
assert_eq!(mutex.into_inner(), 0);
Source§impl<T: ?Sized, R: Relax> Mutex<T, R>
impl<T: ?Sized, R: Relax> Mutex<T, R>
Sourcepub fn try_lock_then<F, Ret>(&self, f: F) -> Ret
pub fn try_lock_then<F, Ret>(&self, f: F) -> Ret
Attempts to acquire this mutex and then runs a closure against the proteced data.
If the lock could not be acquired at this time, then None
is returned.
Otherwise, an Some
with a &mut T
is returned. The lock will be
unlocked once the closure goes out of scope.
This function transparently allocates a MutexNode
in the stack for
each call, and so it will not reuse the same node for other calls.
Consider callig try_lock_with_then
if you want to reuse node
allocations.
This function does not block.
§Examples
use std::sync::Arc;
use std::thread;
use mcslock::raw;
use mcslock::relax::Spin;
type Mutex<T> = raw::Mutex<T, Spin>;
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
thread::spawn(move || {
c_mutex.try_lock_then(|data| {
if let Some(data) = data {
*data = 10;
} else {
println!("try_lock failed");
}
});
})
.join().expect("thread::spawn failed");
let value = mutex.lock_then(|data| *data);
assert_eq!(value, 10);
Compile fail: borrows of the data cannot escape the given closure:
use mcslock::raw::spins::Mutex;
let mutex = Mutex::new(1);
let borrow = mutex.try_lock_then(|data| &*data.unwrap());
Sourcepub fn try_lock_with_then<'a, F, Ret>(
&'a self,
node: &'a mut MutexNode,
f: F,
) -> Ret
pub fn try_lock_with_then<'a, F, Ret>( &'a self, node: &'a mut MutexNode, f: F, ) -> Ret
Attempts to acquire this mutex and then runs a closure against the protected data.
If the lock could not be acquired at this time, then None
is returned.
Otherwise, an Some
with a &mut T
is returned. The lock will be
unlocked once the closure goes out of scope.
To acquire a MCS lock through this function, it’s also required a mutably
borrowed queue node, which is a record that keeps a link for forming the
queue, see MutexNode
.
This function does not block.
§Examples
use std::sync::Arc;
use std::thread;
use mcslock::raw::{self, MutexNode};
use mcslock::relax::Spin;
type Mutex<T> = raw::Mutex<T, Spin>;
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
thread::spawn(move || {
let mut node = MutexNode::new();
c_mutex.try_lock_with_then(&mut node, |data| {
if let Some(data) = data {
*data = 10;
} else {
println!("try_lock failed");
}
});
})
.join().expect("thread::spawn failed");
let mut node = MutexNode::new();
let value = mutex.lock_with_then(&mut node, |data| *data);
assert_eq!(value, 10);
Compile fail: borrows of the data cannot escape the given closure:
use mcslock::raw::{spins::Mutex, MutexNode};
let mutex = Mutex::new(1);
let mut node = MutexNode::new();
let borrow = mutex.try_lock_with_then(&mut node, |data| &*data.unwrap());
Sourcepub fn lock_then<F, Ret>(&self, f: F) -> Ret
pub fn lock_then<F, Ret>(&self, f: F) -> Ret
Acquires this mutex and then runs the closure against the protected data.
This function will block the local thread until it is available to acquire the mutex. Upon acquiring the mutex, the user provided closure will be executed against the mutex proteced data. Once the closure goes out of scope, it will unlock the mutex.
This function transparently allocates a MutexNode
in the stack for
each call, and so it will not reuse the same node for other calls.
Consider callig lock_with_then
if you want to reuse node
allocations.
This function will block if the lock is unavailable.
§Examples
use std::sync::Arc;
use std::thread;
use mcslock::raw;
use mcslock::relax::Spin;
type Mutex<T> = raw::Mutex<T, Spin>;
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
thread::spawn(move || {
c_mutex.lock_then(|data| *data = 10);
})
.join().expect("thread::spawn failed");
assert_eq!(mutex.lock_then(|data| *data), 10);
Compile fail: borrows of the data cannot escape the given closure:
use mcslock::raw::spins::Mutex;
let mutex = Mutex::new(1);
let borrow = mutex.lock_then(|data| &*data);
Sourcepub fn lock_with_then<'a, F, Ret>(
&'a self,
node: &'a mut MutexNode,
f: F,
) -> Ret
pub fn lock_with_then<'a, F, Ret>( &'a self, node: &'a mut MutexNode, f: F, ) -> Ret
Acquires this mutex and then runs the closure against the proteced data.
This function will block the local thread until it is available to acquire the mutex. Upon acquiring the mutex, the user provided closure will be executed against the mutex proteced data. Once the closure goes out of scope, it will unlock the mutex.
To acquire a MCS lock through this function, it’s also required a mutably
borrowed queue node, which is a record that keeps a link for forming the
queue, see MutexNode
.
This function will block if the lock is unavailable.
§Examples
use std::sync::Arc;
use std::thread;
use mcslock::raw::{self, MutexNode};
use mcslock::relax::Spin;
type Mutex<T> = raw::Mutex<T, Spin>;
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
thread::spawn(move || {
let mut node = MutexNode::new();
c_mutex.lock_with_then(&mut node, |data| *data = 10);
})
.join().expect("thread::spawn failed");
let mut node = MutexNode::new();
assert_eq!(mutex.lock_with_then(&mut node, |data| *data), 10);
Compile fail: borrows of the data cannot escape the given closure:
use mcslock::raw::{spins::Mutex, MutexNode};
let mutex = Mutex::new(1);
let mut node = MutexNode::new();
let borrow = mutex.lock_with_then(&mut node, |data| &*data);
Source§impl<T: ?Sized, R> Mutex<T, R>
impl<T: ?Sized, R> Mutex<T, R>
Sourcepub fn is_locked(&self) -> bool
pub fn is_locked(&self) -> bool
Returns true
if the lock is currently held.
This method does not provide any synchronization guarantees, so its only useful as a heuristic, and so must be considered not up to date.
§Example
use mcslock::raw;
use mcslock::relax::Spin;
type Mutex<T> = raw::Mutex<T, Spin>;
let mutex = Mutex::new(0);
mutex.lock_then(|_data| {
assert_eq!(mutex.is_locked(), true);
});
assert_eq!(mutex.is_locked(), false);
Sourcepub fn get_mut(&mut self) -> &mut T
pub fn get_mut(&mut self) -> &mut T
Returns a mutable reference to the underlying data.
Since this call borrows the Mutex
mutably, no actual locking needs to
take place - the mutable borrow statically guarantees no locks exist.
§Examples
use mcslock::raw;
use mcslock::relax::Spin;
type Mutex<T> = raw::Mutex<T, Spin>;
let mut mutex = Mutex::new(0);
*mutex.get_mut() = 10;
assert_eq!(mutex.lock_then(|data| *data), 10);
Source§impl<T: ?Sized, R: Relax> Mutex<T, R>
impl<T: ?Sized, R: Relax> Mutex<T, R>
Sourcepub fn try_lock_with_local_then<F, Ret>(
&self,
node: &'static LocalMutexNode,
f: F,
) -> Ret
Available on crate feature thread_local
only.
pub fn try_lock_with_local_then<F, Ret>( &self, node: &'static LocalMutexNode, f: F, ) -> Ret
thread_local
only.Attempts to acquire this mutex and then runs a closure against the protected data.
If the lock could not be acquired at this time, then a None
value is
given back as the closure argument. If the lock has been acquired, then
a Some
value with the mutex proteced data is given instead. The lock
will be unlocked when the closure scope ends.
To acquire a MCS lock through this function, it’s also required a
queue node, which is a record that keeps a link for forming the queue,
to be stored in the current locking thread local storage. See
LocalMutexNode
and thread_local_node!
.
This function does not block.
§Panics
Will panic if the thread local node is already mutably borrowed.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
§Examples
use std::sync::Arc;
use std::thread;
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
thread::spawn(move || {
c_mutex.try_lock_with_local_then(&NODE, |data| {
if let Some(data) = data {
*data = 10;
} else {
println!("try_lock_with_local_then failed");
}
});
})
.join().expect("thread::spawn failed");
assert_eq!(mutex.lock_with_local_then(&NODE, |data| *data), 10);
Compile fail: borrows of the data cannot escape the given closure:
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Mutex::new(1);
let borrow = mutex.try_lock_with_local_then(&NODE, |data| &*data.unwrap());
Panic: thread local node cannot be borrowed more than once at the same time:
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Mutex::new(0);
mutex.lock_with_local_then(&NODE, |_data| {
// `NODE` is already mutably borrowed in this thread by the enclosing
// `lock_with_local_then`, the borrow is live for the full duration
// of this closure scope.
let mutex = Mutex::new(());
mutex.try_lock_with_local_then(&NODE, |_data| ());
});
Sourcepub unsafe fn try_lock_with_local_then_unchecked<F, Ret>(
&self,
node: &'static LocalMutexNode,
f: F,
) -> Ret
Available on crate feature thread_local
only.
pub unsafe fn try_lock_with_local_then_unchecked<F, Ret>( &self, node: &'static LocalMutexNode, f: F, ) -> Ret
thread_local
only.Attempts to acquire this mutex and then runs a closure against the protected data.
If the lock could not be acquired at this time, then a None
value is
given back as the closure argument. If the lock has been acquired, then
a Some
value with the mutex protected data is given instead. The lock
will be unlocked when the closure scope ends.
To acquire a MCS lock through this function, it’s also required a
queue node, which is a record that keeps a link for forming the queue,
to be stored in the current locking thread local storage. See
LocalMutexNode
and thread_local_node!
.
This function does not block.
§Safety
Unlike try_lock_with_local_then
, this method is unsafe because it does
not check if the current thread local node is already mutably borrowed.
If the current thread local node is already borrowed, calling this
function is undefined behavior.
§Panics
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
§Examples
use std::sync::Arc;
use std::thread;
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
thread::spawn(move || unsafe {
c_mutex.try_lock_with_local_then_unchecked(&NODE, |data| {
if let Some(data) = data {
*data = 10;
} else {
println!("try_lock_with_local_then_unchecked failed");
}
});
})
.join().expect("thread::spawn failed");
assert_eq!(mutex.lock_with_local_then(&NODE, |d| *d), 10);
Compile fail: borrows of the data cannot escape the given closure:
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Mutex::new(1);
let data = unsafe {
mutex.try_lock_with_local_then_unchecked(&NODE, |g| &*g.unwrap())
};
Undefined behavior: thread local node cannot be borrowed more than once at the same time:
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Mutex::new(0);
mutex.lock_with_local_then(&NODE, |_data| unsafe {
// UB: `NODE` is already mutably borrowed in this thread by the
// enclosing `lock_with_local_then`, the borrow is live for the full
// duration of this closure scope.
let mutex = Mutex::new(());
mutex.try_lock_with_local_then_unchecked(&NODE, |_data| ());
});
Sourcepub fn lock_with_local_then<F, Ret>(
&self,
node: &'static LocalMutexNode,
f: F,
) -> Ret
Available on crate feature thread_local
only.
pub fn lock_with_local_then<F, Ret>( &self, node: &'static LocalMutexNode, f: F, ) -> Ret
thread_local
only.Acquires this mutex and then runs the closure against the protected data.
This function will block the local thread until it is available to acquire the mutex. Upon acquiring the mutex, the user provided closure will be executed against the mutex protected data. Once the closure goes out of scope, it will unlock the mutex.
To acquire a MCS lock through this function, it’s also required a
queue node, which is a record that keeps a link for forming the queue,
to be stored in the current locking thread local storage. See
LocalMutexNode
and thread_local_node!
.
This function will block if the lock is unavailable.
§Panics
Will panic if the thread local node is already mutably borrowed.
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
§Examples
use std::sync::Arc;
use std::thread;
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
thread::spawn(move || {
c_mutex.lock_with_local_then(&NODE, |data| *data = 10);
})
.join().expect("thread::spawn failed");
assert_eq!(mutex.lock_with_local_then(&NODE, |data| *data), 10);
Compile fail: borrows of the data cannot escape the given closure:
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Mutex::new(1);
let borrow = mutex.lock_with_local_then(&NODE, |data| &*data);
Panic: thread local node cannot be borrowed more than once at the same time:
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Mutex::new(0);
mutex.lock_with_local_then(&NODE, |_data| {
// `NODE` is already mutably borrowed in this thread by the
// enclosing `lock_with_local_then`, the borrow is live for the full
// duration of this closure scope.
let mutex = Mutex::new(());
mutex.lock_with_local_then(&NODE, |_data| ());
});
Sourcepub unsafe fn lock_with_local_then_unchecked<F, Ret>(
&self,
node: &'static LocalMutexNode,
f: F,
) -> Ret
Available on crate feature thread_local
only.
pub unsafe fn lock_with_local_then_unchecked<F, Ret>( &self, node: &'static LocalMutexNode, f: F, ) -> Ret
thread_local
only.Acquires this mutex and then runs the closure against the protected data.
This function will block the local thread until it is available to acquire the mutex. Upon acquiring the mutex, the user provided closure will be executed against the mutex protected data. Once the closure goes out of scope, it will unlock the mutex.
To acquire a MCS lock through this function, it’s also required a
queue node, which is a record that keeps a link for forming the queue,
to be stored in the current locking thread local storage. See
LocalMutexNode
and thread_local_node!
.
This function will block if the lock is unavailable.
§Safety
Unlike lock_with_local_then
, this method is unsafe because it does not
check if the current thread local node is already mutably borrowed. If
the current thread local node is already borrowed, calling this
function is undefined behavior.
§Panics
Panics if the key currently has its destructor running, and it may panic if the destructor has previously been run for this thread.
§Examples
use std::sync::Arc;
use std::thread;
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);
thread::spawn(move || unsafe {
c_mutex.lock_with_local_then_unchecked(&NODE, |data| *data = 10);
})
.join().expect("thread::spawn failed");
assert_eq!(mutex.lock_with_local_then(&NODE, |data| *data), 10);
Compile fail: borrows of the data cannot escape the given closure:
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Mutex::new(1);
let data = unsafe {
mutex.lock_with_local_then_unchecked(&NODE, |data| &*data)
};
Undefined behavior: thread local node cannot be borrowed more than once at the same time:
use mcslock::raw::spins::Mutex;
mcslock::thread_local_node!(static NODE);
let mutex = Mutex::new(0);
mutex.lock_with_local_then(&NODE, |_data| unsafe {
// UB: `NODE` is already mutably borrowed in this thread by the
// enclosing `lock_with_local_then`, the borrow is live for the full
// duration of this closure scope.
let mutex = Mutex::new(());
mutex.lock_with_local_then_unchecked(&NODE, |_data| ());
});