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
// SPDX-License-Identifier: MIT
// Copyright 2023 IROX Contributors
//
//! Contains the [`SynchronizedOptional`] and other associated primitives
use alloc::sync::Arc;
use core::fmt::{Debug, Formatter};
use std::sync::RwLock;
///
/// Basically a [`RwLock<Option<Arc<T>>>`] - the benefits here being:
/// 1. This structure is [`Sync`] in that it can be shared between threads
/// 2. This structure can be lazily initialized, and then reset back to [`None`], and back again
/// 3. This structure provides multiple access to a single shared instance, like a [`String`] so we're
/// not cloning it a bunch of times unnecessarily. ([`Arc<T>`])
///
/// The [`Arc<T>`] bit is because you can't return a `&T` out of a [`RwLock<T>`] the way you can
/// from a [`std::sync::OnceLock<T>`]. The only reason this isn't a OnceLock is because I wanted
/// a `swap` method that was atomic, rather than relying on a [`std::sync::OnceLock<T>::take`]
/// followed by a [`std::sync::OnceLock<T>::set`], which allows a very slight race condition.
pub struct SynchronizedOptional<T> {
inner: RwLock<Option<Arc<T>>>,
}
impl<T> Default for SynchronizedOptional<T> {
fn default() -> Self {
SynchronizedOptional::empty()
}
}
impl<T> SynchronizedOptional<T> {
/// Returns a new uninitialized/empty struct
#[must_use]
pub fn empty() -> Self {
Self {
inner: RwLock::new(None),
}
}
/// Returns a new struct initialized with the provided value
#[must_use]
pub fn new(value: T) -> Self {
Self {
inner: RwLock::new(Some(Arc::new(value))),
}
}
/// Returns a new struct initialized with the provided already shared value
#[must_use]
pub fn new_shared(value: Arc<T>) -> Self {
Self {
inner: RwLock::new(Some(value)),
}
}
/// If this struct has been initialized, returns a copy of the data, otherwise None
#[must_use]
pub fn get(&self) -> Option<Arc<T>> {
if let Ok(read) = self.inner.read() {
return read.clone();
}
None
}
/// Sets the value to be the specified value, throwing away any value that was stored previously
/// Returns the value provided as a parameter if it was unable to replace the value.
pub fn set(&self, value: Option<T>) -> Result<(), Option<T>> {
if let Ok(mut write) = self.inner.write() {
*write = value.map(Arc::new);
return Ok(());
}
Err(value)
}
/// Sets the value to be the specified value, throwing away any value that was stored previously
/// Returns the value provided as a parameter if it was unable to replace the value.
pub fn set_shared(&self, value: Arc<T>) -> Result<(), Arc<T>> {
if let Ok(mut write) = self.inner.write() {
*write = Some(value);
return Ok(());
}
Err(value)
}
/// Takes the value out of this structure, leaving `None` in it's place.
pub fn take(&self) -> Option<Arc<T>> {
if let Ok(mut write) = self.inner.write() {
let out = write.clone();
*write = None;
return out;
}
None
}
/// Swaps the value contained within this structure (if any) with the value provided. Upon
/// success, returns the old value (which is possibly [`None`]).
/// Will only fail if the lock has been poisoned, at which point it returns the provided
/// value back to you.
pub fn swap(&self, value: T) -> Result<Option<Arc<T>>, T> {
if let Ok(mut write) = self.inner.write() {
let inner = write.clone();
*write = Some(Arc::new(value));
return Ok(inner);
}
Err(value)
}
/// Swaps the value contained within this structure (if any) with the already shared value
/// provided. Upon success, returns the old value (which is possibly [`None`]).
/// Will only fail if the lock has been poisoned, at which point it returns the provided
/// value back to you.
pub fn swap_shared(&self, value: Arc<T>) -> Result<Option<Arc<T>>, Arc<T>> {
if let Ok(mut write) = self.inner.write() {
let inner = write.clone();
*write = Some(value);
return Ok(inner);
}
Err(value)
}
}
impl<T> Debug for SynchronizedOptional<T>
where
T: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", self.get())
}
}