Crate arc_swap[][src]

Making Arc itself atomic

The Arc uses atomic reference counters, so the object behind it can be safely pointed to by several threads at once. However, the Arc itself is quite ordinary ‒ to change its value (make it point somewhere else), one has to be the sole owner of it (or store it behind a Mutex).

On the other hand, there's AtomicPtr. It can be modified and read from multiple threads, allowing to pass the value from one thread to another without the use of a Mutex. The downside is, tracking when the data can be safely deleted is hard.

This library provides ArcSwap that allows both at once. It can be constructed from ordinary Arc, but its value can be loaded and stored atomically, by multiple concurrent threads.

Motivation

For one, the C++ shared_ptr has this ability, so it is only fair to have it too.

For another, it seemed like a really good exercise.

And finally, there are some real use cases for this functionality. For example, when one thread publishes something (for example configuration) and other threads want to have a peek to the current one from time to time. There's a global ArcSwap, holding the current snapshot and everyone is free to make a copy and hold onto it for a while. The publisher thread simply stores a new snapshot every time and the old configuration gets dropped once all the other threads give up their copies of the pointer.

Performance characteristics

The data structure is optimise for read-heavy situations with only occasional writes.

Only very basic benchmarks were done so far (you can find them in the git repository). These suggest reading operations are faster than using a mutex, in a contended situation by a large margin and comparable on writes.

Furthermore, this implementation doesn't suffer from contention. Specifically, arbitrary number of readers can access the shared value and won't block each other, and are not blocked by writers. The writers will be somewhat slower when there are active readers at the same time, but won't be stopped indefinitely. Readers always perform the same number of instructions, without any locking or waiting (though they can slow each other down by accessing the same memory locations under circumstances).

However, the data structure is a bit large so it probably is not suitable to have a lot of them around. It is more aimed to „anchor“ a global immutable data structure than building complex atomic data structures with many pointers inside them.

RCU

This also offers an RCU implementation, for read-heavy situations. Note that the RCU update is considered relatively slow operation. In case there's only one update thread, using store is enough.

Atomic orderings

It is guaranteed each operation performs at least one SeqCst atomic read-write operation, therefore even operations on different instances have a defined global order of operations.

Unix signal handlers

Unix signals are hard to use correctly, partly because there is a very restricted set of functions one might use inside them. Specifically, it is not allowed to use mutexes inside them (because that could cause a deadlock).

On the other hand, it is possible to use ArcSwap::peek_signal_safe (but not the others). Note that the signal handler is not allowed to allocate or deallocate memory, therefore it is not recommended to upgrade the returned guard (it is strictly speaking possible to use that safely, but it is hard and brings no benefit).

Support for NULL

Similar to Arc, ArcSwap always contains a value. There is, however, ArcSwapOption, which works on Option<Arc<_>> instead of Arc<_> and supports mostly the same operations. In fact, both are just type aliases of ArcSwapAny. Therefore, most documentation and methods can be found there instead on the type aliases.

It is also possible to support other types similar to Arc by implementing the RefCnt trait.

Examples

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 config = ArcSwap::from(Arc::new(String::default()));
    thread::scope(|scope| {
        scope.spawn(|| {
            let new_conf = Arc::new("New configuration".to_owned());
            config.store(new_conf);
        });
        for _ in 0..10 {
            scope.spawn(|| {
                loop {
                    let cfg = config.load();
                    if !cfg.is_empty() {
                        assert_eq!(*cfg, "New configuration");
                        return;
                    }
                }
            });
        }
    });
}

Structs

ArcSwapAny

An atomic storage for a smart pointer like Arc or Option<Arc>.

Guard

A short-term proxy object from peek.

Traits

AsRaw

A trait describing things that can be turned into a raw pointer.

NonNull

A trait describing smart pointers that can't hold NULL.

RefCnt

A trait describing smart reference counted pointers.

Type Definitions

ArcSwap

An atomic storage for Arc.

ArcSwapOption

An atomic storage for Option<Arc>.