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, my 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

Some benchmarks need to be done. Due to the complexity, it may be possible that using Mutex<Arc<T>> might be faster in some cases.

However, this implementation doesn't suffer from contention. Specifically, arbitrary number of readers can access the shared value and won't be blocked. Even when there are many readers and writers at once, they don't block each other. The writers will be somewhat slower when there are active readers at the same time, but won't be stopped indefinitely.

Example

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

ArcSwap

An atomic storage for Arc.