Struct arc_swap::ArcSwap [−][src]
pub struct ArcSwap<T> { /* fields omitted */ }An atomic storage for Arc.
This is a storage where an Arc may live. It can be read and written atomically from several
threads, but doesn't act like a pointer itself.
One can be created from an Arc. To get an Arc back, use the load
method.
Examples
let arc = Arc::new(42); let arc_swap = ArcSwap::from(arc); assert_eq!(42, *arc_swap.load()); // It can be read multiple times assert_eq!(42, *arc_swap.load()); // Put a new one in there let new_arc = Arc::new(0); assert_eq!(42, *arc_swap.swap(new_arc)); assert_eq!(0, *arc_swap.load());
Methods
impl<T> ArcSwap<T>[src]
impl<T> ArcSwap<T>pub fn load(&self) -> Arc<T>[src]
pub fn load(&self) -> Arc<T>Loads the value.
This makes another copy (reference) and returns it, atomically (it is safe even when other thread stores into the same instance at the same time).
The method is lock-free and wait-free.
pub fn store(&self, arc: Arc<T>)[src]
pub fn store(&self, arc: Arc<T>)Replaces the value inside this instance.
Further loads will yield the new value. Uses swap internally.
pub fn swap(&self, arc: Arc<T>) -> Arc<T>[src]
pub fn swap(&self, arc: Arc<T>) -> Arc<T>Exchanges the value inside this instance.
While multiple swaps can run concurrently and won't block each other, each one needs to
wait for all the loads that have seen the old value to finish before
returning.
pub fn compare_and_swap(&self, current: Arc<T>, new: Arc<T>) -> (bool, Arc<T>)[src]
pub fn compare_and_swap(&self, current: Arc<T>, new: Arc<T>) -> (bool, Arc<T>)Swaps the stored Arc if it is equal to current.
If the current value of the ArcSwap is equal to current, the new is stored inside. If
not, nothing happens.
True is returned as the first part of result if it did swap content, false otherwise.
Either way, the previous content is returned as the second part. The property of standard
library atomics that if previous is the same as current, the swap happened, is still true,
but unlike the values in the atomics, Arc is not copy ‒ therefore, you may want to pass
the only instance of current into it and not clone it just to compare.
In other words, if the caller „guesses“ the value of current correctly, it acts like
swap, otherwise it acts like load (including the
limitations).
pub fn rcu<R, F>(&self, f: F) -> Arc<T> where
F: FnMut(&Arc<T>) -> R,
R: Into<Arc<T>>, [src]
pub fn rcu<R, F>(&self, f: F) -> Arc<T> where
F: FnMut(&Arc<T>) -> R,
R: Into<Arc<T>>, Read-Copy-Update of the pointer inside.
This is useful in read-heavy situations with several threads that sometimes update the data
pointed to. The readers can just repeatedly use load without any locking.
The writer uses this method to perform the update.
In case there's only one thread that does updates or in case the next version is
independent of the previous onse, simple swap or store
is enough. Otherwise, it may be needed to retry the update operation if some other thread
made an update in between. This is what this method does.
Examples
This will not work as expected, because between loading and storing, some other thread might have updated the value.
extern crate arc_swap; extern crate crossbeam_utils; use std::sync::Arc; use arc_swap::ArcSwap; use crossbeam_utils::scoped as thread; fn main() { let cnt = ArcSwap::from(Arc::new(0)); thread::scope(|scope| { for _ in 0..10 { scope.spawn(|| { let inner = cnt.load(); // Another thread might have stored some other number than what we have // between the load and store. cnt.store(Arc::new(*inner + 1)); }); } }); // This will likely fail: // assert_eq!(10, *cnt.load()); }
This will, but it can call the closure multiple times to do retries:
extern crate arc_swap; extern crate crossbeam_utils; use std::sync::Arc; use arc_swap::ArcSwap; use crossbeam_utils::scoped as thread; fn main() { let cnt = ArcSwap::from(Arc::new(0)); thread::scope(|scope| { for _ in 0..10 { scope.spawn(|| cnt.rcu(|inner| **inner + 1)); } }); assert_eq!(10, *cnt.load()); }
Due to the retries, you might want to perform all the expensive operations before the rcu. As an example, if there's a cache of some computations as a map, and the map is cheap to clone but the computations are not, you could do something like this:
extern crate arc_swap; extern crate crossbeam_utils; #[macro_use] extern crate lazy_static; use std::collections::HashMap; use std::sync::Arc; use arc_swap::ArcSwap; use crossbeam_utils::scoped as thread; fn expensive_computation(x: usize) -> usize { x * 2 // Let's pretend multiplication is really expensive } type Cache = HashMap<usize, usize>; lazy_static! { static ref CACHE: ArcSwap<Cache> = ArcSwap::from(Arc::new(HashMap::new())); } fn cached_computation(x: usize) -> usize { let cache = CACHE.load(); if let Some(result) = cache.get(&x) { return *result; } // Not in cache. Compute and store. // The expensive computation goes outside, so it is not retried. let result = expensive_computation(x); CACHE.rcu(|cache| { // The cheaper clone of the cache can be retried if need be. let mut cache = HashMap::clone(cache); cache.insert(x, result); cache }); result } fn main() { assert_eq!(42, cached_computation(21)); assert_eq!(42, cached_computation(21)); }
The cost of cloning
Depending on the size of cache above, the cloning might not be as cheap. You can however
use persistent data structures ‒ each modification creates a new data structure, but it
shares most of the data with the old one. Something like
rpds or im might do
what you need.
pub fn rcu_unwrap<R, F>(&self, f: F) -> T where
F: FnMut(&T) -> R,
R: Into<Arc<T>>, [src]
pub fn rcu_unwrap<R, F>(&self, f: F) -> T where
F: FnMut(&T) -> R,
R: Into<Arc<T>>, An rcu which waits to be the sole owner of the original value and unwraps
it.
This one works the same way as the rcu method, but works on the inner type
instead of Arc. After replacing the original, it waits until there are no other owners of
the arc and unwraps it.
One use case is around signal handlers. The signal handler loads the pointer, but it must
not be the last one to drop it. Therefore, this methods make sure there are no signal
handlers owning it at the time of deconstruction of the Arc.
Another use case might be an RCU with a structure that is rather slow to drop ‒ if it was left to random reader (the last one to hold the old value), it could cause a timeout or jitter in a query time. With this, the deallocation is done in the updater thread, therefore outside of a hot path.
Warning
Note that if you store a copy of the Arc somewhere except the ArcSwap itself for
extended period of time, this'll busy-wait the whole time. Unless you need the assurance
the Arc is deconstructed here, prefer rcu.
Trait Implementations
impl<T> From<Arc<T>> for ArcSwap<T>[src]
impl<T> From<Arc<T>> for ArcSwap<T>impl<T> Drop for ArcSwap<T>[src]
impl<T> Drop for ArcSwap<T>impl<T> Clone for ArcSwap<T>[src]
impl<T> Clone for ArcSwap<T>fn clone(&self) -> Self[src]
fn clone(&self) -> SelfReturns a copy of the value. Read more
fn clone_from(&mut self, source: &Self)1.0.0[src]
fn clone_from(&mut self, source: &Self)Performs copy-assignment from source. Read more
impl<T: Debug> Debug for ArcSwap<T>[src]
impl<T: Debug> Debug for ArcSwap<T>fn fmt(&self, formatter: &mut Formatter) -> FmtResult[src]
fn fmt(&self, formatter: &mut Formatter) -> FmtResultFormats the value using the given formatter. Read more
impl<T: Display> Display for ArcSwap<T>[src]
impl<T: Display> Display for ArcSwap<T>