RwLock

Struct RwLock 

Source
pub struct RwLock<T: ?Sized> { /* private fields */ }
Expand description

A reader-writer lock.

It behaves like std::sync::RwLock except for using spinlock. What is more, the constructor is a const function; i.e. it is possible to declare static RwLock<T> variable as long as the inner data can be built statically.

This type of lock allows either a number of readers or at most one writer at the same time. Readers are allowed read-only access (shared access) to the underlying data while the writer is allowed read/write access (exclusive access.)

In comparison, a Mutex does not distinguish between readers and writers, therefore blocking any threads waiting for the lock to become available. An RwLock will allow any number of readers to acquire the lock as long as a writer is not holding the lock.

There is no priority difference with respect to the ordering of whether contentious readers or writers will acquire the lock first.

§Poisoning

An RwLock, like Mutex, will become poisoned on a panic. Note, however, that an RwLock may only be poisoned if a panic occurs while it is locked exclusively (write mode). If a panic occurs in any reader, then the lock will not be poisoned.

§Examples

Create a variable protected by a RwLock, increment it by 2 in worker threads at the same time, and check the variable was updated rightly.

use spin_sync::RwLock;
use std::sync::Arc;
use std::thread;

const WORKER_NUM: usize = 10;
let mut handles = Vec::with_capacity(WORKER_NUM);

// We can declare static RwLock<usize> variable because RwLock::new is a const function.
static RWLOCK: RwLock<usize> = RwLock::new(0);

// Create worker threads to inclement the value by 2.
for _ in 0..WORKER_NUM {
    let handle = thread::spawn(move || {
        let mut num = RWLOCK.write().unwrap();
        *num += 2;
    });

    handles.push(handle);
}

// Make sure the value is always multipile of 2 even if some worker threads
// are working.
//
// Enclosing the lock with `{}` to drop it before waiting for the worker
// threads; otherwise, deadlocks could be occurred.
{
    let num = RWLOCK.read().unwrap();
    assert_eq!(0, *num % 2);
}

// Wait for the all worker threads are finished.
for handle in handles {
    handle.join().unwrap();
}

// Make sure the value is incremented by 2 times the worker count.
let num = RWLOCK.read().unwrap();
assert_eq!(2 * WORKER_NUM, *num);

Implementations§

Source§

impl<T> RwLock<T>

Source

pub const fn new(t: T) -> Self

Creates a new instance in unlocked state ready for use.

§Examples

Declare as a static variable.

use spin_sync::RwLock;

static LOCK: RwLock<i32> = RwLock::new(5);

Declare as a local variable.

use spin_sync::RwLock;

let lock = RwLock::new(5);
Source

pub fn into_inner(self) -> LockResult<T>

Consumes this instance and returns the underlying data.

Note that this method won’t acquire any lock because we know there is no other references to self.

§Errors

If another user panicked while holding the exclusive write lock of this instance, this method call wraps the guard in an error and returns it.

§Examples
use spin_sync::RwLock;

let rwlock = RwLock::new(0);
assert_eq!(0, rwlock.into_inner().unwrap());
Source§

impl<T: ?Sized> RwLock<T>

Source

pub const MAX_READ_LOCK_COUNT: u64 = 4_611_686_018_427_387_903u64

The maximum shared read locks of each instance.

Source

pub fn read(&self) -> LockResult<RwLockReadGuard<'_, T>>

Blocks the current thread until acquiring a shared read lock, and returns an RAII guard object.

The actual flow will be as follows.

  1. User calls this method.
    1. Blocks until this thread acquires a shared read lock (i.e. until the exclusive write lock is held.)
    2. Creates an RAII guard object.
    3. Wrapps the guard in Result and returns it. If this instance has been poisoned, it is wrapped in an Err; otherwise wrapped in an Ok.
  2. User accesses to the underlying data to read through the guard. (No write access is then.)
  3. The guard is dropped (falls out of scope) and the lock is released.
§Errors

If another user panicked while holding the exclusive write lock of this instance, this method call wraps the guard in an error and returns it.

§Panics

This method panics if MAX_READ_LOCK_COUNT shared locks are.

§Examples
use spin_sync::RwLock;

let lock = RwLock::new(1);

let guard1 = lock.read().unwrap();
assert_eq!(1, *guard1);

let guard2 = lock.read().unwrap();
assert_eq!(1, *guard2);
Source

pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<'_, T>>

Attempts to acquire a shared read lock and returns an RAII guard object if succeeded.

Behaves like read except for this method returns an error immediately if the exclusive write lock is being held.

This function does not block.

The actual flow will be as follows.

  1. User calls this method.
    1. Tries to acquire a shared read lock. If failed (i.e. if the exclusive write lock is being held,) returns an error immediately and this flow is finished here.
    2. Creates an RAII guard object.
    3. Wrapps the guard in Result and returns it. If this instance has been poisoned, it is wrapped in an Err; otherwise wrapped in an Ok.
  2. User accesses to the underlying data to read through the guard. (No write access is at then.)
  3. The guard is dropped (falls out of scope) and the lock is released.
