1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! # Overview
//!
//! hermit-sync provides synchronization primitives targeted at operating system kernels.
//!
//! # Interrupts
//!
//! [`without_interrupts`] runs a closure with disabled interrupts.
//!
//! # Mutexes
//!
//! This crate provides three kinds of mutexes based on [`lock_api::RawMutex`]:
//! * [`RawSpinMutex`] is a simple [test and test-and-set] [spinlock] with [exponential backoff].
//! * [`RawTicketMutex`] is a [fair] [ticket lock] with [exponential backoff].
//! * [`RawInterruptMutex`] wraps another mutex and disables interrupts while locked.
//!
//! [test and test-and-set]: https://en.wikipedia.org/wiki/Test_and_test-and-set
//! [spinlock]: https://en.wikipedia.org/wiki/Spinlock
//! [exponential backoff]: https://en.wikipedia.org/wiki/Exponential_backoff
//! [fair]: https://en.wikipedia.org/wiki/Unbounded_nondeterminism
//! [ticket lock]: https://en.wikipedia.org/wiki/Ticket_lock
//!
//! For API documentation see [`lock_api::Mutex`].
//!
//! ## Examples
//!
//! ```
//! use hermit_sync::InterruptSpinMutex;
//!
//! static NUMBER: InterruptSpinMutex<usize> = InterruptSpinMutex::new(0);
//!
//! // Modify the data
//! *NUMBER.lock() = 2;
//!
//! // Read the data
//! let answer = *NUMBER.lock();
//! assert_eq!(2, answer);
//! ```
//!
//! # Initializing Static Data
//!
//! There are two primitives for safely initializing static data based on [`generic_once_cell`] and [`RawSpinMutex`]:
//! * [`OnceCell`] can be written to only once and can then be accessed without locking.
//! * [`Lazy`] wraps a [`OnceCell`] and is initialized on the first access from a closure.
//!
//! For API documentation see [`generic_once_cell::OnceCell`] and [`generic_once_cell::Lazy`].
//!
//! ## Examples
//!
//! ```
//! use std::collections::HashMap;
//!
//! use hermit_sync::InterruptLazy;
//!
//! static MAP: InterruptLazy<HashMap<usize, String>> = InterruptLazy::new(|| {
//!     // This is run on the first access of MAP.
//!     let mut map = HashMap::new();
//!     map.insert(42, "Ferris".to_string());
//!     map.insert(3, "やれやれだぜ".to_string());
//!     map
//! });
//!
//! assert_eq!("Ferris", MAP.get(&42).unwrap());
//! ```
//!
//! # Accessing Static Data Mutably
//!
//! There is [`ExclusiveCell`] for safely accessing static data mutable _once_.
//!
//! # Type Definitions
//!
//! This crate provides a lot of type definitions for ease of use:
//!
//! | [`RawMutex`]       | Base                 | With [`RawInterruptMutex`]    |
//! | ------------------ | -------------------- | ----------------------------- |
//! | `R`                | [`Mutex`]            | [`InterruptMutex`]            |
//! | [`RawSpinMutex`]   |                      | [`RawInterruptSpinMutex`]     |
//! |                    | [`SpinMutex`]        | [`InterruptSpinMutex`]        |
//! |                    | [`SpinMutexGuard`]   | [`InterruptSpinMutexGuard`]   |
//! |                    | [`OnceCell`]         | [`InterruptOnceCell`]         |
//! |                    | [`Lazy`]             | [`InterruptLazy`]             |
//! | [`RawTicketMutex`] |                      | [`RawInterruptTicketMutex`]   |
//! |                    | [`TicketMutex`]      | [`InterruptTicketMutex`]      |
//! |                    | [`TicketMutexGuard`] | [`InterruptTicketMutexGuard`] |
//!
//! [`RawMutex`]: lock_api::RawMutex
//! [`Mutex`]: lock_api::Mutex

#![cfg_attr(not(test), no_std)]
#![warn(unsafe_op_in_unsafe_fn)]

pub(crate) mod interrupts;
pub(crate) mod mutex;

pub use exclusive_cell::{CallOnce, CallOnceError, ExclusiveCell};
pub use interrupts::without_interrupts;
pub use mutex::{
    interrupt::{InterruptMutex, InterruptMutexGuard, RawInterruptMutex},
    spin::{RawSpinMutex, SpinMutex, SpinMutexGuard},
    ticket::{RawTicketMutex, TicketMutex, TicketMutexGuard},
    InterruptSpinMutex, InterruptSpinMutexGuard, InterruptTicketMutex, InterruptTicketMutexGuard,
    RawInterruptSpinMutex, RawInterruptTicketMutex,
};

/// A [`generic_once_cell::OnceCell`], initialized using [`RawSpinMutex`].
pub type OnceCell<T> = generic_once_cell::OnceCell<RawSpinMutex, T>;

/// A [`generic_once_cell::Lazy`], initialized using [`RawSpinMutex`].
pub type Lazy<T, F = fn() -> T> = generic_once_cell::Lazy<RawSpinMutex, T, F>;

/// A [`generic_once_cell::OnceCell`], initialized using [`RawInterruptSpinMutex`].
pub type InterruptOnceCell<T> = generic_once_cell::OnceCell<RawInterruptSpinMutex, T>;

/// A [`generic_once_cell::Lazy`], initialized using [`RawInterruptSpinMutex`].
pub type InterruptLazy<T, F = fn() -> T> = generic_once_cell::Lazy<RawInterruptSpinMutex, T, F>;