mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Draw arena generations from a global counter so foreign handles never upgrade
This commit is contained in:
@@ -30,21 +30,32 @@ struct DropEntry {
|
||||
|
||||
// SAFETY: disjoint regions are handed out by an atomic bump; a region is written
|
||||
// only by its allocating caller before publication, and cross-thread hand-off is
|
||||
// ordered by the Release/Acquire pair on the published handle word. The stored
|
||||
// payloads are `Send + Sync` by the bound on every allocating method, so drop
|
||||
// glue running on the resetting thread and lent `&T` crossing threads are sound.
|
||||
// ordered by the Release/Acquire pair on the published handle word. Payloads are
|
||||
// `Send + Sync` by the bound on every allocating method.
|
||||
unsafe impl Sync for Arena {}
|
||||
unsafe impl Send for Arena {}
|
||||
|
||||
impl std::panic::UnwindSafe for Arena {}
|
||||
impl std::panic::RefUnwindSafe for Arena {}
|
||||
|
||||
/// Shared by all arenas, so a foreign handle misses like a stale one.
|
||||
static NEXT_GENERATION: AtomicU64 = AtomicU64::new(1);
|
||||
|
||||
/// Skips generations whose encoded bits are zero, which would let `NULL` upgrade.
|
||||
fn next_generation() -> u64 {
|
||||
loop {
|
||||
let generation = NEXT_GENERATION.fetch_add(1, Ordering::Relaxed);
|
||||
if generation & GENERATION_MASK != 0 {
|
||||
return generation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arena {
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
let buf = (0..capacity).map(|_| UnsafeCell::new(MaybeUninit::uninit())).collect();
|
||||
Self {
|
||||
// Starts at 1 so the null handle word (0) can never upgrade.
|
||||
generation: AtomicU64::new(1),
|
||||
generation: AtomicU64::new(next_generation()),
|
||||
offset: AtomicUsize::new(0),
|
||||
buf,
|
||||
drops: Mutex::new(Vec::new()),
|
||||
@@ -123,7 +134,7 @@ impl Arena {
|
||||
unsafe { (entry.drop_fn)(base.add(entry.offset)) }
|
||||
}
|
||||
*self.offset.get_mut() = 0;
|
||||
self.generation.fetch_add(1, Ordering::Release);
|
||||
self.generation.store(next_generation(), Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +286,16 @@ mod tests {
|
||||
assert!(arena.alloc(0u32).is_some(), "the arena stays usable");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handles_do_not_upgrade_against_a_foreign_arena() {
|
||||
let first = Arena::new(1024);
|
||||
let second = Arena::new(1024);
|
||||
let (_, weak) = first.alloc(41u32).unwrap();
|
||||
assert_eq!(weak.upgrade(&first), Some(&41));
|
||||
assert_eq!(weak.upgrade(&second), None, "a handle must not resolve against another arena");
|
||||
assert_eq!(ArenaWeak::<u32>::NULL.upgrade(&second), None, "the null handle never upgrades");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_drops_dependents_before_their_dependencies() {
|
||||
static ORDER: Mutex<Vec<u32>> = Mutex::new(Vec::new());
|
||||
|
||||
Reference in New Issue
Block a user