dualcache_ff/componant/
qsbr.rs1#![allow(clippy::not_unsafe_ptr_arg_deref)]
2#![allow(clippy::missing_safety_doc)]
3use ::core::sync::atomic::{AtomicPtr, AtomicUsize, AtomicBool, Ordering};
4use core::ptr;
5
6static GLOBAL_EPOCH: AtomicUsize = AtomicUsize::new(1);
7static THREAD_STATES: AtomicPtr<ThreadStateNode> = AtomicPtr::new(ptr::null_mut());
8
9#[doc(hidden)]
10pub unsafe fn reset() {
11 GLOBAL_EPOCH.store(1, Ordering::SeqCst);
12 THREAD_STATES.store(ptr::null_mut(), Ordering::SeqCst);
13}
14
15#[derive(Copy, Clone)]
16struct RetiredNode {
17 index: u32,
18 epoch: u64,
19}
20
21const GARBAGE_CAP: usize = 16384;
22pub struct GarbageQueue {
23 items: [RetiredNode; GARBAGE_CAP],
24 head: AtomicUsize,
25 tail: AtomicUsize,
26}
27
28impl GarbageQueue {
29 const fn new() -> Self {
30 Self {
31 items: [RetiredNode { index: 0, epoch: 0 }; GARBAGE_CAP],
32 head: AtomicUsize::new(0),
33 tail: AtomicUsize::new(0),
34 }
35 }
36}
37
38pub struct MpmcQueue<const N: usize> {
39 head: AtomicUsize,
40 tail: AtomicUsize,
41 buffer: [::core::sync::atomic::AtomicU32; N],
42}
43
44impl<const N: usize> Default for MpmcQueue<N> {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl<const N: usize> MpmcQueue<N> {
51 pub const fn new() -> Self {
52 Self {
53 head: AtomicUsize::new(0),
54 tail: AtomicUsize::new(0),
55 buffer: [const { ::core::sync::atomic::AtomicU32::new(u32::MAX) }; N],
56 }
57 }
58 pub fn push(&self, val: u32) -> bool {
59 let mut tail = self.tail.load(Ordering::Relaxed);
60 loop {
61 if tail.wrapping_sub(self.head.load(Ordering::Acquire)) >= N {
62 return false;
63 }
64 if self.tail.compare_exchange_weak(tail, tail + 1, Ordering::SeqCst, Ordering::Relaxed).is_ok() {
65 while self.buffer[tail % N].load(Ordering::Acquire) != u32::MAX {
66 ::core::hint::spin_loop();
67 }
68 self.buffer[tail % N].store(val, Ordering::Release);
69 return true;
70 }
71 tail = self.tail.load(Ordering::Relaxed);
72 }
73 }
74 pub fn pop(&self) -> Option<u32> {
75 let mut head = self.head.load(Ordering::Relaxed);
76 loop {
77 if head == self.tail.load(Ordering::Acquire) {
78 return None;
79 }
80 let val = self.buffer[head % N].load(Ordering::Acquire);
81 if val != u32::MAX {
82 if self.head.compare_exchange_weak(head, head + 1, Ordering::SeqCst, Ordering::Relaxed).is_ok() {
83 self.buffer[head % N].store(u32::MAX, Ordering::Release);
84 return Some(val);
85 }
86 } else {
87 return None;
88 }
89 head = self.head.load(Ordering::Relaxed);
90 }
91 }
92}
93
94
95pub struct LocalFreeQueue {
96 items: [u32; 16384],
97 len: usize,
98}
99
100impl Default for LocalFreeQueue {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106impl LocalFreeQueue {
107 pub const fn new() -> Self {
108 Self { items: [0; 16384], len: 0 }
109 }
110 pub fn pop(&mut self) -> Option<u32> {
111 if self.len > 0 {
112 self.len -= 1;
113 Some(self.items[self.len])
114 } else {
115 None
116 }
117 }
118 #[must_use]
119 pub fn push(&mut self, val: u32) -> bool {
120 if self.len < 16384 {
121 self.items[self.len] = val;
122 self.len += 1;
123 true
124 } else {
125 false
126 }
127 }
128}
129
130pub struct QsbrToken;
131
132#[cfg_attr(not(feature = "std"), no_std_tool::auto_static(capacity = 256, partition = "qsbr"))]
133pub struct ThreadStateNode {
134 pub active: AtomicBool,
135 pub epoch: AtomicUsize,
136 pub next: *mut ThreadStateNode,
137 pub garbage_queue: GarbageQueue,
138 pub local_free: core::cell::UnsafeCell<LocalFreeQueue>,
139}
140
141impl Default for ThreadStateNode {
142 fn default() -> Self {
143 Self::new()
144 }
145}
146
147impl ThreadStateNode {
148 pub const fn new() -> Self {
149 Self {
150 active: AtomicBool::new(false),
151 epoch: AtomicUsize::new(0),
152 next: ptr::null_mut(),
153 garbage_queue: GarbageQueue::new(),
154 local_free: core::cell::UnsafeCell::new(LocalFreeQueue::new()),
155 }
156 }
157}
158
159unsafe impl Send for ThreadStateNode {}
160unsafe impl Sync for ThreadStateNode {}
161
162pub fn register_node(node: *mut ThreadStateNode) {
165 let mut head = THREAD_STATES.load(Ordering::Acquire);
166 loop {
167 unsafe { (*node).next = head };
168
169 #[cfg(test)]
171 std::thread::yield_now();
172
173 match THREAD_STATES.compare_exchange_weak(
174 head,
175 node,
176 Ordering::Release,
177 Ordering::Relaxed,
178 ) {
179 Ok(_) => break,
180 Err(new_head) => {
181 head = new_head;
182 ::core::hint::spin_loop();
183 }
184 }
185 }
186}
187
188pub fn get_global_epoch() -> usize {
189 GLOBAL_EPOCH.load(Ordering::Relaxed)
190}
191
192pub struct Guard {
194 node: *mut ThreadStateNode,
195}
196
197impl Guard {
198 pub fn new(node: *mut ThreadStateNode) -> Self {
200 let global_epoch = GLOBAL_EPOCH.load(Ordering::Acquire);
201 unsafe {
202 (*node).epoch.store(global_epoch, Ordering::Release);
203 (*node).active.store(true, Ordering::Release);
204 }
205 Self { node }
206 }
207
208 #[inline(always)]
209 pub unsafe fn unpinned(node: *mut ThreadStateNode) -> Self {
210 Self { node }
211 }
212
213 #[inline(always)]
214 pub unsafe fn dummy() -> Self {
215 Self { node: core::ptr::null_mut() }
216 }
217
218 #[inline(always)]
220 pub fn node(&self) -> *mut ThreadStateNode {
221 self.node
222 }
223}
224
225impl Drop for Guard {
226 #[inline(always)]
227 fn drop(&mut self) {
228 if !self.node.is_null() {
229 let node_ref = unsafe { &*self.node };
230 node_ref.active.store(false, Ordering::Release);
231 }
232 }
233}
234
235pub fn pin_relaxed(node: *mut ThreadStateNode) -> Guard {
236 let node_ref = unsafe { &*node };
237 node_ref.active.store(true, Ordering::Relaxed);
238 Guard { node }
239}
240
241#[inline(always)]
242pub fn pin(node: *mut ThreadStateNode) -> Guard {
243 Guard::new(node)
244}
245
246
247pub fn retire<F: FnMut(u32)>(index: usize, node: *mut ThreadStateNode, mut _free_fn: F) {
250 let epoch = GLOBAL_EPOCH.load(Ordering::Acquire) as u64;
251 unsafe {
252 let q = &mut (*node).garbage_queue;
253
254 let mut head = q.head.load(Ordering::Relaxed);
256 while head.wrapping_sub(q.tail.load(Ordering::Acquire)) >= GARBAGE_CAP {
257 let cur_epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
259 (*node).epoch.store(cur_epoch, Ordering::Release);
260 ::core::hint::spin_loop();
261 head = q.head.load(Ordering::Relaxed);
262 }
263
264 let idx = head % GARBAGE_CAP;
265 q.items[idx] = RetiredNode {
266 index: index as u32,
267 epoch,
268 };
269 q.head.store(head.wrapping_add(1), Ordering::Release);
270 }
271}
272
273pub fn daemon_reclaim<F: FnMut(&[u32])>(mut free_batch_fn: F) {
275 GLOBAL_EPOCH.fetch_add(1, Ordering::Relaxed);
276 let min_epoch = get_min_epoch();
277
278 let mut batch = [0u32; 128];
279 let mut batch_len = 0;
280
281 let mut node = THREAD_STATES.load(Ordering::Acquire);
282 while !node.is_null() {
283 unsafe {
284 let q = &mut (*node).garbage_queue;
285 let mut tail = q.tail.load(Ordering::Relaxed);
286 let head = q.head.load(Ordering::Acquire);
287
288 while tail < head {
289 let idx = tail % GARBAGE_CAP;
290 let retired = q.items[idx];
291
292 if retired.epoch < min_epoch as u64 {
293 batch[batch_len] = retired.index;
294 batch_len += 1;
295 tail += 1;
296
297 if batch_len == 128 {
298 free_batch_fn(&batch[..batch_len]);
299 batch_len = 0;
300 }
301 } else {
302 break;
303 }
304 }
305 q.tail.store(tail, Ordering::Release);
306
307 node = (*node).next;
308 }
309 }
310 if batch_len > 0 {
311 free_batch_fn(&batch[..batch_len]);
312 }
313}
314
315fn get_min_epoch() -> usize {
316 let mut min_epoch = GLOBAL_EPOCH.load(Ordering::Acquire);
317 let mut node = THREAD_STATES.load(Ordering::Acquire);
318 while !node.is_null() {
319 unsafe {
320 if (*node).active.load(Ordering::Acquire) {
321 let e = (*node).epoch.load(Ordering::Acquire);
322 if e < min_epoch { min_epoch = e; }
323 }
324 node = (*node).next;
325 }
326 }
327 min_epoch
328}
329
330
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335
336 #[test]
337 fn test_qsbr_thread_state_node_new() {
338 let node = ThreadStateNode::new();
339 assert_eq!(node.epoch.load(Ordering::Relaxed), 0);
340 }
341}