hermit_sync/
lib.rs

1//! # Overview
2//!
3//! hermit-sync provides synchronization primitives targeted at operating system kernels.
4//!
5//! # Interrupts
6//!
7//! [`without_interrupts`] runs a closure with disabled interrupts.
8//!
9//! # Mutexes
10//!
11//! This crate provides three kinds of mutexes based on [`lock_api::RawMutex`]:
12//! * [`RawSpinMutex`] is a simple [test and test-and-set] [spinlock] with [exponential backoff].
13//! * [`RawTicketMutex`] is a [fair] [ticket lock] with [exponential backoff].
14//! * [`RawInterruptMutex`] wraps another mutex and disables interrupts while locked.
15//!
16//! [test and test-and-set]: https://en.wikipedia.org/wiki/Test_and_test-and-set
17//! [spinlock]: https://en.wikipedia.org/wiki/Spinlock
18//! [exponential backoff]: https://en.wikipedia.org/wiki/Exponential_backoff
19//! [fair]: https://en.wikipedia.org/wiki/Unbounded_nondeterminism
20//! [ticket lock]: https://en.wikipedia.org/wiki/Ticket_lock
21//!
22//! For API documentation see [`lock_api::Mutex`].
23//!
24//! ## Examples
25//!
26//! ```
27//! use hermit_sync::InterruptSpinMutex;
28//!
29//! static NUMBER: InterruptSpinMutex<usize> = InterruptSpinMutex::new(0);
30//!
31//! // Modify the data
32//! *NUMBER.lock() = 2;
33//!
34//! // Read the data
35//! let answer = *NUMBER.lock();
36//! assert_eq!(2, answer);
37//! ```
38//!
39//! # Initializing Static Data
40//!
41//! There are two primitives for safely initializing static data based on [`generic_once_cell`] and [`RawSpinMutex`]:
42//! * [`OnceCell`] can be written to only once and can then be accessed without locking.
43//! * [`Lazy`] wraps a [`OnceCell`] and is initialized on the first access from a closure.
44//!
45//! For API documentation see [`generic_once_cell::OnceCell`] and [`generic_once_cell::Lazy`].
46//!
47//! ## Examples
48//!
49//! ```
50//! use std::collections::HashMap;
51//!
52//! use hermit_sync::InterruptLazy;
53//!
54//! static MAP: InterruptLazy<HashMap<usize, String>> = InterruptLazy::new(|| {
55//!     // This is run on the first access of MAP.
56//!     let mut map = HashMap::new();
57//!     map.insert(42, "Ferris".to_string());
58//!     map.insert(3, "やれやれだぜ".to_string());
59//!     map
60//! });
61//!
62//! assert_eq!("Ferris", MAP.get(&42).unwrap());
63//! ```
64//!
65//! # Accessing Static Data Mutably
66//!
67//! There is [`ExclusiveCell`] for safely accessing static data mutable _once_.
68//!
69//! # Type Definitions
70//!
71//! This crate provides a lot of type definitions for ease of use:
72//!
73//! | [`RawMutex`]        | Base                  | With [`RawInterruptMutex`]     |
74//! | ------------------- | --------------------- | ------------------------------ |
75//! | `R`                 | [`Mutex`]             | [`InterruptMutex`]             |
76//! | [`RawSpinMutex`]    |                       | [`RawInterruptSpinMutex`]      |
77//! |                     | [`SpinMutex`]         | [`InterruptSpinMutex`]         |
78//! |                     | [`SpinMutexGuard`]    | [`InterruptSpinMutexGuard`]    |
79//! |                     | [`OnceCell`]          | [`InterruptOnceCell`]          |
80//! |                     | [`Lazy`]              | [`InterruptLazy`]              |
81//! | [`RawOneShotMutex`] |                       | [`RawInterruptOneShotMutex`]   |
82//! |                     | [`OneShotMutex`]      | [`InterruptOneShotMutex`]      |
83//! |                     | [`OneShotMutexGuard`] | [`InterruptOneShotMutexGuard`] |
84//! | [`RawTicketMutex`]  |                       | [`RawInterruptTicketMutex`]    |
85//! |                     | [`TicketMutex`]       | [`InterruptTicketMutex`]       |
86//! |                     | [`TicketMutexGuard`]  | [`InterruptTicketMutexGuard`]  |
87//!
88//! [`RawMutex`]: lock_api::RawMutex
89//! [`Mutex`]: lock_api::Mutex
90
91#![cfg_attr(not(test), no_std)]
92#![warn(unsafe_op_in_unsafe_fn)]
93
94pub(crate) mod mutex;
95#[cfg(not(feature = "all-one-shot"))]
96pub(crate) mod rwlock {
97    /// A simple spinning, read-preferring readers-writer lock with exponential backoff.
98    pub type RawRwSpinLock = spinning_top::RawRwSpinlock<spinning_top::relax::Backoff>;
99
100    /// A [`lock_api::RwLock`] based on [`RawRwSpinLock`].
101    pub type RwSpinLock<T> = lock_api::RwLock<RawRwSpinLock, T>;
102
103    /// A [`lock_api::RwLockReadGuard`] based on [`RawRwSpinLock`].
104    pub type RwSpinLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, RawRwSpinLock, T>;
105
106    /// A [`lock_api::RwLockUpgradableReadGuard`] based on [`RawRwSpinLock`].
107    pub type RwSpinLockUpgradableReadGuard<'a, T> =
108        lock_api::RwLockUpgradableReadGuard<'a, RawRwSpinLock, T>;
109
110    /// A [`lock_api::RwLockWriteGuard`] based on [`RawRwSpinLock`].
111    pub type RwSpinLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, RawRwSpinLock, T>;
112}
113#[cfg(feature = "all-one-shot")]
114pub(crate) mod rwlock {
115    pub use one_shot_mutex::{
116        OneShotRwLock as RwSpinLock, OneShotRwLockReadGuard as RwSpinLockReadGuard,
117        OneShotRwLockUpgradableReadGuard as RwSpinLockUpgradableReadGuard,
118        OneShotRwLockWriteGuard as RwSpinLockWriteGuard, RawOneShotRwLock as RawRwSpinLock,
119    };
120}
121
122pub use exclusive_cell::{CallOnce, CallOnceError, ExclusiveCell};
123pub use interrupt_mutex::{InterruptMutex, InterruptMutexGuard, RawInterruptMutex};
124pub use interrupts::without as without_interrupts;
125pub use mutex::spin::{RawSpinMutex, SpinMutex, SpinMutexGuard};
126pub use mutex::ticket::{RawTicketMutex, TicketMutex, TicketMutexGuard};
127pub use mutex::{
128    InterruptOneShotMutex, InterruptOneShotMutexGuard, InterruptSpinMutex, InterruptSpinMutexGuard,
129    InterruptTicketMutex, InterruptTicketMutexGuard, RawInterruptOneShotMutex,
130    RawInterruptSpinMutex, RawInterruptTicketMutex,
131};
132pub use one_shot_mutex::{
133    OneShotMutex, OneShotMutexGuard, OneShotRwLock, OneShotRwLockReadGuard,
134    OneShotRwLockUpgradableReadGuard, OneShotRwLockWriteGuard, RawOneShotMutex, RawOneShotRwLock,
135};
136pub use rwlock::{
137    RawRwSpinLock, RwSpinLock, RwSpinLockReadGuard, RwSpinLockUpgradableReadGuard,
138    RwSpinLockWriteGuard,
139};
140
141/// A [`generic_once_cell::OnceCell`], initialized using [`RawSpinMutex`].
142pub type OnceCell<T> = generic_once_cell::OnceCell<RawSpinMutex, T>;
143
144/// A [`generic_once_cell::Lazy`], initialized using [`RawSpinMutex`].
145pub type Lazy<T, F = fn() -> T> = generic_once_cell::Lazy<RawSpinMutex, T, F>;
146
147/// A [`generic_once_cell::OnceCell`], initialized using [`RawInterruptSpinMutex`].
148pub type InterruptOnceCell<T> = generic_once_cell::OnceCell<RawInterruptSpinMutex, T>;
149
150/// A [`generic_once_cell::Lazy`], initialized using [`RawInterruptSpinMutex`].
151pub type InterruptLazy<T, F = fn() -> T> = generic_once_cell::Lazy<RawInterruptSpinMutex, T, F>;