Skip to main content

litellm_rs/utils/sync/
atomic_value.rs

1//! Atomic value container using arc-swap
2//!
3//! Provides a concurrent-safe single value container with atomic swap semantics.
4//! Ideal for configuration values or shared state that needs to be updated atomically.
5
6use arc_swap::ArcSwap;
7use std::sync::Arc;
8
9/// A concurrent-safe single value container using arc-swap.
10///
11/// This container provides atomic load and store operations for a single value.
12/// It's ideal for configuration values or shared state that needs to be read
13/// frequently and updated occasionally.
14///
15/// # Type Parameters
16///
17/// * `T` - The value type
18///
19/// # Example
20///
21/// ```rust
22/// use litellm_rs::utils::sync::AtomicValue;
23///
24/// let value: AtomicValue<String> = AtomicValue::new("initial".to_string());
25/// assert_eq!(value.load().as_ref(), "initial");
26///
27/// value.store("updated".to_string());
28/// assert_eq!(value.load().as_ref(), "updated");
29/// ```
30#[derive(Debug)]
31pub struct AtomicValue<T> {
32    inner: ArcSwap<T>,
33}
34
35impl<T> AtomicValue<T> {
36    /// Creates a new `AtomicValue` with the specified initial value.
37    ///
38    /// # Arguments
39    ///
40    /// * `value` - The initial value
41    ///
42    /// # Example
43    ///
44    /// ```rust
45    /// use litellm_rs::utils::sync::AtomicValue;
46    ///
47    /// let value: AtomicValue<i32> = AtomicValue::new(42);
48    /// ```
49    pub fn new(value: T) -> Self {
50        Self {
51            inner: ArcSwap::from_pointee(value),
52        }
53    }
54
55    /// Loads the current value.
56    ///
57    /// Returns an `Arc` pointing to the current value. This is a very fast
58    /// operation and does not block other readers or writers.
59    ///
60    /// # Returns
61    ///
62    /// An `Arc<T>` containing the current value.
63    ///
64    /// # Example
65    ///
66    /// ```rust
67    /// use litellm_rs::utils::sync::AtomicValue;
68    ///
69    /// let value: AtomicValue<i32> = AtomicValue::new(42);
70    /// let current = value.load();
71    /// assert_eq!(*current, 42);
72    /// ```
73    pub fn load(&self) -> Arc<T> {
74        self.inner.load_full()
75    }
76
77    /// Stores a new value, replacing the current one.
78    ///
79    /// This operation is atomic and will not block readers.
80    ///
81    /// # Arguments
82    ///
83    /// * `value` - The new value to store
84    ///
85    /// # Example
86    ///
87    /// ```rust
88    /// use litellm_rs::utils::sync::AtomicValue;
89    ///
90    /// let value: AtomicValue<i32> = AtomicValue::new(42);
91    /// value.store(100);
92    /// assert_eq!(*value.load(), 100);
93    /// ```
94    pub fn store(&self, value: T) {
95        self.inner.store(Arc::new(value));
96    }
97
98    /// Swaps the current value with a new one, returning the old value.
99    ///
100    /// # Arguments
101    ///
102    /// * `value` - The new value to store
103    ///
104    /// # Returns
105    ///
106    /// An `Arc<T>` containing the previous value.
107    ///
108    /// # Example
109    ///
110    /// ```rust
111    /// use litellm_rs::utils::sync::AtomicValue;
112    ///
113    /// let value: AtomicValue<i32> = AtomicValue::new(42);
114    /// let old = value.swap(100);
115    /// assert_eq!(*old, 42);
116    /// assert_eq!(*value.load(), 100);
117    /// ```
118    pub fn swap(&self, value: T) -> Arc<T> {
119        self.inner.swap(Arc::new(value))
120    }
121
122    /// Stores a new value from an Arc.
123    ///
124    /// This is useful when you already have an Arc and want to avoid
125    /// an extra allocation.
126    ///
127    /// # Arguments
128    ///
129    /// * `value` - An Arc containing the new value
130    ///
131    /// # Example
132    ///
133    /// ```rust
134    /// use litellm_rs::utils::sync::AtomicValue;
135    /// use std::sync::Arc;
136    ///
137    /// let value: AtomicValue<i32> = AtomicValue::new(42);
138    /// value.store_arc(Arc::new(100));
139    /// assert_eq!(*value.load(), 100);
140    /// ```
141    pub fn store_arc(&self, value: Arc<T>) {
142        self.inner.store(value);
143    }
144
145    /// Swaps the current value with a new Arc, returning the old value.
146    ///
147    /// # Arguments
148    ///
149    /// * `value` - An Arc containing the new value
150    ///
151    /// # Returns
152    ///
153    /// An `Arc<T>` containing the previous value.
154    ///
155    /// # Example
156    ///
157    /// ```rust
158    /// use litellm_rs::utils::sync::AtomicValue;
159    /// use std::sync::Arc;
160    ///
161    /// let value: AtomicValue<i32> = AtomicValue::new(42);
162    /// let old = value.swap_arc(Arc::new(100));
163    /// assert_eq!(*old, 42);
164    /// ```
165    pub fn swap_arc(&self, value: Arc<T>) -> Arc<T> {
166        self.inner.swap(value)
167    }
168}
169
170impl<T: Clone> AtomicValue<T> {
171    /// Updates the value using a closure, with atomic compare-and-swap retry.
172    ///
173    /// This operation uses `ArcSwap::rcu()` to perform a read-copy-update loop,
174    /// retrying until the compare-and-swap succeeds. The closure may be called
175    /// more than once if concurrent updates occur, so it must be side-effect-free
176    /// (i.e., `Fn` rather than `FnOnce`).
177    ///
178    /// # Arguments
179    ///
180    /// * `f` - A closure that takes a reference to the current value and returns the new value
181    ///
182    /// # Returns
183    ///
184    /// The new value after the update.
185    ///
186    /// # Example
187    ///
188    /// ```rust
189    /// use litellm_rs::utils::sync::AtomicValue;
190    ///
191    /// let value: AtomicValue<i32> = AtomicValue::new(42);
192    /// let new_value = value.update(|v| v + 1);
193    /// assert_eq!(new_value, 43);
194    /// ```
195    pub fn update<F>(&self, f: F) -> T
196    where
197        F: Fn(&T) -> T,
198    {
199        use std::cell::Cell;
200        // `rcu` retries the closure until a compare-and-swap succeeds, so the
201        // closure may be called more than once.  The last invocation is always
202        // the one whose result was actually stored, so we capture that Arc and
203        // return a clone of it.
204        let last_new: Cell<Option<Arc<T>>> = Cell::new(None);
205        self.inner.rcu(|current| {
206            let new_arc = Arc::new(f(current.as_ref()));
207            last_new.set(Some(Arc::clone(&new_arc)));
208            new_arc
209        });
210        match last_new.into_inner() {
211            Some(arc) => (*arc).clone(),
212            None => unreachable!("rcu always invokes the closure at least once"),
213        }
214    }
215
216    /// Gets a clone of the current value.
217    ///
218    /// This is a convenience method that loads the value and clones it.
219    ///
220    /// # Returns
221    ///
222    /// A clone of the current value.
223    ///
224    /// # Example
225    ///
226    /// ```rust
227    /// use litellm_rs::utils::sync::AtomicValue;
228    ///
229    /// let value: AtomicValue<String> = AtomicValue::new("hello".to_string());
230    /// let cloned: String = value.get();
231    /// assert_eq!(cloned, "hello");
232    /// ```
233    pub fn get(&self) -> T {
234        (*self.load()).clone()
235    }
236}
237
238impl<T: Default> Default for AtomicValue<T> {
239    fn default() -> Self {
240        Self::new(T::default())
241    }
242}
243
244impl<T> Clone for AtomicValue<T> {
245    /// Creates a new `AtomicValue` pointing to the same underlying data.
246    ///
247    /// Note: This creates a new `AtomicValue` that shares the same `Arc`,
248    /// but updates to one will not affect the other after the clone.
249    fn clone(&self) -> Self {
250        Self {
251            inner: ArcSwap::new(self.inner.load_full()),
252        }
253    }
254}
255
256impl<T> From<T> for AtomicValue<T> {
257    fn from(value: T) -> Self {
258        Self::new(value)
259    }
260}
261
262impl<T> From<Arc<T>> for AtomicValue<T> {
263    fn from(value: Arc<T>) -> Self {
264        Self {
265            inner: ArcSwap::new(value),
266        }
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use super::*;
273    use std::thread;
274
275    #[test]
276    fn test_new_and_load() {
277        let value: AtomicValue<i32> = AtomicValue::new(42);
278        assert_eq!(*value.load(), 42);
279    }
280
281    #[test]
282    fn test_store() {
283        let value: AtomicValue<i32> = AtomicValue::new(42);
284        value.store(100);
285        assert_eq!(*value.load(), 100);
286    }
287
288    #[test]
289    fn test_swap() {
290        let value: AtomicValue<i32> = AtomicValue::new(42);
291        let old = value.swap(100);
292        assert_eq!(*old, 42);
293        assert_eq!(*value.load(), 100);
294    }
295
296    #[test]
297    fn test_store_arc() {
298        let value: AtomicValue<i32> = AtomicValue::new(42);
299        value.store_arc(Arc::new(100));
300        assert_eq!(*value.load(), 100);
301    }
302
303    #[test]
304    fn test_swap_arc() {
305        let value: AtomicValue<i32> = AtomicValue::new(42);
306        let old = value.swap_arc(Arc::new(100));
307        assert_eq!(*old, 42);
308        assert_eq!(*value.load(), 100);
309    }
310
311    #[test]
312    fn test_update() {
313        let value: AtomicValue<i32> = AtomicValue::new(42);
314        let new_value = value.update(|v| v + 1);
315        assert_eq!(new_value, 43);
316        assert_eq!(*value.load(), 43);
317    }
318
319    #[test]
320    fn test_get() {
321        let value: AtomicValue<String> = AtomicValue::new("hello".to_string());
322        let cloned: String = value.get();
323        assert_eq!(cloned, "hello");
324    }
325
326    #[test]
327    fn test_default() {
328        let value: AtomicValue<i32> = AtomicValue::default();
329        assert_eq!(*value.load(), 0);
330
331        let value: AtomicValue<String> = AtomicValue::default();
332        assert_eq!(*value.load(), "");
333    }
334
335    #[test]
336    fn test_clone() {
337        let value1: AtomicValue<i32> = AtomicValue::new(42);
338        let value2 = value1.clone();
339
340        // Both start with the same value
341        assert_eq!(*value1.load(), 42);
342        assert_eq!(*value2.load(), 42);
343
344        // Updates to one don't affect the other
345        value1.store(100);
346        assert_eq!(*value1.load(), 100);
347        assert_eq!(*value2.load(), 42);
348    }
349
350    #[test]
351    fn test_from_value() {
352        let value: AtomicValue<i32> = AtomicValue::from(42);
353        assert_eq!(*value.load(), 42);
354    }
355
356    #[test]
357    fn test_from_arc() {
358        let arc = Arc::new(42);
359        let value: AtomicValue<i32> = AtomicValue::from(arc);
360        assert_eq!(*value.load(), 42);
361    }
362
363    #[test]
364    fn test_with_string() {
365        let value: AtomicValue<String> = AtomicValue::new("initial".to_string());
366        assert_eq!(value.load().as_ref(), "initial");
367
368        value.store("updated".to_string());
369        assert_eq!(value.load().as_ref(), "updated");
370    }
371
372    #[test]
373    fn test_with_struct() {
374        #[derive(Debug, Clone, PartialEq)]
375        struct Config {
376            host: String,
377            port: u16,
378        }
379
380        let config = Config {
381            host: "localhost".to_string(),
382            port: 8080,
383        };
384
385        let value: AtomicValue<Config> = AtomicValue::new(config);
386        assert_eq!(value.load().host, "localhost");
387        assert_eq!(value.load().port, 8080);
388
389        value.store(Config {
390            host: "0.0.0.0".to_string(),
391            port: 9090,
392        });
393        assert_eq!(value.load().host, "0.0.0.0");
394        assert_eq!(value.load().port, 9090);
395    }
396
397    #[test]
398    fn test_concurrent_reads() {
399        let value: Arc<AtomicValue<i32>> = Arc::new(AtomicValue::new(42));
400        let handles: Vec<_> = (0..10)
401            .map(|_| {
402                let value = Arc::clone(&value);
403                thread::spawn(move || {
404                    for _ in 0..1000 {
405                        let _ = value.load();
406                    }
407                })
408            })
409            .collect();
410
411        for handle in handles {
412            handle.join().unwrap();
413        }
414
415        assert_eq!(*value.load(), 42);
416    }
417
418    #[test]
419    fn test_concurrent_writes() {
420        let value: Arc<AtomicValue<i32>> = Arc::new(AtomicValue::new(0));
421        let handles: Vec<_> = (0..10)
422            .map(|i| {
423                let value = Arc::clone(&value);
424                thread::spawn(move || {
425                    for _ in 0..100 {
426                        value.store(i);
427                    }
428                })
429            })
430            .collect();
431
432        for handle in handles {
433            handle.join().unwrap();
434        }
435
436        // Value should be one of the written values (0-9)
437        let final_value = *value.load();
438        assert!((0..10).contains(&final_value));
439    }
440
441    #[test]
442    fn test_concurrent_updates() {
443        let value: Arc<AtomicValue<i32>> = Arc::new(AtomicValue::new(0));
444        let handles: Vec<_> = (0..10)
445            .map(|_| {
446                let value = Arc::clone(&value);
447                thread::spawn(move || {
448                    for _ in 0..100 {
449                        value.update(|v| v + 1);
450                    }
451                })
452            })
453            .collect();
454
455        for handle in handles {
456            handle.join().unwrap();
457        }
458
459        // update() uses rcu() for atomic compare-and-swap retry, so all 1000
460        // increments must be visible — no updates are lost under concurrency.
461        let final_value = *value.load();
462        assert_eq!(final_value, 1000);
463    }
464}