10 个版本
0.1.8 | 2023年12月17日 |
---|---|
0.1.7 | 2023年10月3日 |
0.1.5 | 2023年1月14日 |
0.1.4 | 2022年4月10日 |
0.0.0 | 2022年1月30日 |
#74 in 内存管理
在 3 crates 中使用
96KB
1K SLoC
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1121r3.pdf https://github.com/facebook/folly/tree/0e92d3c2705a45ba7850708fd7fe0c709d6a0e5f
许可证
基于 Apache 许可证,版本 2.0 (LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)。
贡献
除非您明确声明,否则您提交的任何有意用于包含在作品中的贡献,根据 Apache-2.0 许可证定义,应按上述方式许可,而不附加任何额外条款或条件。
lib.rs
:
无锁数据结构的动态内存管理。
此库实现了危害指针内存回收机制,特别是针对C++ 并发技术规范中提出的。它源自 Facebook 的 实现,该实现位于 Facebook 的 Folly 库中。实现的前期阶段都是现场直播的。
在较高层面,危害指针提供了一种机制,允许共享指针的读取者防止在读取操作进行期间,并发写入者对指向的对象进行并发回收。当写入者从数据结构中删除一个对象时,它会指示危害指针库该对象不再可达(即它是退休的),并且库应该在安全时最终丢弃该对象(回收它)。同时,读取者会在任何想要通过共享指针与写入者读取数据时通知库。内部,库会记录读取的地址,以便确保如果指向的对象在读取者仍然可以访问时被退休,它不会被回收。只有当读取者不再可以访问读取指针时,库才允许回收对象。
待办:还可以帮助解决ABA问题(确保对象在没有任何指针指向它之前不被重用,因此不能“看到”A,直到没有A为止)。
关于危害指针的并发垃圾回收概述,请参阅“使用危害指针的无畏并发”。关于使用基于周期的回收(见下文)的替代方法的讨论,Aaron Turon在"无需垃圾回收的锁自由"上的帖子也是一个很好的参考。
高级API结构
危害指针与其他延迟回收机制的比较
待办:参考提案的第3.4节和第4节以及folly文档中的部分。
特别注意内存使用。
示例
待办:参考提案的第5节和folly文档中的示例。
use haphazard::{AtomicPtr, Domain, HazardPointer};
// First, create something that's intended to be concurrently accessed.
let x = AtomicPtr::from(Box::new(42));
// All reads must happen through a hazard pointer, so make one of those:
let mut h = HazardPointer::new();
// We can now use the hazard pointer to read from the pointer without
// worrying about it being deallocated while we read.
let my_x = x.safe_load(&mut h).expect("not null");
assert_eq!(*my_x, 42);
// We can willingly give up the guard to allow writers to reclaim the Box.
h.reset_protection();
// Doing so invalidates the reference we got from .load:
// let _ = *my_x; // won't compile
// Hazard pointers can be re-used across multiple reads.
let my_x = x.safe_load(&mut h).expect("not null");
assert_eq!(*my_x, 42);
// Dropping the hazard pointer releases our guard on the Box.
drop(h);
// And it also invalidates the reference we got from .load:
// let _ = *my_x; // won't compile
// Multiple readers can access a value at once:
let mut h = HazardPointer::new();
let my_x = x.safe_load(&mut h).expect("not null");
let mut h_tmp = HazardPointer::new();
let _ = x.safe_load(&mut h_tmp).expect("not null");
drop(h_tmp);
// Writers can replace the value, but doing so won't reclaim the old Box.
let old = x.swap(Box::new(9001)).expect("not null");
// New readers will see the new value:
let mut h2 = HazardPointer::new();
let my_x2 = x.safe_load(&mut h2).expect("not null");
assert_eq!(*my_x2, 9001);
// And old readers can still access the old value:
assert_eq!(*my_x, 42);
// The writer can retire the value old readers are seeing.
//
// Safety: this value has not been retired before.
unsafe { old.retire() };
// Reads will continue to work fine, as they're guarded by the hazard.
assert_eq!(*my_x, 42);
// Even if the writer actively tries to reclaim retired objects, the hazard makes readers safe.
let n = Domain::global().eager_reclaim();
assert_eq!(n, 0);
assert_eq!(*my_x, 42);
// Only once the last hazard guarding the old value goes away will the value be reclaimed.
drop(h);
let n = Domain::global().eager_reclaim();
assert_eq!(n, 1);
// Remember to also retire the item stored in the AtomicPtr when it's dropped
// (assuming of course that the pointer is not shared elsewhere):
unsafe { x.retire(); }
与规范的不同
与folly的不同
待办:注意与规范和folly的差异。其他方面,请参阅folly的此注释。
依赖关系
~0–25MB
~334K 额外代码行数