7个版本

0.3.0 2019年9月27日
0.2.4 2019年8月2日
0.2.1 2019年7月31日
0.1.0 2019年7月11日

#28 in #managed

MIT许可证

21KB
188

Hulunbuir

Build Status Doc

查看 examples 文件夹以获取基本用法。


lib.rs:

Hulunbuir 是一个跨线程垃圾收集器。管理对象可以在多线程中使用,收集过程可能发生在任何一个线程中。

通常,读取或更新管理对象必须锁定全局收集器,这会显著降低多线程性能。然而,Hulunbuir 不提供常见的 "读保护" 和 "写保护" 接口;相反,它只支持两个函数: allocatereplace。第一个创建一个管理对象,如果需要,可能会触发垃圾收集过程;第二个用由参数提供的新值替换管理对象的值。全局收集器只需要在替换时锁定,当工作线程拥有该值时可以释放锁。因此,锁不会成为性能瓶颈。

Hulunbuir 还提供了 Slot 作为更高层次抽象和接口。

基本用法

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);
}

这种基于 replace 的对象更新策略适用于简单的单线程使用。收集器将在没有任何 "真实" 对象替换时正确工作,这意味着,当其中任何一个 被替换 出来时

  • 没有显式调用 Collector::collect
  • 没有调用 Collector::allocate,因为它可能会在没有槽位可用时触发收集

在多线程环境中,上述任何一种都不能实现,因为每个线程都不知道其他线程在做什么。因此,必须引入更复杂的策略。Hulunbuir 提供了 slot 模块用于此目的,但你也可以自由开发自己的模块。

依赖关系

~2MB
~46K SLoC