Struct Mutex

Source
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>

Source

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);
Source

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>

Source

pub fn try_lock_then<F, Ret>(&self, f: F) -> Ret
where F: FnOnce(Option<&mut T>) -> 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());
Source

pub fn try_lock_with_then<'a, F, Ret>( &'a self, node: &'a mut MutexNode, f: F, ) -> Ret
where F: FnOnce(Option<&mut T>) -> 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());
Source

pub fn lock_then<F, Ret>(&self, f: F) -> Ret
where F: FnOnce(&mut T) -> 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);
Source

pub fn lock_with_then<'a, F, Ret>( &'a self, node: &'a mut MutexNode, f: F, ) -> Ret
where F: FnOnce(&mut T) -> 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>

Source

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);
Source

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>

Source

pub fn try_lock_with_local_then<F, Ret>( &self, node: &'static LocalMutexNode, f: F, ) -> Ret
where F: FnOnce(Option<&mut T>) -> Ret,

Available on crate feature 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| ());
});
Source

pub unsafe fn try_lock_with_local_then_unchecked<F, Ret>( &self, node: &'static LocalMutexNode, f: F, ) -> Ret
where F: FnOnce(Option<&mut T>) -> Ret,

Available on crate feature 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| ());
});
Source

pub fn lock_with_local_then<F, Ret>( &self, node: &'static LocalMutexNode, f: F, ) -> Ret
where F: FnOnce(&mut T) -> Ret,

Available on crate feature 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| ());
});
Source

pub unsafe fn lock_with_local_then_unchecked<F, Ret>( &self, node: &'static LocalMutexNode, f: F, ) -> Ret
where F: FnOnce(&mut T) -> Ret,

Available on crate feature 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| ());
});

Trait Implementations§

Source§

impl<T: ?Sized + Debug, R: Relax> Debug for Mutex<T, R>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Default, R> Default for Mutex<T, R>

Source§

fn default() -> Self

Creates a Mutex<T, R>, with the Default value for T.

Source§

impl<T, R> From<T> for Mutex<T, R>

Source§

fn from(data: T) -> Self

Creates a Mutex<T, R> from a instance of T.

Source§

impl<T: ?Sized + Send, R> Send for Mutex<T, R>

Source§

impl<T: ?Sized + Send, R> Sync for Mutex<T, R>

Auto Trait Implementations§

§

impl<T, R> !Freeze for Mutex<T, R>

§

impl<T, R> !RefUnwindSafe for Mutex<T, R>

§

impl<T, R> Unpin for Mutex<T, R>
where T: Unpin + ?Sized, R: Unpin,

§

impl<T, R> UnwindSafe for Mutex<T, R>
where T: UnwindSafe + ?Sized, R: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<!> for T

Source§

fn from(t: !) -> T

Converts to this type from the input type.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.