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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//! This crate provides a set of synchronized initialization primitives, which
//! are primarily useful for lazy and one-time initialization of static
//! variables.
//!
//! # Synchronization Primitives
//!
//! With the `std` feature enabled (which is the default setting), this crate
//! provides the [`Once`], [`OnceCell`] and [`Lazy`] types and the equivalents
//! of these types using spin-locks in the `spin` sub-module.
//!
//! ## Lazy
//!
//! The [`Lazy`] type has the same functionality as the `lazy_static!` macro,
//! but works without any macros.
//!
//! ## Once
//!
//! This type is very similar to the `std::sync::Once` type in the standard
//! library, but features a richer API.
//!
//! ## OnceCell
//!
//! A cell type with interior mutability that can be only written to once and
//! only allows read access to the contained data after initialization.
//!
//! # Credits
//!
//! Inspiration for this crate is heavily drawn from [`once_cell`](https://crates.io/crates/once_cell),
//! but features clear distinctions between blocking and non-blocking operations
//! and support for `#[no_std]` environments out of the box, by offering
//! additional synchronization primitives using spin-locks instead of OS reliant
//! blocking mechanisms.
//! Unlike many other crates, support for the `#[no_std]` compatible types is
//! not mutually exclusive with using the types relying on the presence of the
//! standard library.
//!
//! The idea for the implementation of the [`Once`]/[`OnceCell`] types is also
//! directly inspired by the implementation in the standard library.
//! The reasoning behind re-implementing [`Once`] is the fairly restricted and
//! partly unstable API of the version in the standard library.

#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
#![deny(missing_docs)]

#[cfg(test)]
#[macro_use]
mod tests;

pub mod spin;
pub mod raw {
    //! Generic 'raw' types exposed through various type aliases.
    pub use crate::cell::OnceCell;
    pub use crate::lazy::Lazy;
}

mod cell;
mod lazy;
mod state;
#[cfg(feature = "std")]
mod with_std;

mod internal {
    pub trait Internal {}
}

pub use crate::cell::{TryGetError, TryInitError};

use crate::internal::Internal;
#[cfg(feature = "std")]
use crate::with_std::ParkThread;

/// Whenever a poisoned [`OnceCell`] is encountered, the panic is propagated
/// with this message.
const POISON_PANIC_MSG: &str = "OnceCell instance has been poisoned.";

#[cfg(feature = "std")]
/// A type for lazy initialization of e.g. global static variables, which
/// provides the same functionality as the `lazy_static!` macro.
///
/// For the API of this type alias, see the API of the generic
/// [`Lazy`](crate::raw::Lazy) type.
///
/// # Examples
///
/// ```
/// use std::sync::Mutex;
///
/// use conquer_once::Lazy;
///
/// static MUTEX: Lazy<Mutex<Vec<i32>>> = Lazy::new(Mutex::default);
///
/// let mut lock = MUTEX.lock().unwrap();
///
/// lock.push(1);
/// lock.push(2);
/// lock.push(3);
///
/// assert_eq!(lock.as_slice(), &[1, 2, 3]);
/// ```
///
/// The associated [`new`](crate::raw::Lazy::new) function can be used with any function or
/// closure that implements `Fn() -> T`.
///
/// ```
/// use std::collections::HashMap;
///
/// use conquer_once::Lazy;
///
/// static CAPITALS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
///     let mut map = HashMap::new();
///     map.insert("Norway", "Oslo");
///     map.insert("Belgium", "Brussels");
///     map.insert("Latvia", "Riga");
///     map
/// });
///
/// assert_eq!(CAPITALS.get(&"Norway"), Some(&"Oslo"));
/// assert_eq!(CAPITALS.get(&"Belgium"), Some(&"Brussels"));
/// assert_eq!(CAPITALS.get(&"Latvia"), Some(&"Riga"));
/// ```
pub type Lazy<T, F = fn() -> T> = crate::lazy::Lazy<T, ParkThread, F>;

#[cfg(feature = "std")]
/// An interior mutability cell type which allows synchronized one-time
/// initialization and read-only access exclusively after initialization.
///
/// For the API of this type alias, see the generic
/// [`OnceCell`](crate::raw::OnceCell) type.
///
/// # Examples
///
/// ```
/// use conquer_once::OnceCell;
///
/// #[derive(Copy, Clone)]
/// struct Configuration {
///     mode: i32,
///     threshold: u64,
///     msg: &'static str,
/// }
///
/// static CONFIG: OnceCell<Configuration> = OnceCell::uninit();
///
/// // producer thread
/// CONFIG.init_once(|| Configuration {
///     mode: 2,
///     threshold: 128,
///     msg: "..."
/// });
///
/// // consumer thread
/// let res = CONFIG.get().copied();
/// if let Some(config) = res {
///     assert_eq!(config.mode, 2);
///     assert_eq!(config.threshold, 128);
/// }
/// ```
pub type OnceCell<T> = crate::cell::OnceCell<T, ParkThread>;

#[cfg(feature = "std")]
/// A synchronization primitive which can be used to run a one-time global
/// initialization.
///
/// For the API of this type alias, see the generic
/// [`OnceCell`](crate::raw::OnceCell) type.
/// This is a specialization with `T = ()`.
///
/// # Examples
///
/// ```
/// use conquer_once::Once;
///
/// static mut GLOBAL: usize = 0;
/// static INIT: Once = Once::uninit();
///
/// fn get_global() -> usize {
///     // this is safe because the `Once` ensures the `static mut` is assigned
///     // by only one thread and without data races.
///     unsafe {
///         INIT.init_once(|| {
///             GLOBAL = expensive_computation();
///         });
///         # assert_eq!(GLOBAL, 1);
///         GLOBAL
///     }
/// }
///
/// fn expensive_computation() -> usize {
///     // ...
///     # 1
/// }
/// ```
pub type Once = OnceCell<()>;