Skip to main content

dualcache_ff/componant/
arena.rs

1#![allow(clippy::missing_safety_doc)]
2use ::core::sync::atomic::{AtomicUsize, Ordering};
3use core::cell::UnsafeCell;
4use core::mem::MaybeUninit;
5
6pub const NULL_INDEX: u32 = u32::MAX;
7
8/// A lock-free static memory pool for Nodes using tagged indices to prevent ABA.
9pub struct Arena<K, V, const N: usize> {
10    nodes: [UnsafeCell<MaybeUninit<Node<K, V>>>; N],
11    next_free: [::core::sync::atomic::AtomicU32; N],
12    free_head: AtomicUsize, // Packed: (tag << 32) | index
13}
14
15// Ensure Arena can be shared across threads
16unsafe impl<K: Send, V: Send, const N: usize> Send for Arena<K, V, N> {}
17unsafe impl<K: Sync, V: Sync, const N: usize> Sync for Arena<K, V, N> {}
18
19pub struct Node<K, V> {
20    pub key: K,
21    pub value: V,
22}
23
24impl<K, V, const N: usize> Default for Arena<K, V, N> {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl<K, V, const N: usize> Arena<K, V, N> {
31    pub const fn new() -> Self {
32        let mut next_free = [const { ::core::sync::atomic::AtomicU32::new(0) }; N];
33        let mut i = 0;
34        while i < N - 1 {
35            next_free[i] = ::core::sync::atomic::AtomicU32::new((i + 1) as u32);
36            i += 1;
37        }
38        next_free[N - 1] = ::core::sync::atomic::AtomicU32::new(NULL_INDEX);
39
40        // Nodes array initialization
41        let nodes: [UnsafeCell<MaybeUninit<Node<K, V>>>; N] = unsafe {
42            core::mem::MaybeUninit::uninit().assume_init()
43        };
44
45        Self {
46            nodes,
47            next_free,
48            free_head: AtomicUsize::new(0), // tag 0, index 0
49        }
50    }
51
52    /// Allocates a node from the free list and initializes it.
53    /// Returns the index of the allocated node.
54    /// # Safety
55    /// `node` must be a valid pointer.
56    #[allow(clippy::not_unsafe_ptr_arg_deref)]
57    pub fn alloc(&self, key: K, value: V, node: *mut crate::componant::qsbr::ThreadStateNode) -> Option<usize> {
58        let local_free = unsafe { &mut *(*node).local_free.get() };
59        if let Some(idx) = local_free.pop() {
60            unsafe {
61                (*self.nodes[idx as usize].get()).write(Node { key, value });
62            }
63            return Some(idx as usize);
64        }
65
66
67
68        // Batch pop from free_head to amortize CAS contention
69        let mut head = self.free_head.load(Ordering::Acquire);
70        loop {
71            let index = (head & 0xFFFFFFFF) as u32;
72            if index == NULL_INDEX {
73                return None; // OOM
74            }
75
76            // Find the 64th node to batch
77            let mut curr = index;
78            let mut count = 1;
79            while count < 64 {
80                let next = self.next_free[curr as usize].load(Ordering::Relaxed);
81                if next == NULL_INDEX {
82                    break;
83                }
84                curr = next;
85                count += 1;
86            }
87
88            let next_after_batch = self.next_free[curr as usize].load(Ordering::Relaxed);
89            let tag = head >> 32;
90            let new_head = (tag.wrapping_add(1) << 32) | (next_after_batch as usize);
91
92            match self.free_head.compare_exchange_weak(head, new_head, Ordering::AcqRel, Ordering::Acquire) {
93                Ok(_) => {
94                    // Success! Grabbed a batch of `count` items.
95                    // Put the first one into our return, and the rest into local_free
96                    let mut p = self.next_free[index as usize].load(Ordering::Relaxed);
97                    for _ in 1..count {
98                        let _ = local_free.push(p);
99                        p = self.next_free[p as usize].load(Ordering::Relaxed);
100                    }
101                    
102                    unsafe {
103                        (*self.nodes[index as usize].get()).write(Node { key, value });
104                    }
105                    return Some(index as usize);
106                }
107                Err(h) => {
108                    head = h;
109                    ::core::hint::spin_loop();
110                }
111            }
112        }
113    }
114
115    /// Safely frees a node, running its drop logic, and returning it to the free list.
116    /// MUST only be called when no threads are reading the node (e.g., via QSBR).
117    pub unsafe fn free(&self, index: usize) { unsafe {
118        self.drop_node(index);
119        self.free_raw(index);
120    }}
121
122    /// Drops the inner item without pushing it to the free list.
123    pub unsafe fn drop_node(&self, index: usize) {
124        unsafe {
125            core::ptr::drop_in_place((*self.nodes[index].get()).as_mut_ptr());
126        }
127    }
128    
129    pub fn set_next_free(&self, index: u32, next: u32) {
130        self.next_free[index as usize].store(next, Ordering::Relaxed);
131    }
132
133    /// Pushes a batch of nodes to the global free list without dropping it.
134    pub unsafe fn free_batch(&self, head_idx: u32, tail_idx: u32) {
135        let mut head = self.free_head.load(Ordering::Relaxed);
136        loop {
137            let next = (head & 0xFFFFFFFF) as u32;
138            self.next_free[tail_idx as usize].store(next, Ordering::Relaxed);
139            let tag = head >> 32;
140            let new_head = (tag.wrapping_add(1) << 32) | (head_idx as usize);
141            
142            if self.free_head.compare_exchange_weak(head, new_head, Ordering::AcqRel, Ordering::Acquire).is_ok() {
143                return;
144            } else {
145                head = self.free_head.load(Ordering::Relaxed);
146                ::core::hint::spin_loop();
147            }
148        }
149    }
150
151    /// Pushes a node index to the global free list without dropping it.
152    pub unsafe fn free_raw(&self, index: usize) { unsafe {
153        self.free_batch(index as u32, index as u32);
154    }}
155
156    /// Get a reference to a node.
157    /// Caller must ensure index is valid and the node is currently allocated.
158    #[inline(always)]
159    pub unsafe fn get(&self, index: usize) -> &Node<K, V> {
160        unsafe {
161            &*((*self.nodes[index].get()).as_ptr())
162        }
163    }
164
165    #[allow(clippy::mut_from_ref)]
166    pub unsafe fn get_mut(&self, index: usize) -> &mut Node<K, V> {
167        unsafe {
168            &mut *((*self.nodes[index].get()).as_mut_ptr())
169        }
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176    use std::sync::Arc;
177    use std::thread;
178
179    #[test]
180    fn test_arena_default() {
181        let arena = Arena::<u64, u64, 100>::new();
182        let mut dummy_node = crate::componant::qsbr::ThreadStateNode::new();
183        let dummy_ptr = &mut dummy_node as *mut _;
184        assert!(arena.alloc(1, 10, dummy_ptr).is_some());
185    }
186
187    #[test]
188    fn test_arena_cas_retries() {
189        let arena = Arc::new(Arena::<u64, u64, 1000>::new());
190        let mut handles = vec![];
191
192        for i in 0..10 {
193            let arena_clone = arena.clone();
194            handles.push(thread::spawn(move || {
195                let mut dummy_node = crate::componant::qsbr::ThreadStateNode::new();
196                let dummy_ptr = &mut dummy_node as *mut _;
197                let mut idxs = vec![];
198                for _ in 0..50 {
199                    if let Some(idx) = arena_clone.alloc(i, i * 10, dummy_ptr) {
200                        idxs.push(idx);
201                    }
202                }
203                for idx in idxs {
204                    unsafe { arena_clone.free(idx); }
205                }
206            }));
207        }
208        for handle in handles {
209            handle.join().unwrap();
210        }
211    }
212    #[test]
213    fn test_arena_oom() {
214        let arena = Arena::<u64, u64, 4>::new();
215        let node = {
216            let node = std::boxed::Box::into_raw(std::boxed::Box::new(crate::componant::qsbr::ThreadStateNode::new()));
217            crate::componant::qsbr::register_node(node, 0, ::core::ptr::null(), None);
218            node
219        };
220        assert!(arena.alloc(1, 1, node).is_some());
221        assert!(arena.alloc(2, 2, node).is_some());
222        assert!(arena.alloc(3, 3, node).is_some());
223        assert!(arena.alloc(4, 4, node).is_some());
224        assert!(arena.alloc(5, 5, node).is_none()); // OOM
225    }
226}