nstd_sys/mutex.rs
1//! A mutual exclusion primitive useful for protecting shared data.
2use crate::{
3 alloc::CBox,
4 core::{optional::NSTDOptional, result::NSTDResult},
5 heap_ptr::{
6 nstd_heap_ptr_drop, nstd_heap_ptr_get, nstd_heap_ptr_get_mut, NSTDHeapPtr,
7 NSTDOptionalHeapPtr,
8 },
9 NSTDAny, NSTDAnyMut, NSTDBool,
10};
11use nstdapi::nstdapi;
12use std::sync::{Mutex, MutexGuard, TryLockError};
13
14/// A mutual exclusion primitive useful for protecting shared data.
15#[nstdapi]
16pub struct NSTDMutex<'a> {
17 /// The Rust [Mutex].
18 mtx: CBox<Mutex<NSTDHeapPtr<'a>>>,
19}
20
21/// Represents an optional value of type `NSTDMutex`.
22pub type NSTDOptionalMutex<'a> = NSTDOptional<NSTDMutex<'a>>;
23
24/// A guard providing access to a mutex's protected data.
25#[nstdapi]
26pub struct NSTDMutexGuard<'m, 'a> {
27 /// The Rust [MutexGuard].
28 guard: CBox<MutexGuard<'m, NSTDHeapPtr<'a>>>,
29}
30
31/// A lock result returned from `nstd_mutex_lock` containing the mutex guard whether or not the
32/// data is poisoned.
33pub type NSTDMutexLockResult<'m, 'a> = NSTDResult<NSTDMutexGuard<'m, 'a>, NSTDMutexGuard<'m, 'a>>;
34
35/// An optional value of type `NSTDMutexLockResult`.
36///
37/// This type is returned from `nstd_mutex_try_lock` where the uninitialized variant means that the
38/// function would block.
39pub type NSTDOptionalMutexLockResult<'m, 'a> = NSTDOptional<NSTDMutexLockResult<'m, 'a>>;
40
41/// Creates a new mutual exclusion primitive.
42///
43/// # Parameters:
44///
45/// - `NSTDHeapPtr data` - The data to protect.
46///
47/// # Returns
48///
49/// `NSTDOptionalMutex mutex` - The new mutex protecting `data` on success, or an uninitialized
50/// "none" variant on error.
51#[inline]
52#[nstdapi]
53pub fn nstd_mutex_new(data: NSTDHeapPtr<'_>) -> NSTDOptionalMutex<'_> {
54 CBox::new(Mutex::new(data)).map_or(NSTDOptional::None, |mtx| {
55 NSTDOptional::Some(NSTDMutex { mtx })
56 })
57}
58
59/// Determines whether or not a mutex's data is poisoned.
60///
61/// Mutexes are poisoned when a thread that owns the mutex guard panics. This function is useful
62/// for those that configure `nstd` to unwind the stack instead of aborting on panic.
63///
64/// # Parameters:
65///
66/// - `const NSTDMutex *mutex` - The mutex.
67///
68/// # Returns
69///
70/// `NSTDBool is_poisoned` - A boolean value indicating whether or not `mutex` is poisoned.
71#[inline]
72#[nstdapi]
73pub fn nstd_mutex_is_poisoned(mutex: &NSTDMutex<'_>) -> NSTDBool {
74 mutex.mtx.is_poisoned()
75}
76
77/// Waits for a mutex lock to become acquired, returning a guard wrapping the protected data.
78///
79/// Attempting to call this function on a thread that already owns the lock will either result in a
80/// panic or a deadlock.
81///
82/// # Parameters:
83///
84/// - `const NSTDMutex *mutex` - The mutex to lock.
85///
86/// # Returns
87///
88/// `NSTDOptionalMutexLockResult guard` - A handle to the mutex's protected data on success, or an
89/// uninitialized "none" variant on error.
90///
91/// # Panics
92///
93/// This operation may panic if the lock is already held by the current thread.
94#[nstdapi]
95pub fn nstd_mutex_lock<'m, 'a>(mutex: &'m NSTDMutex<'a>) -> NSTDOptionalMutexLockResult<'m, 'a> {
96 match mutex.mtx.lock() {
97 Ok(guard) => CBox::new(guard).map_or(NSTDOptional::None, |guard| {
98 NSTDOptional::Some(NSTDResult::Ok(NSTDMutexGuard { guard }))
99 }),
100 Err(err) => CBox::new(err.into_inner()).map_or(NSTDOptional::None, |guard| {
101 NSTDOptional::Some(NSTDResult::Err(NSTDMutexGuard { guard }))
102 }),
103 }
104}
105
106/// The non-blocking variant of `nstd_mutex_lock` returning an uninitialized "none" result if the
107/// mutex is locked by another thread.
108///
109/// # Parameters:
110///
111/// - `const NSTDMutex *mutex` - The mutex to lock.
112///
113/// # Returns
114///
115/// `NSTDOptionalMutexLockResult guard` - A handle to the mutex's protected data.
116#[nstdapi]
117pub fn nstd_mutex_try_lock<'m, 'a>(
118 mutex: &'m NSTDMutex<'a>,
119) -> NSTDOptionalMutexLockResult<'m, 'a> {
120 match mutex.mtx.try_lock() {
121 Ok(guard) => CBox::new(guard).map_or(NSTDOptional::None, |guard| {
122 NSTDOptional::Some(NSTDResult::Ok(NSTDMutexGuard { guard }))
123 }),
124 Err(err) => match err {
125 TryLockError::WouldBlock => NSTDOptional::None,
126 TryLockError::Poisoned(err) => CBox::new(err.into_inner())
127 .map_or(NSTDOptional::None, |guard| {
128 NSTDOptional::Some(NSTDResult::Err(NSTDMutexGuard { guard }))
129 }),
130 },
131 }
132}
133
134/// Returns a pointer to a mutex's raw data.
135///
136/// # Parameters:
137///
138/// - `const NSTDMutexGuard *guard` - A handle to the mutex's protected data.
139///
140/// # Returns
141///
142/// `NSTDAny data` - A pointer to the mutex's data.
143#[inline]
144#[nstdapi]
145pub fn nstd_mutex_get(guard: &NSTDMutexGuard<'_, '_>) -> NSTDAny {
146 nstd_heap_ptr_get(&guard.guard)
147}
148
149/// Returns a mutable pointer to a mutex's raw data.
150///
151/// # Parameters:
152///
153/// - `NSTDMutexGuard *guard` - A handle to the mutex's protected data.
154///
155/// # Returns
156///
157/// `NSTDAnyMut data` - A mutable pointer to the mutex's data.
158#[inline]
159#[nstdapi]
160pub fn nstd_mutex_get_mut(guard: &mut NSTDMutexGuard<'_, '_>) -> NSTDAnyMut {
161 nstd_heap_ptr_get_mut(&mut guard.guard)
162}
163
164/// Consumes a mutex and returns the data it was protecting.
165///
166/// # Parameters:
167///
168/// - `NSTDMutex mutex` - The mutex to take ownership of.
169///
170/// # Returns
171///
172/// `NSTDOptionalHeapPtr data` - Ownership of the mutex's data, or an uninitialized "none" variant
173/// if the mutex was poisoned.
174#[inline]
175#[nstdapi]
176pub fn nstd_mutex_into_inner(mutex: NSTDMutex<'_>) -> NSTDOptionalHeapPtr<'_> {
177 mutex
178 .mtx
179 .into_inner()
180 .into_inner()
181 .map_or(NSTDOptional::None, NSTDOptional::Some)
182}
183
184/// Unlocks a mutex by consuming a mutex guard.
185///
186/// # Parameters:
187///
188/// - `NSTDMutexGuard guard` - The mutex guard.
189#[inline]
190#[nstdapi]
191#[allow(
192 unused_variables,
193 clippy::missing_const_for_fn,
194 clippy::needless_pass_by_value
195)]
196pub fn nstd_mutex_unlock(guard: NSTDMutexGuard<'_, '_>) {}
197
198/// Frees an instance of `NSTDMutex`.
199///
200/// # Parameters:
201///
202/// - `NSTDMutex mutex` - The mutex to free.
203#[inline]
204#[nstdapi]
205#[allow(
206 unused_variables,
207 clippy::missing_const_for_fn,
208 clippy::needless_pass_by_value
209)]
210pub fn nstd_mutex_free(mutex: NSTDMutex<'_>) {}
211
212/// Frees an instance of `NSTDMutex` after invoking `callback` with the mutex's data.
213///
214/// `callback` will not be called if the mutex is poisoned.
215///
216/// # Parameters:
217///
218/// - `NSTDMutex mutex` - The mutex to free.
219///
220/// - `void (*callback)(NSTDAnyMut)` - The mutex data's destructor.
221///
222/// # Safety
223///
224/// This operation makes a direct call on a C function pointer (`callback`).
225#[inline]
226#[nstdapi]
227pub unsafe fn nstd_mutex_drop(mutex: NSTDMutex<'_>, callback: unsafe extern "C" fn(NSTDAnyMut)) {
228 if let Ok(data) = mutex.mtx.into_inner().into_inner() {
229 nstd_heap_ptr_drop(data, callback);
230 }
231}