7 releases
0.3.0 | Sep 27, 2019 |
---|---|
0.2.4 | Aug 2, 2019 |
0.2.1 | Jul 31, 2019 |
0.1.0 | Jul 11, 2019 |
#35 in #managed
21KB
188 lines
Hulunbuir
See examples
folder for basic usage.
lib.rs
:
Hulunbuir is a cross-thread garbage collector. The managed objects could be used in multithreads, and collecting process may happen in any of them.
Normally, reading or updating a managed object must lock global collector as well,
which significantly decrease multithread performance. However, Hulunbuir does not provide
common "read guard" and "write guard" interface; instead it only supports two functions:
allocate
and replace
. The first one create a managed object, and may trigger a garbage
collecting process if necessary; the second one replace the value of a managed object with
a new one provided by argument. The global collector only have to be locked during replacing
and the lock could be released when working thread owns the value. So the lock will not
become the bottleneck of performance.
Hulunbuir also provides Slot
as higher level abstraction and interface.
Basic usage
use hulunbuir::{Address, Collector, Keep};
// create a managed type
struct ListNode(i32, Option<Address>);
// implement Keep for it, so it could be managed
impl Keep for ListNode {
fn with_keep<F: FnMut(&Address)>(&self, mut keep: F) {
// each node keeps only its tail, so call `keep` with it...
if let Some(tail) = &self.1 {
// ...if the node has tail
keep(tail)
}
}
}
fn main() {
// create a collector with 128 slots available
let mut collector = Collector::new(128);
let root = collector.allocate(ListNode(0, None)).unwrap();
collector.set_root(root.clone());
let tail = collector.allocate(ListNode(1, None)).unwrap();
// replace root node out with something not important
let mut root_node = collector.replace(&root, ListNode(42, None)).unwrap();
root_node.1 = Some(tail);
// replace root node back
let _ = collector.replace(&root, root_node).unwrap();
let _orphan = collector.allocate(ListNode(2, None)).unwrap();
// before collecting...
assert_eq!(collector.alive_count(), 3);
collector.collect();
// after collecting...
assert_eq!(collector.alive_count(), 2);
}
This replace
-based object updating strategy is suitable for simple single-thread usage.
The collector will work correctly only when no garbage collection happens when any
"real" object is replaced out, which means, when any of them is replaced out:
- no explicit calling to
Collector::collect
- no calling to
Collector::allocate
, since it may trigger collection as well if there's no slot available
In multithreading context, none of above could be archieved since each thread has no idea
about what the others are doing. So more complicated strategy must be introduced. Hulunbuir
provides slot
module for this purpose, but you are free to develop your own one.
Dependencies
~2MB
~46K SLoC