Expand description
Thread-safe hybrid reference counting pointers
Loosely based on the algorithm described in “Biased reference counting: minimizing atomic operations in garbage collection” by Jiho Choi et. al. but adapted to Rust’s type system and its lack of a managed runtime environment.
The type HybridRc<T, State>
provides thread-safe shared ownership of a value of type T
allocated on the heap, just like std::sync::Arc<T>
does. The main difference is that one
thread at a time can use non-atomic reference counting for better performance. That means that
HybridRc
is especially suited for workloads where one thread accesses the shared value
significantly more often than others.
There a two variants of HybridRc
:
HybridRc<T,
Local
>
(type aliased asRc
): very fast but only usable on one thread.HybridRc<T,
Shared
>
(type aliased asArc
): slower but universally usable.
Instances of both variants are convertible into each other. Especially, an Rc
can always be
converted into an Arc
using HybridRc::to_shared(&rc)
or .into()
.
An Arc
on the other hand can only be converted into an Rc
using HybridRc::to_local(&arc)
or .try_into()
if no other thread has Rc
s for the same value. The thread holding Rc
s to
a value is called the “owner thread”. Once all Rc
s are dropped, the shared value becomes
ownerless again.
HybridRc
is designed as a drop-in replacement for std::sync::Arc
and std::rc::Rc
, so except
for the conversion functionality outlined above the usage is similar to these and other smart
pointers.
Thread Safety
HybridRc
uses two separate reference counters - one modified non-atomically and one using
atomic operations - and keeps track of a owner thread that is allowed to modify the “local”
reference counter. This means that it is thread-safe, while one thread is exempted from
the disadvantage of atomic operations being more expensive than ordinary memory accesses.
no_std
Support
This crate provides limited support for no_std
environments. In this mode Arc::to_local()
and
Weak::upgrade_local()
only succeed if no Rc
exists on any thread, as threads cannot be
reliably identified without std
.
To enable no_std
mode, disable the default enabled std
feature in Cargo.toml. A global
allocator is required.
[dependencies]
hybrid-rc = { version = "…", default-features = false }
Examples
Multiple threads need a reference to a shared value while one thread needs to clone references to the value significantly more often than the others.
use hybrid_rc::{Rc, Arc};
use std::thread;
use std::sync::mpsc::channel;
let local = Rc::new(SomeComplexType::new());
let (sender, receiver) = channel();
// Spawn of threads for multiple expensive computations
for i in 1..=4 {
let sender = sender.clone();
let shared = Rc::to_shared(&local);
thread::spawn(move || {
sender.send(expensive_computation(shared, i));
});
}
// Do something that needs single-thread reference counting
for i in 1..=1000 {
do_something(local.clone(), i);
}
// Collect expensive computation results
for i in 1..=4 {
println!("{:?}", receiver.recv().unwrap());
}
A library wants to give library consumers flexibility for multithreading but also internally
have the performance of std::rc::Rc
for e.g. a complex tree structure that is mutated on
the main thread.
use hybrid_rc::Rc;
use std::thread;
let reference = get_local_hybridrc_from_some_library();
let shared = Rc::to_shared(&reference);
// do the work in another thread
let worker = thread::spawn(move || {
do_something(&*shared);
});
// Do something useful with the library
worker.join()?;
Modules
Structs
The AllocError
error indicates an allocation failure when using try_new()
etc.
A hybrid reference-counting pointer.
PinWeak<T>
represents a non-owning reference to a pinned value managed by a
Pin
<
HybridRc<T, _>
>
.
Weak<T>
represents a non-owning reference to a value managed by a HybridRc<T, _>
.
The value is accessed by calling upgrade()
or upgrade_local()
on Weak
.
Enums
An enumeration of possible errors when upgrading a Weak
.