Trc
is a performant biased reference-counted smart pointer for Rust.
It is a heap-allocated smart pointer for sharing data across threads is a thread-safe manner without putting locks on the data.
Trc<T>
stands for: Thread Reference Counted.
Trc<T>
provides a shared ownership of the data similar to Arc<T>
and Rc<T>
.
It implements a custom version of biased reference counting, which is based on the observation that most objects are only used by one thread.
This means that two reference counts can be created: one for thread-local use, and one atomic one for sharing between threads.
This implementation of biased reference counting sets the atomic reference count to the number of threads using the data.
A cycle between Trc
pointers cannot be deallocated as the reference counts will never reach zero. The solution is a Weak<T>
.
A Weak<T>
is a non-owning reference to the data held by a Trc<T>
.
They break reference cycles by adding a layer of indirection and act as an observer. They cannot even access the data directly, and
must be converted back into Trc<T>
. Weak<T>
does not keep the value alive (whcih can be dropped), and only keeps the backing allocation alive.
To soundly implement thread safety Trc<T>
does not itself implement [Send
] or [Sync
]. However, SharedTrc<T>
does, and it is the only way to safely send a Trc<T>
across threads. See SharedTrc
for it's API, which is similar to that of Weak
.
Trc
will automatically compile to use either locks or atomics, depending on the system. By default, Trc
uses std
.
However, Trc
can be compiled without std
. Compilation with locks or atomics can be forced with a feature flag.
Example of Trc<T>
in a single thread:
```rust
use trc::Trc;
let mut trc = Trc::new(100); asserteq!(*trc, 100); *trc = 200; asserteq!(*trc, 200); ```
Example of Trc<T>
with multiple threads:
```rust
use std::thread;
use trc::Trc;
use trc::SharedTrc;
let trc = Trc::new(100); let shared = SharedTrc::fromtrc(&threadtrcmain); let handle = thread::spawn(move || { let mut trc = SharedTrc::totrc(shared); *trc2 = 200; });
handle.join().unwrap(); assert_eq!(*trc, 200); ```
Example of Weak<T>
in a single thread:
```rust
use trc::Trc;
use trc::Weak;
let trc = Trc::new(100); let weak = Weak::fromtrc(&trc); let mut newtrc = Weak::totrc(&weak).unwrap(); println!("Deref test! {}", *newtrc); println!("DerefMut test"); *newtrc = 200; println!("Deref test! {}", *newtrc); ```
Example of Weak<T>
with multiple threads:
```rust
use std::thread;
use trc::Trc;
use trc::Weak;
let trc = Trc::new(100); let weak = Weak::from_trc(&trc);
let handle = thread::spawn(move || { let mut trc = Weak::totrc(&weak).unwrap(); println!("{:?}", *trc); *trc = 200; }); handle.join().unwrap(); println!("{}", *trc); asserteq!(*trc, 200); ```
Benchmarks are conducted by Criterion.
| Type | Mean time | | --- | ----------- | | Trc | 37.822ns | | Arc | 24.450ns | | Rc | 14.084ns |
| Type | Mean time | | --- | ----------- | | Trc | 27.083ns | | Arc | 25.114ns | | Rc | 11.911ns |