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())
    }
}