Struct arc_swap::ArcSwapAny[][src]

pub struct ArcSwapAny<T: RefCnt> { /* fields omitted */ }

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.

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.

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: RefCnt> ArcSwapAny<T>
[src]

Constructs a new value.

Extracts the value inside.

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.

Signal safety

The method is not async-signal-safe. Use peek_signal_safe for that.

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.

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 applies, so it is recommended not to spend too much time holding the returned guard.

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 an uncontented load (loads gets slower when multiple threads access the same value at the same time). Unlike the peek, there's no performance penalty to holding onto the object for arbitrary time span. On the other hand, this gets slower with the number of existing leases in the current thread and at some point it falls back to doing full loads under the hood.

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.

Replaces the value inside this instance.

Further loads will yield the new value. Uses swap internally.

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 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));

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.

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::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;

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 Arcs inside to share the unchanged values). Something like rpds or im might do what you need.

Trait Implementations

impl<T: RefCnt> From<T> for ArcSwapAny<T>
[src]

Performs the conversion.

impl<T: RefCnt> Drop for ArcSwapAny<T>
[src]

Executes the destructor for this type. Read more

impl<T: RefCnt> Clone for ArcSwapAny<T>
[src]

Returns a copy of the value. Read more

Performs copy-assignment from source. Read more

impl<T> Debug for ArcSwapAny<T> where
    T: RefCnt,
    T::Base: Debug
[src]

Formats the value using the given formatter. Read more

impl<T> Display for ArcSwapAny<T> where
    T: NonNull,
    T::Base: Display
[src]

Formats the value using the given formatter. Read more

Auto Trait Implementations

impl<T> Send for ArcSwapAny<T> where
    T: Send

impl<T> Sync for ArcSwapAny<T> where
    T: Sync