cranpose_core/
snapshot_pinning.rs1use std::cell::RefCell;
2
3use crate::{
4 snapshot_double_index_heap::{SnapshotDoubleIndexHeap, SnapshotDoubleIndexHeapDebugStats},
5 snapshot_id_set::{SnapshotId, SnapshotIdSet},
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct PinHandle(usize);
13
14impl PinHandle {
15 pub const INVALID: PinHandle = PinHandle(0);
17
18 pub fn is_valid(&self) -> bool {
20 self.0 != 0
21 }
22}
23
24struct PinningTable {
25 heap: SnapshotDoubleIndexHeap,
26}
27
28impl PinningTable {
29 fn new() -> Self {
30 Self {
31 heap: SnapshotDoubleIndexHeap::new(),
32 }
33 }
34
35 fn add(&mut self, snapshot_id: SnapshotId) -> PinHandle {
36 let heap_handle = self.heap.add(snapshot_id);
37 PinHandle(heap_handle + 1)
38 }
39
40 fn remove(&mut self, handle: PinHandle) -> bool {
41 if !handle.is_valid() {
42 return false;
43 }
44
45 let heap_handle = handle.0 - 1;
46
47 if heap_handle < usize::MAX {
48 self.heap.remove(heap_handle);
49 true
50 } else {
51 false
52 }
53 }
54
55 fn lowest_pinned(&self) -> Option<SnapshotId> {
56 if self.heap.is_empty() {
57 None
58 } else {
59 Some(self.heap.lowest_or_default(0))
60 }
61 }
62
63 fn pin_count(&self) -> usize {
64 self.heap.len()
65 }
66
67 fn debug_stats(&self) -> SnapshotPinningDebugStats {
68 SnapshotPinningDebugStats {
69 pin_count: self.pin_count(),
70 lowest_pinned_snapshot: self.lowest_pinned(),
71 heap: self.heap.debug_stats(),
72 }
73 }
74}
75
76thread_local! {
77 static PINNING_TABLE: RefCell<PinningTable> = RefCell::new(PinningTable::new());
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
81pub struct SnapshotPinningDebugStats {
82 pub pin_count: usize,
83 pub lowest_pinned_snapshot: Option<SnapshotId>,
84 pub heap: SnapshotDoubleIndexHeapDebugStats,
85}
86
87pub fn track_pinning(snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> PinHandle {
102 let pinned_id = invalid.lowest(snapshot_id);
103
104 PINNING_TABLE.with(|cell| cell.borrow_mut().add(pinned_id))
105}
106
107pub fn release_pinning(handle: PinHandle) {
117 if !handle.is_valid() {
118 return;
119 }
120
121 PINNING_TABLE.with(|cell| {
122 cell.borrow_mut().remove(handle);
123 });
124}
125
126pub fn lowest_pinned_snapshot() -> Option<SnapshotId> {
134 PINNING_TABLE.with(|cell| cell.borrow().lowest_pinned())
135}
136
137pub fn pin_count() -> usize {
140 PINNING_TABLE.with(|cell| cell.borrow().pin_count())
141}
142
143pub fn debug_snapshot_pinning_stats() -> SnapshotPinningDebugStats {
144 PINNING_TABLE.with(|cell| cell.borrow().debug_stats())
145}
146
147#[cfg(test)]
148pub fn reset_pinning_table() {
149 PINNING_TABLE.with(|cell| {
150 let mut table = cell.borrow_mut();
151 table.heap = SnapshotDoubleIndexHeap::new();
152 });
153}
154
155#[cfg(test)]
156#[path = "tests/snapshot_pinning_tests.rs"]
157mod tests;