Skip to main content

irox_tools/sync/
optional.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5//! Contains the [`SynchronizedOptional`], [`SharedCell`] and other associated primitives
6
7extern crate alloc;
8use alloc::sync::Arc;
9use core::fmt::{Debug, Formatter};
10use core::ops::Deref;
11use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
12
13///
14/// Basically a [`RwLock<Option<Arc<T>>>`] - the benefits here being:
15/// 1. This structure is [`Sync`] in that it can be shared between threads
16/// 2. This structure can be lazily initialized, and then reset back to [`None`], and back again
17/// 3. This structure provides multiple access to a single shared instance, like a [`String`] so we're
18///    not cloning it a bunch of times unnecessarily.  ([`Arc<T>`])
19///
20/// The [`Arc<T>`] bit is because you can't return a `&T` out of a [`RwLock<T>`] the way you can
21/// from a [`std::sync::OnceLock<T>`].  The only reason this isn't a OnceLock is because I wanted
22/// a `swap` method that was atomic, rather than relying on a [`std::sync::OnceLock<T>::take`]
23/// followed by a [`std::sync::OnceLock<T>::set`], which allows a very slight race condition.
24pub struct SynchronizedOptional<T> {
25    inner: RwLock<Option<Arc<T>>>,
26}
27
28impl<T> Default for SynchronizedOptional<T> {
29    fn default() -> Self {
30        SynchronizedOptional::empty()
31    }
32}
33
34impl<T> SynchronizedOptional<T> {
35    /// Returns a new uninitialized/empty struct
36    #[must_use]
37    pub fn empty() -> Self {
38        Self {
39            inner: RwLock::new(None),
40        }
41    }
42
43    /// Returns a new struct initialized with the provided value
44    #[must_use]
45    pub fn new(value: T) -> Self {
46        Self {
47            inner: RwLock::new(Some(Arc::new(value))),
48        }
49    }
50
51    /// Returns a new struct initialized with the provided already shared value
52    #[must_use]
53    pub fn new_shared(value: Arc<T>) -> Self {
54        Self {
55            inner: RwLock::new(Some(value)),
56        }
57    }
58
59    /// If this struct has been initialized, returns a copy of the data, otherwise None
60    #[must_use]
61    pub fn get(&self) -> Option<Arc<T>> {
62        if let Ok(read) = self.inner.read() {
63            return read.clone();
64        }
65        None
66    }
67
68    /// Sets the value to be the specified value, throwing away any value that was stored previously
69    /// Returns the value provided as a parameter if it was unable to replace the value.
70    pub fn set(&self, value: Option<T>) -> Result<(), Option<T>> {
71        if let Ok(mut write) = self.inner.write() {
72            *write = value.map(Arc::new);
73            return Ok(());
74        }
75        Err(value)
76    }
77
78    /// Sets the value to be the specified value, throwing away any value that was stored previously
79    /// Returns the value provided as a parameter if it was unable to replace the value.
80    pub fn set_shared(&self, value: Arc<T>) -> Result<(), Arc<T>> {
81        if let Ok(mut write) = self.inner.write() {
82            *write = Some(value);
83            return Ok(());
84        }
85
86        Err(value)
87    }
88
89    /// Takes the value out of this structure, leaving `None` in it's place.
90    pub fn take(&self) -> Option<Arc<T>> {
91        if let Ok(mut write) = self.inner.write() {
92            let out = write.clone();
93            *write = None;
94            return out;
95        }
96        None
97    }
98
99    /// Swaps the value contained within this structure (if any) with the value provided.  Upon
100    /// success, returns the old value (which is possibly [`None`]).
101    /// Will only fail if the lock has been poisoned, at which point it returns the provided
102    /// value back to you.
103    pub fn swap(&self, value: T) -> Result<Option<Arc<T>>, T> {
104        if let Ok(mut write) = self.inner.write() {
105            let inner = write.clone();
106            *write = Some(Arc::new(value));
107            return Ok(inner);
108        }
109        Err(value)
110    }
111
112    /// Swaps the value contained within this structure (if any) with the already shared value
113    /// provided.  Upon success, returns the old value (which is possibly [`None`]).
114    /// Will only fail if the lock has been poisoned, at which point it returns the provided
115    /// value back to you.
116    pub fn swap_shared(&self, value: Arc<T>) -> Result<Option<Arc<T>>, Arc<T>> {
117        if let Ok(mut write) = self.inner.write() {
118            let inner = write.clone();
119            *write = Some(value);
120            return Ok(inner);
121        }
122        Err(value)
123    }
124}
125
126impl<T> Debug for SynchronizedOptional<T>
127where
128    T: Debug,
129{
130    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
131        write!(f, "{:?}", self.get())
132    }
133}
134
135///
136/// Atomic shared version of `OnceLock` - backed by an `Arc<RwLock<Option<T>>>`.
137///
138/// ```
139/// # use irox_tools::sync::SharedCell;
140/// let value = SharedCell::<bool>::empty(); // or default()
141/// assert_eq!(None, value.take());
142///
143/// value.set(true);
144/// assert_eq!(Some(true), value.take());
145/// assert_eq!(None, value.take());
146/// ```
147#[derive(Debug)]
148pub struct SharedCell<T> {
149    inner: Arc<RwLock<Option<T>>>,
150}
151impl<T> Clone for SharedCell<T> {
152    fn clone(&self) -> Self {
153        SharedCell {
154            inner: self.inner.clone(),
155        }
156    }
157}
158impl<T> Default for SharedCell<T> {
159    fn default() -> Self {
160        SharedCell {
161            inner: Arc::new(RwLock::new(None)),
162        }
163    }
164}
165impl<T> From<Option<T>> for SharedCell<T> {
166    fn from(value: Option<T>) -> Self {
167        SharedCell {
168            inner: Arc::new(RwLock::new(value)),
169        }
170    }
171}
172impl<T> SharedCell<T> {
173    /// Creates a new, unset cell.
174    pub fn empty() -> Self {
175        None.into()
176    }
177    /// Create a new, set cell with the provided value.
178    pub fn new(val: T) -> Self {
179        Some(val).into()
180    }
181    /// Sets the value, throwing away any old value
182    pub fn set(&self, val: T) {
183        if let Ok(mut lock) = self.inner.write() {
184            *lock = Some(val)
185        }
186    }
187    /// Takes the value, if previously set.
188    pub fn take(&self) -> Option<T> {
189        if let Ok(mut lock) = self.inner.write() {
190            return lock.take();
191        }
192        None
193    }
194    /// Peeks at the value, does not consume it.
195    pub fn peek<V: Fn(Option<&T>)>(&self, func: V) {
196        if let Ok(lock) = self.inner.read() {
197            func(lock.as_ref())
198        }
199    }
200
201    ///
202    /// Returns a shared (locked) reference to the inner object
203    pub fn as_ref(&self) -> ReadGuard<'_, T> {
204        if let Ok(lock) = self.inner.read() {
205            return Some(lock).into();
206        }
207        None.into()
208    }
209
210    /// Mutably peeks at the value, allows for edits.
211    pub fn peek_mut<V: FnMut(Option<&mut T>)>(&self, mut func: V) {
212        if let Ok(mut lock) = self.inner.write() {
213            func(lock.as_mut())
214        }
215    }
216    ///
217    /// Returns a mutable (locked) reference to the inner object.
218    pub fn as_mut(&self) -> WriteGuard<'_,T> {
219        if let Ok(lock) = self.inner.write() {
220            return Some(lock).into();
221        }
222        None.into()
223    }
224}
225/// Type-safe wrapper around a [`RwLockReadGuard`]
226#[derive(Default)]
227pub struct ReadGuard<'a, T> {
228    lock: Option<RwLockReadGuard<'a, Option<T>>>,
229}
230impl<'a, T> From<Option<RwLockReadGuard<'a, Option<T>>>> for ReadGuard<'a, T> {
231    fn from(value: Option<RwLockReadGuard<'a, Option<T>>>) -> Self {
232        ReadGuard { lock: value }
233    }
234}
235impl<T> Deref for ReadGuard<'_, T> {
236    type Target = Option<T>;
237
238    fn deref(&self) -> &Self::Target {
239        if let Some(lock) = &self.lock {
240            return lock.deref();
241        }
242        &None
243    }
244}
245
246/// Type-safe wrapper around a [`RwLockWriteGuard`]
247#[derive(Default)]
248pub struct WriteGuard<'a, T> {
249    lock: Option<RwLockWriteGuard<'a, Option<T>>>,
250}
251impl<T> Deref for WriteGuard<'_, T> {
252    type Target = Option<T>;
253
254    fn deref(&self) -> &Self::Target {
255        if let Some(lock) = &self.lock {
256            return lock.deref();
257        }
258        &None
259    }
260}
261impl<'a, T> From<Option<RwLockWriteGuard<'a, Option<T>>>> for WriteGuard<'a, T> {
262    fn from(value: Option<RwLockWriteGuard<'a, Option<T>>>) -> Self {
263        WriteGuard { lock: value }
264    }
265}