Skip to main content

tipc_std/
sync.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 KylinSoft Co., Ltd. <https://www.kylinos.cn/>
3// See LICENSES for license details.
4
5//! Synchronization primitives for tipc-std.
6//!
7//! Provides multi-thread-safe `Mutex`, `OnceLock`, and `LazyLock`
8//! using atomics, suitable for both x-kernel and LK environments.
9
10use core::{
11    cell::UnsafeCell,
12    ops::{Deref, DerefMut},
13    sync::atomic::{AtomicBool, AtomicU8, Ordering},
14};
15
16/// A mutual exclusion primitive based on `AtomicBool` spinlock.
17///
18/// Safe for multi-threaded environments.
19pub struct Mutex<T> {
20    locked: AtomicBool,
21    data: UnsafeCell<T>,
22}
23
24/// A guard that provides access to the data protected by a [`Mutex`].
25pub struct MutexGuard<'a, T> {
26    mutex: &'a Mutex<T>,
27}
28
29impl<T> Mutex<T> {
30    /// Creates a new mutex in an unlocked state.
31    pub const fn new(data: T) -> Self {
32        Self { locked: AtomicBool::new(false), data: UnsafeCell::new(data) }
33    }
34
35    /// Acquires the mutex, blocking until it is available.
36    ///
37    /// Returns `Ok` if the lock was acquired. In this implementation
38    /// the mutex is never poisoned, so `Ok` is always returned.
39    pub fn lock(&self) -> Result<MutexGuard<'_, T>, PoisonError<MutexGuard<'_, T>>> {
40        while self.locked.swap(true, Ordering::Acquire) {
41            core::hint::spin_loop();
42        }
43        Ok(MutexGuard { mutex: self })
44    }
45}
46
47// SAFETY: Mutex provides exclusive access via spinlock.
48unsafe impl<T: Send> Sync for Mutex<T> {}
49unsafe impl<T: Send> Send for Mutex<T> {}
50
51impl<T> Deref for MutexGuard<'_, T> {
52    type Target = T;
53
54    fn deref(&self) -> &T {
55        // SAFETY: We hold the lock, so we have exclusive access.
56        unsafe { &*self.mutex.data.get() }
57    }
58}
59
60impl<T> DerefMut for MutexGuard<'_, T> {
61    fn deref_mut(&mut self) -> &mut T {
62        // SAFETY: We hold the lock, so we have exclusive access.
63        unsafe { &mut *self.mutex.data.get() }
64    }
65}
66
67impl<T> Drop for MutexGuard<'_, T> {
68    fn drop(&mut self) {
69        self.mutex.locked.store(false, Ordering::Release);
70    }
71}
72
73/// Error type for source-level compatibility with `std::sync::PoisonError`.
74///
75/// This implementation never actually poisons.
76pub struct PoisonError<T> {
77    _guard: T,
78}
79
80impl<T> PoisonError<T> {
81    #[allow(dead_code)]
82    pub fn new(guard: T) -> Self {
83        PoisonError { _guard: guard }
84    }
85}
86
87impl<T> core::fmt::Debug for PoisonError<T> {
88    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
89        f.write_str("PoisonError { .. }")
90    }
91}
92
93impl<T> core::fmt::Display for PoisonError<T> {
94    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
95        f.write_str("poisoned lock: another task failed while holding the lock")
96    }
97}
98
99const UNINIT: u8 = 0;
100const INITIALIZING: u8 = 1;
101const INITIALIZED: u8 = 2;
102
103/// A synchronization primitive for one-time initialization.
104///
105/// Uses a three-state atomic with `compare_exchange` to ensure exactly
106/// one thread performs initialization.
107pub struct OnceLock<T> {
108    inner: UnsafeCell<Option<T>>,
109    state: AtomicU8,
110}
111
112// SAFETY: OnceLock ensures exclusive access during initialization via
113// compare_exchange, and only provides shared (&T) access after init.
114unsafe impl<T: Send> Sync for OnceLock<T> {}
115unsafe impl<T: Send> Send for OnceLock<T> {}
116
117impl<T> OnceLock<T> {
118    /// Creates a new empty `OnceLock`.
119    pub const fn new() -> Self {
120        OnceLock { inner: UnsafeCell::new(None), state: AtomicU8::new(UNINIT) }
121    }
122
123    /// Returns `Some(&value)` if initialized, `None` otherwise.
124    pub fn get(&self) -> Option<&T> {
125        if self.state.load(Ordering::Acquire) == INITIALIZED {
126            // SAFETY: After INITIALIZED is visible, inner is immutable.
127            unsafe { (*self.inner.get()).as_ref() }
128        } else {
129            None
130        }
131    }
132
133    /// Initializes the cell with `value` if not yet initialized.
134    pub fn set(&self, value: T) -> Result<(), T> {
135        match self.state.compare_exchange(
136            UNINIT,
137            INITIALIZING,
138            Ordering::Acquire,
139            Ordering::Acquire,
140        ) {
141            Ok(_) => {
142                // SAFETY: We hold the initialization lock.
143                unsafe {
144                    *self.inner.get() = Some(value);
145                }
146                self.state.store(INITIALIZED, Ordering::Release);
147                Ok(())
148            }
149            Err(_) => Err(value),
150        }
151    }
152
153    /// Initializes the cell with the result of `f` if not yet initialized,
154    /// then returns a reference to the value.
155    pub fn get_or_init<F>(&self, f: F) -> &T
156    where
157        F: FnOnce() -> T,
158    {
159        match self.state.compare_exchange(
160            UNINIT,
161            INITIALIZING,
162            Ordering::Acquire,
163            Ordering::Acquire,
164        ) {
165            Ok(_) => {
166                let value = f();
167                // SAFETY: We hold the initialization lock.
168                unsafe {
169                    *self.inner.get() = Some(value);
170                }
171                self.state.store(INITIALIZED, Ordering::Release);
172            }
173            Err(INITIALIZED) => {}
174            Err(_) => {
175                while self.state.load(Ordering::Acquire) != INITIALIZED {
176                    core::hint::spin_loop();
177                }
178            }
179        }
180        // SAFETY: state is INITIALIZED, so inner is Some and immutable.
181        unsafe { (*self.inner.get()).as_ref().unwrap() }
182    }
183}
184
185impl<T> Default for OnceLock<T> {
186    fn default() -> Self {
187        Self::new()
188    }
189}
190
191/// A synchronization primitive for lazy initialization.
192///
193/// Wraps `OnceLock<T>` and stores the initialization function in a
194/// `Mutex<Option<F>>`.
195pub struct LazyLock<T, F = fn() -> T> {
196    once: OnceLock<T>,
197    init: Mutex<Option<F>>,
198}
199
200// SAFETY: OnceLock<T> is Sync when T: Send+Sync.
201// Mutex<Option<F>> is Sync when Option<F>: Send, i.e. F: Send.
202unsafe impl<T: Send + Sync, F: Send> Sync for LazyLock<T, F> {}
203unsafe impl<T: Send, F: Send> Send for LazyLock<T, F> {}
204
205impl<T, F: FnOnce() -> T> LazyLock<T, F> {
206    /// Creates a new `LazyLock` with the given initialization function.
207    pub const fn new(f: F) -> Self {
208        LazyLock { once: OnceLock::new(), init: Mutex::new(Some(f)) }
209    }
210
211    /// Forces initialization and returns a reference to the value.
212    pub fn force(&self) -> &T {
213        self.once.get_or_init(|| {
214            let f = self
215                .init
216                .lock()
217                .expect("LazyLock init mutex poisoned")
218                .take()
219                .expect("LazyLock init function already taken");
220            f()
221        })
222    }
223}
224
225impl<T, F: FnOnce() -> T> core::ops::Deref for LazyLock<T, F> {
226    type Target = T;
227
228    fn deref(&self) -> &Self::Target {
229        self.force()
230    }
231}