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 optimised 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, but about 2-3 times slower on writes than mutex (it is still faster than RwLock
on
writes and mutex gets much slower on contended 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, with the exception of the first
lease
in each thread (though they can slow each other
down by accessing the same memory locations under circumstances).
What reading operation to choose
There are actually three different ways to read the data, with different characteristics.
load
creates a full-blownArc
. You can hold onto it as long as desired without any restrictions, but in case there are multiple readers of the sameArcSwapAny
, they slow each other down by fighting over the cache line with reference counts. Therefore, this is suitable for long-term storage of the result.lease
is suitable for short-term storage or manipulation during some algorithm, for example during some lookup. The creation is relatively fast and doesn't suffer from contention, but there's only limited number of fast active leases possible per thread (currently 6). When the number is exceeded, it falls back to the equivalent ofload
internally.peek
is the fastest. However, existingGuard
, as returned by the method, prevents all writer methods from completing (globally, even on unrelatedArcSwap
s). Therefore, it is possible to create a deadlock with careless usage and hurt the performance by holding onto it for too long. It is suitable for very quick operations only, like reading a single value from configuration. Do not store it and do not call non-trivial methods on the returned value.
The faster but shorter-term proxy objects allow upgrading to the longer-term ones, so it is possible to first do some checks for an optimistic case and obtain the longer-term object in the pesimistic case.
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.lease(); if !cfg.is_empty() { assert_eq!(*cfg, "New configuration"); return; } } }); } }); }
Structs
ArcSwapAny |
An atomic storage for a smart pointer like |
Guard |
A short-term proxy object from |
Lease |
A temporary storage of the pointer. |
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. |
Functions
ptr_eq |
Comparison of two pointer-like things. |
Type Definitions
ArcSwap |
An atomic storage for |
ArcSwapOption |
An atomic storage for |