Struct arc_swap::ArcSwapAny
source · [−]pub struct ArcSwapAny<T: RefCnt, S: LockStorage = Global> { /* private fields */ }
Expand description
An atomic storage for a smart pointer like Arc
or Option<Arc>
.
This is a storage where a smart pointer 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 the pointer back, use the load
method. But to general access to the data, lease
may be more appropriate.
Note
This is the generic low-level implementation. This allows sharing the same code for storing
both Arc
and Option<Arc>
(and possibly other similar types).
In your code, you most probably want to interact with it through the
ArcSwap
and ArcSwapOption
aliases. However,
the methods they share are described here and are applicable to both of them. That’s why the
examples here use ArcSwap
‒ but they could as well be written with ArcSwapOption
or
ArcSwapAny
.
Type parameters
T
: The smart pointer to be kept inside. This crate provides implementation forArc<_>
andOption<Arc<_>>
. But third party could provide implementations of theRefCnt
trait and plug in others.S
: This describes where the generation lock is stored and how it works.
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());
Implementations
sourceimpl<T: RefCnt, S: LockStorage> ArcSwapAny<T, S>
impl<T: RefCnt, S: LockStorage> ArcSwapAny<T, S>
sourcepub fn into_inner(self) -> T
pub fn into_inner(self) -> T
Extracts the value inside.
sourcepub fn load(&self) -> T
pub fn load(&self) -> 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, but usually more expensive than
lease
.
Signal safety
The method is not async-signal-safe. Use peek_signal_safe
for that.
sourcepub fn peek(&self) -> Guard<'_, T, S>
pub fn peek(&self) -> Guard<'_, T, S>
Provides a peek inside the held value.
This returns a temporary borrow of the object currently held inside. This is slightly
faster than load
, but it is not suitable for holding onto for longer
periods of time.
If you discover later on that you need to hold onto it for longer, you can
Guard::upgrade
it.
Warning
This currently prevents the pointer inside from being replaced. Any swap
,
store
or rcu
will busy-loop while waiting for the proxy
object to be destroyed, even on unrelated objects. Therefore, this is suitable only for
things like reading a (reasonably small) configuration value, but not for eg. computations
on the held values.
If you want to do anything non-trivial, prefer lease
.
If you are not sure what is better, benchmarking is recommended.
Signal safety
For an async-signal-safe version, use peek_signal_safe
.
sourcepub fn peek_signal_safe(&self) -> Guard<'_, T, S>
pub fn peek_signal_safe(&self) -> Guard<'_, T, S>
An async-signal-safe version of peek
This method uses only restricted set of primitives to be async-signal-safe, at a slight performance hit in a contended scenario (signals should be rare, so it shouldn’t be a problem in practice).
As the returned guard prevents the value inside to be dropped, the value can be used during the signal handler. Unless it is upgraded (which is not recommended in a signal handler), there’s also no way the signal handler would have to drop the pointed to value.
The same performance warning about writer methods of peek
applies, so it
is recommended not to spend too much time holding the returned guard.
sourcepub fn lease(&self) -> Lease<T>
pub fn lease(&self) -> Lease<T>
Provides a temporary borrow of the object inside.
This returns a proxy object allowing access to the thing held inside and it is usually
as fast as peek
. However, there’s only limited amount of possible cheap
proxies in existence for each thread ‒ if more are created, it falls back to
load
internally.
This is therefore a good choice to use for eg. searching a data structure or juggling the pointers around a bit, but not as something to store in larger amounts. The rule of thumb is this is suited for local variables on stack, but not in structures.
sourcepub fn store(&self, val: T)
pub fn store(&self, val: T)
Replaces the value inside this instance.
Further loads will yield the new value. Uses swap
internally.
sourcepub fn swap(&self, new: T) -> T
pub fn swap(&self, new: T) -> T
Exchanges the value inside this instance.
While multiple swap
s can run concurrently and won’t block each other, each one needs to
wait for all the load
s and peek
Guards that have seen
the old value to finish before returning. This is in a way similar to locking ‒ a living
Guard
can prevent this from finishing. However, unlike RwLock
, a
steady stream of readers will not block writers and if each guard is held only for a short
period of time, writers will progress too.
However, it is also possible to cause a deadlock (eg. this is an example of broken code):
let shared = ArcSwap::from(Arc::new(42));
let guard = shared.peek();
// This will deadlock, because the guard is still active here and swap
// can't pull the value from under its feet.
shared.swap(Arc::new(0));
sourcepub fn compare_and_swap<C: AsRaw<T::Base>>(
&self,
current: C,
new: T
) -> Lease<T>
pub fn compare_and_swap<C: AsRaw<T::Base>>(
&self,
current: C,
new: T
) -> Lease<T>
Swaps the stored Arc if it is equal to current
.
If the current value of the ArcSwapAny
is equal to current
, the new
is stored inside.
If not, nothing happens.
The previous value (no matter if the swap happened or not) is returned. Therefore, if the
returned value is equal to current
, the swap happened. You want to do a pointer-based
comparison to determine it (like Arc::ptr_eq
).
In other words, if the caller „guesses“ the value of current correctly, it acts like
swap
, otherwise it acts like load
(including the
limitations).
The current
can be specified as &Arc
, Guard
,
&Lease
or as a raw pointer.
sourcepub fn rcu<R, F>(&self, f: F) -> T where
F: FnMut(&Lease<T>) -> R,
R: Into<T>,
pub fn rcu<R, F>(&self, f: F) -> T where
F: FnMut(&Lease<T>) -> R,
R: Into<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 one, 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::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));
});
}
}).unwrap();
// 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::thread;
fn main() {
let cnt = ArcSwap::from(Arc::new(0));
thread::scope(|scope| {
for _ in 0..10 {
scope.spawn(|_| cnt.rcu(|inner| **inner + 1));
}
}).unwrap();
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;
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 (which is usually accomplished by using Arc
s
inside to share the unchanged values). Something like
rpds
or im
might do
what you need.
sourceimpl<T, S: LockStorage> ArcSwapAny<Arc<T>, S>
impl<T, S: LockStorage> ArcSwapAny<Arc<T>, S>
sourcepub fn from_pointee(val: T) -> Self
pub fn from_pointee(val: T) -> Self
A convenience constructor directly from the pointed-to value.
Direct equivalent for ArcSwap::new(Arc::new(val))
.
sourcepub fn rcu_unwrap<R, F>(&self, f: F) -> T where
F: FnMut(&T) -> R,
R: Into<Arc<T>>,
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.
Possible 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 the 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
.
sourceimpl<T, S: LockStorage> ArcSwapAny<Option<Arc<T>>, S>
impl<T, S: LockStorage> ArcSwapAny<Option<Arc<T>>, S>
sourcepub fn from_pointee<V: Into<Option<T>>>(val: V) -> Self
pub fn from_pointee<V: Into<Option<T>>>(val: V) -> Self
A convenience constructor directly from a pointed-to value.
This just allocates the Arc
under the hood.
Examples
use arc_swap::ArcSwapOption;
let empty: ArcSwapOption<usize> = ArcSwapOption::from_pointee(None);
assert!(empty.load().is_none());
let non_empty: ArcSwapOption<usize> = ArcSwapOption::from_pointee(42);
assert_eq!(42, *non_empty.load().unwrap());
Trait Implementations
sourceimpl<T: RefCnt, S: LockStorage> Clone for ArcSwapAny<T, S>
impl<T: RefCnt, S: LockStorage> Clone for ArcSwapAny<T, S>
sourceimpl<T, S: LockStorage> Debug for ArcSwapAny<T, S> where
T: RefCnt,
T::Base: Debug,
impl<T, S: LockStorage> Debug for ArcSwapAny<T, S> where
T: RefCnt,
T::Base: Debug,
sourceimpl<T: RefCnt + Default, S: LockStorage> Default for ArcSwapAny<T, S>
impl<T: RefCnt + Default, S: LockStorage> Default for ArcSwapAny<T, S>
sourceimpl<T, S: LockStorage> Display for ArcSwapAny<T, S> where
T: NonNull,
T::Base: Display,
impl<T, S: LockStorage> Display for ArcSwapAny<T, S> where
T: NonNull,
T::Base: Display,
sourceimpl<T: RefCnt, S: LockStorage> Drop for ArcSwapAny<T, S>
impl<T: RefCnt, S: LockStorage> Drop for ArcSwapAny<T, S>
sourceimpl<T: RefCnt, S: LockStorage> From<T> for ArcSwapAny<T, S>
impl<T: RefCnt, S: LockStorage> From<T> for ArcSwapAny<T, S>
Auto Trait Implementations
impl<T, S> RefUnwindSafe for ArcSwapAny<T, S> where
S: RefUnwindSafe,
T: RefUnwindSafe,
impl<T, S> Send for ArcSwapAny<T, S> where
S: Send,
T: Send,
impl<T, S> Sync for ArcSwapAny<T, S> where
S: Sync,
T: Sync,
impl<T, S> Unpin for ArcSwapAny<T, S> where
S: Unpin,
T: Unpin,
impl<T, S> UnwindSafe for ArcSwapAny<T, S> where
S: UnwindSafe,
T: UnwindSafe,
<T as RefCnt>::Base: RefUnwindSafe,
Blanket Implementations
sourceimpl<T> BorrowMut<T> for T where
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
const: unstable · sourcepub fn borrow_mut(&mut self) -> &mut T
pub fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
sourceimpl<T> ToOwned for T where
T: Clone,
impl<T> ToOwned for T where
T: Clone,
type Owned = T
type Owned = T
The resulting type after obtaining ownership.
sourcepub fn to_owned(&self) -> T
pub fn to_owned(&self) -> T
Creates owned data from borrowed data, usually by cloning. Read more
sourcepub fn clone_into(&self, target: &mut T)
pub fn clone_into(&self, target: &mut T)
toowned_clone_into
)Uses borrowed data to replace owned data, usually by cloning. Read more