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
Only very basic benchmarks were done so far (you can find them in the git repository). These suggest this is slightly faster than using a mutex in most cases.
Furthermore, this implementation doesn't suffer from contention. Specifically, arbitrary number of readers can access the shared value and won't block each other, 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. Readers always perform the same number of instructions, without any locking or waiting.
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.
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::load
(and theoretically some of the others, but a correct use would be very hard). Note that the
signal handler is not allowed to allocate or deallocate memory. This also means the signal
handler must not drop the last reference to the Arc
received by loading ‒ therefore, the part
changing it from the outside needs to make sure it does not drop the only other instance once
it swapped the content while the signal handler still runs. See
ArcSwap::rcu_unwrap
.
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 |