| 1 | // Copyright 2018 Amanieu d'Antras |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or |
| 4 | // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or |
| 5 | // http://opensource.org/licenses/MIT>, at your option. This file may not be |
| 6 | // copied, modified, or distributed except according to those terms. |
| 7 | |
| 8 | //! This library provides type-safe and fully-featured [`Mutex`] and [`RwLock`] |
| 9 | //! types which wrap a simple raw mutex or rwlock type. This has several |
| 10 | //! benefits: not only does it eliminate a large portion of the work in |
| 11 | //! implementing custom lock types, it also allows users to write code which is |
| 12 | //! generic with regards to different lock implementations. |
| 13 | //! |
| 14 | //! Basic usage of this crate is very straightforward: |
| 15 | //! |
| 16 | //! 1. Create a raw lock type. This should only contain the lock state, not any |
| 17 | //! data protected by the lock. |
| 18 | //! 2. Implement the `RawMutex` trait for your custom lock type. |
| 19 | //! 3. Export your mutex as a type alias for `lock_api::Mutex`, and |
| 20 | //! your mutex guard as a type alias for `lock_api::MutexGuard`. |
| 21 | //! See the [example](#example) below for details. |
| 22 | //! |
| 23 | //! This process is similar for [`RwLock`]s, except that two guards need to be |
| 24 | //! exported instead of one. (Or 3 guards if your type supports upgradable read |
| 25 | //! locks, see [extension traits](#extension-traits) below for details) |
| 26 | //! |
| 27 | //! # Example |
| 28 | //! |
| 29 | //! ``` |
| 30 | //! use lock_api::{RawMutex, Mutex, GuardSend}; |
| 31 | //! use std::sync::atomic::{AtomicBool, Ordering}; |
| 32 | //! |
| 33 | //! // 1. Define our raw lock type |
| 34 | //! pub struct RawSpinlock(AtomicBool); |
| 35 | //! |
| 36 | //! // 2. Implement RawMutex for this type |
| 37 | //! unsafe impl RawMutex for RawSpinlock { |
| 38 | //! const INIT: RawSpinlock = RawSpinlock(AtomicBool::new(false)); |
| 39 | //! |
| 40 | //! // A spinlock guard can be sent to another thread and unlocked there |
| 41 | //! type GuardMarker = GuardSend; |
| 42 | //! |
| 43 | //! fn lock(&self) { |
| 44 | //! // Note: This isn't the best way of implementing a spinlock, but it |
| 45 | //! // suffices for the sake of this example. |
| 46 | //! while !self.try_lock() {} |
| 47 | //! } |
| 48 | //! |
| 49 | //! fn try_lock(&self) -> bool { |
| 50 | //! self.0 |
| 51 | //! .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) |
| 52 | //! .is_ok() |
| 53 | //! } |
| 54 | //! |
| 55 | //! unsafe fn unlock(&self) { |
| 56 | //! self.0.store(false, Ordering::Release); |
| 57 | //! } |
| 58 | //! } |
| 59 | //! |
| 60 | //! // 3. Export the wrappers. This are the types that your users will actually use. |
| 61 | //! pub type Spinlock<T> = lock_api::Mutex<RawSpinlock, T>; |
| 62 | //! pub type SpinlockGuard<'a, T> = lock_api::MutexGuard<'a, RawSpinlock, T>; |
| 63 | //! ``` |
| 64 | //! |
| 65 | //! # Extension traits |
| 66 | //! |
| 67 | //! In addition to basic locking & unlocking functionality, you have the option |
| 68 | //! of exposing additional functionality in your lock types by implementing |
| 69 | //! additional traits for it. Examples of extension features include: |
| 70 | //! |
| 71 | //! - Fair unlocking (`RawMutexFair`, `RawRwLockFair`) |
| 72 | //! - Lock timeouts (`RawMutexTimed`, `RawRwLockTimed`) |
| 73 | //! - Downgradable write locks (`RawRwLockDowngradable`) |
| 74 | //! - Recursive read locks (`RawRwLockRecursive`) |
| 75 | //! - Upgradable read locks (`RawRwLockUpgrade`) |
| 76 | //! |
| 77 | //! The `Mutex` and `RwLock` wrappers will automatically expose this additional |
| 78 | //! functionality if the raw lock type implements these extension traits. |
| 79 | //! |
| 80 | //! # Cargo features |
| 81 | //! |
| 82 | //! This crate supports three cargo features: |
| 83 | //! |
| 84 | //! - `owning_ref`: Allows your lock types to be used with the `owning_ref` crate. |
| 85 | //! - `arc_lock`: Enables locking from an `Arc`. This enables types such as `ArcMutexGuard`. Note that this |
| 86 | //! requires the `alloc` crate to be present. |
| 87 | |
| 88 | #![no_std ] |
| 89 | #![cfg_attr (docsrs, feature(doc_auto_cfg))] |
| 90 | #![warn (missing_docs)] |
| 91 | #![warn (rust_2018_idioms)] |
| 92 | |
| 93 | #[macro_use ] |
| 94 | extern crate scopeguard; |
| 95 | |
| 96 | #[cfg (feature = "arc_lock" )] |
| 97 | extern crate alloc; |
| 98 | |
| 99 | /// Marker type which indicates that the Guard type for a lock is `Send`. |
| 100 | pub struct GuardSend(()); |
| 101 | |
| 102 | /// Marker type which indicates that the Guard type for a lock is not `Send`. |
| 103 | pub struct GuardNoSend(*mut ()); |
| 104 | |
| 105 | unsafe impl Sync for GuardNoSend {} |
| 106 | |
| 107 | mod mutex; |
| 108 | pub use crate::mutex::*; |
| 109 | |
| 110 | #[cfg (feature = "atomic_usize" )] |
| 111 | mod remutex; |
| 112 | #[cfg (feature = "atomic_usize" )] |
| 113 | pub use crate::remutex::*; |
| 114 | |
| 115 | mod rwlock; |
| 116 | pub use crate::rwlock::*; |
| 117 | |