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}