#synchronization-primitive #lock #mutex #rwlock #locking #no-std

no-std lock_api

Wrappers to create fully-featured Mutex and RwLock types. Compatible with no_std.

23 releases

0.4.12 Apr 25, 2024
0.4.11 Oct 17, 2023
0.4.10 Jun 5, 2023
0.4.9 Sep 20, 2022
0.1.3 Jun 18, 2018

#350 in Concurrency

Download history 2509241/week @ 2024-07-24 2632953/week @ 2024-07-31 2894338/week @ 2024-08-07 2712134/week @ 2024-08-14 2862661/week @ 2024-08-21 2681165/week @ 2024-08-28 3070785/week @ 2024-09-04 2771717/week @ 2024-09-11 2682190/week @ 2024-09-18 3058204/week @ 2024-09-25 3575303/week @ 2024-10-02 3780588/week @ 2024-10-09 3473458/week @ 2024-10-16 2739458/week @ 2024-10-23 2658397/week @ 2024-10-30 2218909/week @ 2024-11-06

11,754,015 downloads per month
Used in 32,678 crates (91 directly)

MIT/Apache

185KB
3K SLoC

This library provides type-safe and fully-featured Mutex and RwLock types which wrap a simple raw mutex or rwlock type. This has several benefits: not only does it eliminate a large portion of the work in implementing custom lock types, it also allows users to write code which is generic with regards to different lock implementations.

Basic usage of this crate is very straightforward:

  1. Create a raw lock type. This should only contain the lock state, not any data protected by the lock.
  2. Implement the RawMutex trait for your custom lock type.
  3. Export your mutex as a type alias for lock_api::Mutex, and your mutex guard as a type alias for lock_api::MutexGuard. See the example below for details.

This process is similar for RwLocks, except that two guards need to be exported instead of one. (Or 3 guards if your type supports upgradable read locks, see extension traits below for details)

Example

use lock_api::{RawMutex, Mutex, GuardSend};
use std::sync::atomic::{AtomicBool, Ordering};

// 1. Define our raw lock type
pub struct RawSpinlock(AtomicBool);

// 2. Implement RawMutex for this type
unsafe impl RawMutex for RawSpinlock {
    const INIT: RawSpinlock = RawSpinlock(AtomicBool::new(false));

    // A spinlock guard can be sent to another thread and unlocked there
    type GuardMarker = GuardSend;

    fn lock(&self) {
        // Note: This isn't the best way of implementing a spinlock, but it
        // suffices for the sake of this example.
        while !self.try_lock() {}
    }

    fn try_lock(&self) -> bool {
        self.0
            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
            .is_ok()
    }

    unsafe fn unlock(&self) {
        self.0.store(false, Ordering::Release);
    }
}

// 3. Export the wrappers. This are the types that your users will actually use.
pub type Spinlock<T> = lock_api::Mutex<RawSpinlock, T>;
pub type SpinlockGuard<'a, T> = lock_api::MutexGuard<'a, RawSpinlock, T>;

Extension traits

In addition to basic locking & unlocking functionality, you have the option of exposing additional functionality in your lock types by implementing additional traits for it. Examples of extension features include:

  • Fair unlocking (RawMutexFair, RawRwLockFair)
  • Lock timeouts (RawMutexTimed, RawRwLockTimed)
  • Downgradable write locks (RawRwLockDowngradable)
  • Recursive read locks (RawRwLockRecursive)
  • Upgradable read locks (RawRwLockUpgrade)

The Mutex and RwLock wrappers will automatically expose this additional functionality if the raw lock type implements these extension traits.

Cargo features

This crate supports three cargo features:

  • owning_ref: Allows your lock types to be used with the owning_ref crate.
  • arc_lock: Enables locking from an Arc. This enables types such as ArcMutexGuard. Note that this requires the alloc crate to be present.

Dependencies

~195KB