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),
25 InsertT1(K, V, u64),
27 BatchInsert(Vec<(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}
66
67unsafe impl<K: Send, V: Send, S: Send> Send for Daemon<K, V, S> {}
68
69impl<K, V, S> Daemon<K, V, S>
70where
71 K: Hash + Eq + Send + Sync + Clone + 'static,
72 V: Send + Sync + Clone + 'static,
73 S: BuildHasher + Clone + Send + 'static,
74{
75 #[allow(clippy::too_many_arguments)]
76 pub fn new(
77 hasher: S,
78 capacity: usize,
79 t1: Arc<T1<K, V>>,
80 t2: Arc<T2<K, V>>,
81 cache: Arc<Cache<K, V>>,
82 cmd_rx: Arc<LossyQueue<Command<K, V>>>,
83 hit_rx: Arc<LossyQueue<[usize; 64]>>,
84 epoch: Arc<AtomicU32>,
85 duration: u32,
86 poll_us: u64,
87 worker_states: ArcSlice<WorkerState>,
88 daemon_tick: Arc<AtomicTick>,
89 is_cold_start: Arc<AtomicBool>,
90 ) -> Self {
91 let _ = duration; Self {
93 hasher,
94 arena: Arena::new(capacity),
95 t1,
96 t2,
97 cache,
98 cmd_rx,
99 hit_rx,
100 epoch,
101 poll_us,
102 admission: Arc::new(AdmissionFilter::new(capacity)),
103 hit_accumulator: Vec::with_capacity(8192),
104 last_decay_epoch: 0,
105 garbage_queue: Vec::new(),
106 worker_states,
107 daemon_tick,
108 is_cold_start,
109 }
110 }
111
112 pub fn run(mut self) {
123 #[cfg(feature = "std")]
124 let mut last_epoch_tick = std::time::Instant::now();
125
126 loop {
127 let mut processed = 0u32;
129 loop {
130 match self.cmd_rx.try_recv() {
131 Some(Command::Shutdown) => return,
132 Some(cmd) => {
133 self.process_cmd(cmd);
134 processed += 1;
135 if processed >= 8192 {
136 break;
137 }
138 }
139 None => break,
140 }
141 }
142
143 #[cfg(feature = "std")]
147 {
148 let now = std::time::Instant::now();
149 if now.duration_since(last_epoch_tick)
150 >= std::time::Duration::from_millis(100)
151 {
152 self.epoch.fetch_add(1, Ordering::Relaxed);
153 last_epoch_tick = now;
154 }
155 }
156 #[cfg(not(feature = "std"))]
157 {
158 let tick = self.daemon_tick.load(Ordering::Relaxed);
159 if tick % 100 == 0 {
160 self.epoch.fetch_add(1, Ordering::Relaxed);
161 }
162 }
163
164 self.maintenance();
166
167 #[cfg(any(feature = "loom", loom))]
169 {
170 if processed > 0 {
171 self.daemon_tick.fetch_add(1, Ordering::Relaxed);
172 }
173 }
174 #[cfg(not(any(feature = "loom", loom)))]
175 {
176 self.daemon_tick.fetch_add(1, Ordering::Relaxed);
177 }
178
179 if processed == 0 {
181 #[cfg(any(feature = "loom", loom))]
182 loom::thread::yield_now();
183 #[cfg(all(feature = "std", not(any(feature = "loom", loom))))]
184 std::thread::sleep(std::time::Duration::from_micros(self.poll_us));
185 #[cfg(not(feature = "std"))]
186 core::hint::spin_loop();
187 }
188 }
189 }
190
191 #[inline(always)]
192 fn process_cmd(&mut self, cmd: Command<K, V>) {
193 match cmd {
194 Command::Insert(k, v, hash) => self.handle_admission_insert(k, v, hash),
195 Command::InsertT1(k, v, hash) => self.handle_insert_t1(k, v, hash),
196 Command::BatchInsert(batch) => {
197 for (k, v, hash) in batch {
198 self.handle_admission_insert(k, v, hash);
199 }
200 }
201 Command::Remove(k, hash) => self.handle_remove(k, hash),
202 Command::Clear(ack) => {
203 self.handle_clear();
204 ack.signal();
205 }
206 Command::Sync(ack) => {
207 self.maintenance();
208 ack.signal();
209 }
210 Command::Shutdown => unreachable!("handled in run()"),
211 }
212 }
213
214 fn handle_admission_insert(&mut self, k: K, v: V, hash: u64) {
218 let cold_start = self.arena.free_list_len() > self.arena.capacity / 20;
219 self.is_cold_start.store(cold_start, Ordering::Relaxed);
220 if cold_start || self.admission.check_ghost(hash) {
221 self.handle_insert_with_hash(k, v, hash);
222 }
223 }
224
225 fn handle_insert_with_hash(&mut self, k: K, v: V, hash: u64) {
226 let tag = (hash >> 48) as u16;
227
228 let global_idx = if let Some(existing_idx) = self.cache.index_probe(hash, tag) {
230 existing_idx
231 } else {
232 if self.arena.free_list_empty() {
234 self.evict_batch();
235 }
236 if let Some(new_idx) = self.arena.pop_free_slot() {
237 new_idx
238 } else {
239 return; }
241 };
242
243 let entry = (tag as u64) << 48 | (global_idx as u64 & 0x0000_FFFF_FFFF_FFFF);
244
245 let node_ptr = Box::into_raw(Box::new(Node {
246 key: k,
247 value: v,
248 expire_at: self.epoch.load(Ordering::Relaxed) + self.get_duration(),
249 g_idx: global_idx as u32,
250 }));
251
252 let old_ptr = self.cache.nodes[global_idx].swap(node_ptr, Ordering::Release);
253 if !old_ptr.is_null() {
254 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
255 self.garbage_queue.push((old_ptr, epoch));
256 }
257
258 self.cache.index_store(hash, tag, entry);
259 self.arena.set_hash(global_idx, hash);
260 self.arena.set_rank(global_idx, MAX_RANK);
262 }
263
264 fn handle_insert_t1(&mut self, k: K, v: V, hash: u64) {
265 let tag = (hash >> 48) as u16;
266
267 let global_idx = if let Some(existing_idx) = self.cache.index_probe(hash, tag) {
268 existing_idx
269 } else {
270 if self.arena.free_list_empty() {
271 self.evict_batch();
272 }
273 if let Some(new_idx) = self.arena.pop_free_slot() {
274 new_idx
275 } else {
276 return;
277 }
278 };
279
280 let entry = (tag as u64) << 48 | (global_idx as u64 & 0x0000_FFFF_FFFF_FFFF);
281
282 let node_ptr = Box::into_raw(Box::new(Node {
283 key: k,
284 value: v,
285 expire_at: self.epoch.load(Ordering::Relaxed) + self.get_duration(),
286 g_idx: global_idx as u32,
287 }));
288
289 let old_ptr = self.cache.nodes[global_idx].swap(node_ptr, Ordering::Release);
290 if !old_ptr.is_null() {
291 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
292 self.garbage_queue.push((old_ptr, epoch));
293 }
294
295 self.cache.index_store(hash, tag, entry);
296 self.arena.set_hash(global_idx, hash);
297 self.arena.set_rank(global_idx, 255); self.t1.store_slot(hash, node_ptr);
301 }
302
303 fn get_duration(&self) -> u32 {
304 10
307 }
308
309 fn handle_remove(&mut self, _k: K, hash: u64) {
310 let tag = (hash >> 48) as u16;
311 if let Some(g_idx) = self.cache.index_probe(hash, tag) {
312 let old_ptr =
313 self.cache.nodes[g_idx].swap(core::ptr::null_mut(), Ordering::Release);
314 if !old_ptr.is_null() {
315 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
316 self.garbage_queue.push((old_ptr, epoch));
317 self.t1.clear_if_matches(hash, old_ptr);
318 self.t2.clear_if_matches(hash, old_ptr);
319 }
320 self.cache.index_remove(hash, tag, g_idx);
321 self.arena.set_rank(g_idx, 0); }
323 }
324
325 fn handle_clear(&mut self) {
326 self.cache.clear();
327 for i in 0..self.t1.len() {
328 self.t1.clear_at(i);
329 }
330 for i in 0..self.t2.len() {
331 self.t2.clear_at(i);
332 }
333 self.admission.clear();
334 self.arena.clear();
335 self.is_cold_start.store(true, Ordering::Relaxed);
336 }
337
338 fn maintenance(&mut self) {
339 if !self.garbage_queue.is_empty() {
341 let current_global = GLOBAL_EPOCH.load(Ordering::Relaxed);
342 GLOBAL_EPOCH.store(current_global + 1, Ordering::Release);
343
344 let mut min_active_epoch = current_global + 1;
345 for state in self.worker_states.iter() {
346 let local = state.local_epoch.load(Ordering::Acquire);
347 if local != 0 && local < min_active_epoch {
348 min_active_epoch = local;
349 }
350 }
351
352 self.garbage_queue.retain(|&(ptr, epoch)| {
353 if epoch < min_active_epoch {
354 unsafe { drop(Box::from_raw(ptr)) };
355 false
356 } else {
357 true
358 }
359 });
360 }
361
362 while let Some(batch) = self.hit_rx.try_recv() {
364 for &g_idx in batch.iter() {
365 if g_idx < self.arena.capacity {
366 self.hit_accumulator.push(g_idx);
367 }
368 }
369 if self.hit_accumulator.len() >= 8192 {
370 break;
371 }
372 }
373
374 if !self.hit_accumulator.is_empty() {
376 self.hit_accumulator.sort_unstable();
377
378 for &g_idx in &self.hit_accumulator {
379 self.arena.set_rank(g_idx, MAX_RANK);
381
382 let hash = self.arena.get_hash(g_idx);
383
384 let ptr = self.cache.nodes[g_idx].load(Ordering::Acquire);
386 if !ptr.is_null() && self.t1.load_slot(hash) != ptr {
387 self.t1.store_slot(hash, ptr);
388 }
389 }
390
391 self.hit_accumulator.clear();
392 }
393
394 if self.arena.free_list_len() < self.arena.capacity / 10 {
395 self.evict_batch();
396 }
397
398 let cold_start = self.arena.free_list_len() > self.arena.capacity / 20;
399 if self.is_cold_start.load(Ordering::Relaxed) != cold_start {
400 self.is_cold_start.store(cold_start, Ordering::Relaxed);
401 }
402 }
403
404 fn evict_batch(&mut self) {
407 let count = 128;
408 let avg = (self.arena.count_sum() / self.arena.capacity as u64) as u8;
409 let threshold = avg.max(1);
410
411 for _ in 0..count {
412 if self.arena.free_list_len() > self.arena.capacity / 10 {
413 break;
414 }
415
416 let idx = self.arena.cursor();
417 let r = self.arena.get_rank(idx);
418
419 if r <= threshold {
420 let hash = self.arena.get_hash(idx);
422 let tag = (hash >> 48) as u16;
423
424 let old_ptr =
425 self.cache.nodes[idx].swap(core::ptr::null_mut(), Ordering::Release);
426 if !old_ptr.is_null() {
427 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
428 self.garbage_queue.push((old_ptr, epoch));
429 self.t1.clear_if_matches(hash, old_ptr);
430 self.t2.clear_if_matches(hash, old_ptr);
431 }
432
433 self.cache.index_remove(hash, tag, idx);
434
435 self.admission.record_death(hash);
439 self.arena.push_free_slot(idx);
440 self.arena.set_rank(idx, 0);
441 } else {
442 self.arena.decrement_rank(idx);
444 }
445 self.arena.advance_cursor();
446 }
447 }
448}
449
450impl<K, V, S> Drop for Daemon<K, V, S> {
451 fn drop(&mut self) {
452 for &(ptr, _) in self.garbage_queue.iter() {
453 if !ptr.is_null() {
454 unsafe {
455 let _ = Box::from_raw(ptr);
456 }
457 }
458 }
459 }
460}
461
462pub struct AdmissionFilter {
475 pub ghost_mask: usize,
476 pub ghost_set: ArcSlice<AtomicU16>,
477}
478
479impl AdmissionFilter {
480 pub fn new(capacity: usize) -> Self {
483 let ghost_size = capacity.max(256);
486
487 let mut ghost_vec = Vec::with_capacity(ghost_size);
488 for _ in 0..ghost_size {
489 ghost_vec.push(AtomicU16::new(0));
490 }
491
492 Self {
493 ghost_mask: ghost_size - 1,
494 ghost_set: new_arc_slice(ghost_vec),
495 }
496 }
497
498 #[inline(always)]
500 pub fn record_death(&self, hash: u64) {
501 let fp = (hash >> 48) as u16;
502 let idx = (hash as usize) & self.ghost_mask;
503 self.ghost_set[idx].store(fp, Ordering::Relaxed);
504 }
505
506 #[inline(always)]
509 pub fn check_ghost(&self, hash: u64) -> bool {
510 let fp = (hash >> 48) as u16;
511 let ghost_idx = (hash as usize) & self.ghost_mask;
512 self.ghost_set[ghost_idx].load(Ordering::Relaxed) == fp
513 }
514
515 pub fn clear(&self) {
516 for val in self.ghost_set.iter() {
517 val.store(0, Ordering::Relaxed);
518 }
519 }
520}