Synchronization Primitives
OnceLock<T>
std::sync::OnceLock<T> is a thread-safe synchronization primitive that can be written to at most once and thereafter hands out lock-free, guard-free shared references &T to any number of concurrent threads. It supersedes std::sync::Once whenever one-time initialization also produces a value T.
Internal Synchronization Lifecycle of OnceLock<T>
OnceLock<T> combines an internal std::sync::Once state word (Incomplete, Running, Complete, Poisoned) with an UnsafeCell<MaybeUninit<T>>:
OnceLock<T> Lifecycle:
State 0: `Incomplete` (OnceLock::new())
|
| Thread A calls `.get_or_init(f)` (or `.set(val)`)
v
State 1: `Running` (Thread A runs `f()`; concurrent callers park on OS futex queue)
|
+-- If `f()` succeeds: writes `T`, sets state to `Complete` (Release), wakes waiters!
|
+-- If `f()` panics: sets state to `Poisoned` (or leaves uninit if fallible init)
v
State 2: `Complete`
- All subsequent `.get()` / `.get_or_init()` calls execute a single atomic `Acquire`
load and return `&T` directly with ZERO locks or guards!
Comparing OnceLock<T> and Mutex<Option<T>>
| Property | OnceLock<T> |
Mutex<Option<T>> |
|---|---|---|
| Return type of read access | Option<&T> or &T (plain reference, no guard!) |
MutexGuard<'_, Option<T>> (holds lock while reading!) |
| Concurrent read scalability | Wait-free atomic load (zero cache-line writes on read!) | Serializes readers and dirties mutex cache line on every .lock() |
Can value be replaced later via &self? |
No (write-once invariant enables returning &T without a guard) |
Yes (can overwrite Some(v) or reset to None) |
Usable directly in static? |
Yes (OnceLock::new() is const fn) |
Yes (Mutex::new(None) is const fn) |
Using OnceLock<T> for One-Time Global and Struct Initialization
use std::sync::OnceLock;
use std::thread;
#[derive(Debug)]
struct ClusterMetadata {
cluster_id: String,
worker_threads: usize,
}
static CLUSTER: OnceLock<ClusterMetadata> = OnceLock::new();
fn get_cluster() -> &'static ClusterMetadata {
CLUSTER.get_or_init(|| {
println!(" [initializing CLUSTER metadata exactly once]");
ClusterMetadata {
cluster_id: String::from("prod-dhaka-01"),
worker_threads: 8,
}
})
}
fn main() {
thread::scope(|s| {
for id in 0..3 {
s.spawn(move || {
// All 3 threads race to call `get_cluster()`; only 1 runs the closure,
// and all 3 receive a direct `&'static ClusterMetadata` reference!
let meta = get_cluster();
println!("thread #{id} sees cluster_id = {:?}", meta.cluster_id);
});
}
});
// Attempting `.set(...)` on an already initialized `OnceLock` returns `Err(val)`:
let duplicate_attempt = CLUSTER.set(ClusterMetadata {
cluster_id: String::from("ignored"),
worker_threads: 1,
});
println!("duplicate .set() returned Err: {}", duplicate_attempt.is_err());
}
[initializing CLUSTER metadata exactly once]
thread #0 sees cluster_id = "prod-dhaka-01"
thread #1 sees cluster_id = "prod-dhaka-01"
thread #2 sees cluster_id = "prod-dhaka-01"
duplicate .set() returned Err: true