1#[cfg(not(feature = "std"))]
2use alloc::{boxed::Box, vec::Vec};
3
4use crate::sync::{Arc, ArcSlice, new_arc_slice};
5use crate::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering};
6use crate::sync::index_types::AtomicTick;
7use core::hash::{Hash, BuildHasher};
8
9use crate::arena::Arena;
10use crate::storage::{Cache, Node};
11use crate::filters::{T1, T2};
12use crate::lossy_queue::{LossyQueue, OneshotAck};
13use crate::cache::{WorkerState, GLOBAL_EPOCH};
14
15const MAX_RANK: u8 = 3;
19
20pub enum Command<K, V> {
23 Insert(K, V, u64, bool),
25 BatchInsert(Vec<(K, V, u64, bool)>),
27 InsertT1(K, V, u64),
29 Remove(K, u64),
31 Clear(Arc<OneshotAck>),
33 Sync(Arc<OneshotAck>),
35 Shutdown,
37}
38
39pub struct Daemon<K, V, S> {
42 pub hasher: S,
43 pub arena: Arena,
44 pub t1: Arc<T1<K, V>>,
45 pub t2: Arc<T2<K, V>>,
46 pub cache: Arc<Cache<K, V>>,
47 pub cmd_rx: Arc<LossyQueue<Command<K, V>>>,
48 pub hit_rx: Arc<LossyQueue<[usize; 64]>>,
49 pub epoch: Arc<AtomicU32>,
50 pub poll_us: u64,
53 pub admission: Arc<AdmissionFilter>,
54 pub hit_accumulator: Vec<usize>,
56 pub last_decay_epoch: u32,
57 pub garbage_queue: Vec<(*mut Node<K, V>, usize)>,
58 pub worker_states: ArcSlice<WorkerState>,
59 pub daemon_tick: Arc<AtomicTick>,
63 pub is_cold_start: Arc<AtomicBool>,
65 pub duration: u32,
67}
68
69unsafe impl<K: Send, V: Send, S: Send> Send for Daemon<K, V, S> {}
70
71impl<K, V, S> Daemon<K, V, S>
72where
73 K: Hash + Eq + Send + Sync + Clone + 'static,
74 V: Send + Sync + Clone + 'static,
75 S: BuildHasher + Clone + Send + 'static,
76{
77 #[allow(clippy::too_many_arguments)]
78 pub fn new(
79 hasher: S,
80 capacity: usize,
81 t1: Arc<T1<K, V>>,
82 t2: Arc<T2<K, V>>,
83 cache: Arc<Cache<K, V>>,
84 cmd_rx: Arc<LossyQueue<Command<K, V>>>,
85 hit_rx: Arc<LossyQueue<[usize; 64]>>,
86 epoch: Arc<AtomicU32>,
87 duration: u32,
88 poll_us: u64,
89 worker_states: ArcSlice<WorkerState>,
90 daemon_tick: Arc<AtomicTick>,
91 is_cold_start: Arc<AtomicBool>,
92 ) -> Self {
93 Self {
94 hasher,
95 arena: Arena::new(capacity),
96 t1,
97 t2,
98 cache,
99 cmd_rx,
100 hit_rx,
101 epoch,
102 poll_us,
103 admission: Arc::new(AdmissionFilter::new(capacity)),
104 hit_accumulator: Vec::with_capacity(8192),
105 last_decay_epoch: 0,
106 garbage_queue: Vec::new(),
107 worker_states,
108 daemon_tick,
109 is_cold_start,
110 duration,
111 }
112 }
113
114 pub fn run(mut self) {
125 #[cfg(feature = "std")]
126 let mut last_epoch_tick = std::time::Instant::now();
127
128 loop {
129 let mut processed = 0u32;
131 loop {
132 match self.cmd_rx.try_recv() {
133 Some(Command::Shutdown) => return,
134 Some(cmd) => {
135 self.process_cmd(cmd);
136 processed += 1;
137 if processed >= 8192 {
138 break;
139 }
140 }
141 None => break,
142 }
143 }
144
145 #[cfg(feature = "std")]
149 {
150 let now = std::time::Instant::now();
151 if now.duration_since(last_epoch_tick)
152 >= std::time::Duration::from_millis(100)
153 {
154 self.epoch.fetch_add(1, Ordering::Relaxed);
155 last_epoch_tick = now;
156 }
157 }
158 #[cfg(not(feature = "std"))]
159 {
160 let tick = self.daemon_tick.load(Ordering::Relaxed);
161 if tick % 100 == 0 {
162 self.epoch.fetch_add(1, Ordering::Relaxed);
163 }
164 }
165
166 self.maintenance();
168
169 #[cfg(any(feature = "loom", loom))]
171 {
172 if processed > 0 {
173 self.daemon_tick.fetch_add(1, Ordering::Relaxed);
174 }
175 }
176 #[cfg(not(any(feature = "loom", loom)))]
177 {
178 self.daemon_tick.fetch_add(1, Ordering::Relaxed);
179 }
180
181 if processed == 0 {
183 #[cfg(any(feature = "loom", loom))]
184 loom::thread::yield_now();
185 #[cfg(all(feature = "std", not(any(feature = "loom", loom))))]
186 std::thread::sleep(std::time::Duration::from_micros(self.poll_us));
187 #[cfg(not(feature = "std"))]
188 core::hint::spin_loop();
189 }
190 }
191 }
192
193 #[inline(always)]
194 fn process_cmd(&mut self, cmd: Command<K, V>) -> bool {
195 match cmd {
196 Command::Insert(k, v, hash, is_t1) => {
197 self.handle_admission_insert(k, v, hash, is_t1);
198 true
199 }
200 Command::BatchInsert(batch) => {
201 for (k, v, hash, is_t1) in batch {
202 self.handle_admission_insert(k, v, hash, is_t1);
203 }
204 true
205 }
206 Command::InsertT1(k, v, hash) => {
207 self.handle_insert_t1(k, v, hash);
208 true
209 }
210 Command::Remove(k, hash) => {
211 self.handle_remove(k, hash);
212 true
213 }
214 Command::Clear(ack) => {
215 self.handle_clear();
216 ack.signal();
217 true
218 }
219 Command::Sync(ack) => {
220 self.maintenance();
221 ack.signal();
222 true
223 }
224 Command::Shutdown => unreachable!("handled in run()"),
225 }
226 }
227
228 fn handle_admission_insert(&mut self, k: K, v: V, hash: u64, is_t1: bool) {
232 if is_t1 {
233 self.handle_insert_t1(k, v, hash);
234 return;
235 }
236 let cold_start = self.arena.free_list_len() > self.arena.capacity / 20;
237 self.is_cold_start.store(cold_start, Ordering::Relaxed);
238 if cold_start || self.admission.check_ghost(hash) {
239 self.handle_insert_with_hash(k, v, hash);
240 }
241 }
242
243 fn handle_insert_with_hash(&mut self, k: K, v: V, hash: u64) {
244 let tag = (hash >> 48) as u16;
245
246 let global_idx = if let Some(existing_idx) = self.cache.index_probe(hash, tag) {
248 existing_idx
249 } else {
250 if self.arena.free_list_empty() {
252 self.evict_batch();
253 }
254 if let Some(new_idx) = self.arena.pop_free_slot() {
255 new_idx
256 } else {
257 return; }
259 };
260
261 let entry = (tag as u64) << 48 | (global_idx as u64 & 0x0000_FFFF_FFFF_FFFF);
262
263 let node_ptr = Box::into_raw(Box::new(Node {
264 key: k,
265 value: v,
266 expire_at: self.epoch.load(Ordering::Relaxed) + self.get_duration(),
267 g_idx: global_idx as u32,
268 }));
269
270 let old_ptr = self.cache.nodes[global_idx].swap(node_ptr, Ordering::Release);
271 if !old_ptr.is_null() {
272 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
273 self.garbage_queue.push((old_ptr, epoch));
274 }
275
276 self.cache.index_store(hash, tag, entry);
277 self.arena.set_hash(global_idx, hash);
278 self.arena.set_rank(global_idx, MAX_RANK);
280 }
281
282 fn handle_insert_t1(&mut self, k: K, v: V, hash: u64) {
283 let tag = (hash >> 48) as u16;
284
285 let global_idx = if let Some(existing_idx) = self.cache.index_probe(hash, tag) {
286 existing_idx
287 } else {
288 if self.arena.free_list_empty() {
289 self.evict_batch();
290 }
291 if let Some(new_idx) = self.arena.pop_free_slot() {
292 new_idx
293 } else {
294 return;
295 }
296 };
297
298 let entry = (tag as u64) << 48 | (global_idx as u64 & 0x0000_FFFF_FFFF_FFFF);
299
300 let node_ptr = Box::into_raw(Box::new(Node {
301 key: k,
302 value: v,
303 expire_at: self.epoch.load(Ordering::Relaxed) + self.get_duration(),
304 g_idx: global_idx as u32,
305 }));
306
307 let old_ptr = self.cache.nodes[global_idx].swap(node_ptr, Ordering::Release);
308 if !old_ptr.is_null() {
309 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
310 self.garbage_queue.push((old_ptr, epoch));
311 }
312
313 self.cache.index_store(hash, tag, entry);
314 self.arena.set_hash(global_idx, hash);
315 self.arena.set_rank(global_idx, 255); self.t1.store_slot(hash, node_ptr);
319 }
320
321 fn get_duration(&self) -> u32 {
322 self.duration
323 }
324
325 fn handle_remove(&mut self, _k: K, hash: u64) {
326 let tag = (hash >> 48) as u16;
327 if let Some(g_idx) = self.cache.index_probe(hash, tag) {
328 let old_ptr =
329 self.cache.nodes[g_idx].swap(core::ptr::null_mut(), Ordering::Release);
330 if !old_ptr.is_null() {
331 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
332 self.garbage_queue.push((old_ptr, epoch));
333 self.t1.clear_if_matches(hash, old_ptr);
334 self.t2.clear_if_matches(hash, old_ptr);
335 }
336 self.cache.index_remove(hash, tag, g_idx);
337 self.arena.set_rank(g_idx, 0); }
339 }
340
341 fn handle_clear(&mut self) {
342 self.cache.clear();
343 for i in 0..self.t1.len() {
344 self.t1.clear_at(i);
345 }
346 for i in 0..self.t2.len() {
347 self.t2.clear_at(i);
348 }
349 self.admission.clear();
350 self.arena.clear();
351 self.is_cold_start.store(true, Ordering::Relaxed);
352 }
353
354 fn maintenance(&mut self) {
355 if !self.garbage_queue.is_empty() {
357 let current_global = GLOBAL_EPOCH.load(Ordering::Relaxed);
358 GLOBAL_EPOCH.store(current_global + 1, Ordering::Release);
359
360 let mut min_active_epoch = current_global + 1;
361 for state in self.worker_states.iter() {
362 let local = state.local_epoch.load(Ordering::Acquire);
363 if local != 0 && local < min_active_epoch {
364 min_active_epoch = local;
365 }
366 }
367
368 self.garbage_queue.retain(|&(ptr, epoch)| {
369 if epoch < min_active_epoch {
370 unsafe { drop(Box::from_raw(ptr)) };
371 false
372 } else {
373 true
374 }
375 });
376 }
377
378 while let Some(batch) = self.hit_rx.try_recv() {
380 for &g_idx in batch.iter() {
381 if g_idx < self.arena.capacity && g_idx != usize::MAX {
382 self.hit_accumulator.push(g_idx);
383 }
384 }
385 if self.hit_accumulator.len() >= 8192 {
386 break;
387 }
388 }
389
390 if !self.hit_accumulator.is_empty() {
392 self.hit_accumulator.sort_unstable();
393
394 for &g_idx in &self.hit_accumulator {
395 self.arena.set_rank(g_idx, MAX_RANK);
397
398 let hash = self.arena.get_hash(g_idx);
399
400 let ptr = self.cache.nodes[g_idx].load(Ordering::Acquire);
402 if !ptr.is_null() && self.t1.load_slot(hash) != ptr {
403 self.t1.store_slot(hash, ptr);
404 }
405 }
406
407 self.hit_accumulator.clear();
408 }
409
410 if self.arena.free_list_len() < self.arena.capacity / 10 {
411 self.evict_batch();
412 }
413
414 let cold_start = self.arena.free_list_len() > self.arena.capacity / 20;
415 if self.is_cold_start.load(Ordering::Relaxed) != cold_start {
416 self.is_cold_start.store(cold_start, Ordering::Relaxed);
417 }
418 }
419
420 fn evict_batch(&mut self) {
423 let count = 128;
424 let avg = (self.arena.count_sum() / self.arena.capacity as u64) as u8;
425 let threshold = avg.max(1);
426
427 for _ in 0..count {
428 if self.arena.free_list_len() > self.arena.capacity / 10 {
429 break;
430 }
431
432 let idx = self.arena.cursor();
433 let r = self.arena.get_rank(idx);
434
435 if r <= threshold {
436 let hash = self.arena.get_hash(idx);
438 let tag = (hash >> 48) as u16;
439
440 let old_ptr =
441 self.cache.nodes[idx].swap(core::ptr::null_mut(), Ordering::Release);
442 if !old_ptr.is_null() {
443 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
444 self.garbage_queue.push((old_ptr, epoch));
445 self.t1.clear_if_matches(hash, old_ptr);
446 self.t2.clear_if_matches(hash, old_ptr);
447 }
448
449 self.cache.index_remove(hash, tag, idx);
450
451 self.admission.record_death(hash);
455 self.arena.push_free_slot(idx);
456 self.arena.set_rank(idx, 0);
457 } else {
458 self.arena.decrement_rank(idx);
460 }
461 self.arena.advance_cursor();
462 }
463 }
464}
465
466impl<K, V, S> Drop for Daemon<K, V, S> {
467 fn drop(&mut self) {
468 for &(ptr, _) in self.garbage_queue.iter() {
469 if !ptr.is_null() {
470 unsafe {
471 let _ = Box::from_raw(ptr);
472 }
473 }
474 }
475 }
476}
477
478pub struct AdmissionFilter {
491 pub ghost_mask: usize,
492 pub ghost_set: ArcSlice<AtomicU16>,
493}
494
495impl AdmissionFilter {
496 pub fn new(capacity: usize) -> Self {
499 let ghost_size = capacity.max(256);
502
503 let mut ghost_vec = Vec::with_capacity(ghost_size);
504 for _ in 0..ghost_size {
505 ghost_vec.push(AtomicU16::new(0));
506 }
507
508 Self {
509 ghost_mask: ghost_size - 1,
510 ghost_set: new_arc_slice(ghost_vec),
511 }
512 }
513
514 #[inline(always)]
516 pub fn record_death(&self, hash: u64) {
517 let fp = (hash >> 48) as u16;
518 let idx = (hash as usize) & self.ghost_mask;
519 self.ghost_set[idx].store(fp, Ordering::Relaxed);
520 }
521
522 #[inline(always)]
525 pub fn check_ghost(&self, hash: u64) -> bool {
526 let fp = (hash >> 48) as u16;
527 let ghost_idx = (hash as usize) & self.ghost_mask;
528 self.ghost_set[ghost_idx].load(Ordering::Relaxed) == fp
529 }
530
531 pub fn clear(&self) {
532 for val in self.ghost_set.iter() {
533 val.store(0, Ordering::Relaxed);
534 }
535 }
536}