§Panics

This method panics if MAX_READ_LOCK shared read locks are.

§Errors
  • If another user is holding the exclusive write lock, TryLockError::WouldBlock is returned.
  • If this method call succeeded to acquire a shared read lock, and if another user had panicked while holding the exclusive write lock, TryLockError::Poisoned is returned.
§Examples
use spin_sync::RwLock;

let lock = RwLock::new(1);

let guard0 = lock.try_read().unwrap();
assert_eq!(1, *guard0);

let guard1 = lock.try_read().unwrap();
assert_eq!(1, *guard1);
Source

pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<'_, T>>

Attempts to acquire the exclusive write lock and returns an RAII guard object if succeeded.

Behaves like write except for this method returns an error immediately if any other lock (either read lock or write lock) is being held.

This method does not block.

The actual flow will be as follows.

  1. User calls this method.
    1. Tries to acquire the exclusive write lock. If failed (i.e. if any other lock is being held,) returns an error immediately and this flow is finished here.
    2. Creates an RAII guard object.
    3. Wraps the guard in Result and returns it. If this instance has been poisoned, it is wrapped in an Err; otherwise wrapped in an Ok.
  2. User accesses to the underlying data to read/write through the guard. (No other access is then.)
  3. The guard is dropped (falls out of scope) and the lock is released.
§Errors
  • If another user is holding any other lock (either read lock or write lock), TryLockError::WouldBlock is returned.
  • If this method call succeeded to acquire the lock, and if another user had panicked while holding the exclusive write lock, TryLockError::Poisoned is returned.
§Examples
use spin_sync::RwLock;

let lock = RwLock::new(1);

let mut guard = lock.try_write().unwrap();
assert_eq!(1, *guard);

*guard += 1;
assert_eq!(2, *guard);

assert!(lock.try_write().is_err());
assert!(lock.try_read().is_err());
Source

pub fn write(&self) -> LockResult<RwLockWriteGuard<'_, T>>

Blocks the current thread until acquiring the exclusive write lock, and returns an RAII guard object.

The actual flow will be as follows.

  1. User calls this method.
    1. Blocks until this thread acquires the exclusive write lock (i.e. until any other lock is held.)
    2. Creates an RAII guard object.
    3. Wrapps the guard in Result and returns it. If this instance has been poisoned, it is wrapped in an Err; otherwise wrapped in an Ok.
  2. User accesses to the underlying data to read/write through the guard. (No other access is then.)
  3. The guard is dropped (falls out of scope) and the lock is released.
§Errors

If another user panicked while holding the exclusive write lock of this instance, this method call wraps the guard in an error and returns it.

§Examples
use spin_sync::RwLock;

let lock = RwLock::new(0);

let mut guard = lock.write().unwrap();
assert_eq!(0, *guard);

*guard += 1;
assert_eq!(1, *guard);

assert_eq!(true, lock.try_read().is_err());
assert_eq!(true, lock.try_write().is_err());
Source

pub fn is_poisoned(&self) -> bool

Determines whether the lock is poisoned or not.

§Warning

This function won’t acquire any lock. If another thread is active, the rwlock can become poisoned at any time. You should not trust a false value for program correctness without additional synchronization.

§Examples
use spin_sync::RwLock;
use std::sync::Arc;
use std::thread;

let lock = Arc::new(RwLock::new(0));
assert_eq!(false, lock.is_poisoned());

{
    let lock = lock.clone();

    let _ = thread::spawn(move || {
        // This panic while holding the lock (`_guard` is in scope) will poison
        // the instance.
        let _guard = lock.write().unwrap();
        panic!("Poison here");
    }).join();
}

assert_eq!(true, lock.is_poisoned());
Source

pub fn get_mut(&mut self) -> LockResult<&mut T>

Returns a mutable reference to the underlying data.

Note that this method won’t acquire any lock because we know there is no other references to self.

§Errors

If another user panicked while holding the exclusive write lock of this instance, this method call wraps the guard in an error and returns it.

§Examples
use spin_sync::RwLock;

let mut lock = RwLock::new(0);
*lock.get_mut().unwrap() = 10;
assert_eq!(*lock.read().unwrap(), 10);

Trait Implementations§

Source§

impl<T: ?Sized + Debug> Debug for RwLock<T>

Source§

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

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

impl<T: Default> Default for RwLock<T>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<T> From<T> for RwLock<T>

Source§

fn from(t: T) -> Self

Converts to this type from the input type.
Source§

impl<T: ?Sized> RefUnwindSafe for RwLock<T>

Source§

impl<T: ?Sized + Send> Send for RwLock<T>

Source§

impl<T: ?Sized + Send + Sync> Sync for RwLock<T>

Source§

impl<T: ?Sized> UnwindSafe for RwLock<T>

Auto Trait Implementations§

§

impl<T> !Freeze for RwLock<T>

§

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

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.