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 BatchInsert(Vec<(K, V, u64)>),
27 Remove(K, u64),
29 Clear(Arc<OneshotAck>),
31 Sync(Arc<OneshotAck>),
33 Shutdown,
35}
36
37pub struct Daemon<K, V, S> {
40 pub hasher: S,
41 pub arena: Arena,
42 pub t1: Arc<T1<K, V>>,
43 pub t2: Arc<T2<K, V>>,
44 pub cache: Arc<Cache<K, V>>,
45 pub cmd_rx: Arc<LossyQueue<Command<K, V>>>,
46 pub hit_rx: Arc<LossyQueue<[usize; 64]>>,
47 pub epoch: Arc<AtomicU32>,
48 pub poll_us: u64,
51 pub admission: Arc<AdmissionFilter>,
52 pub hit_accumulator: Vec<usize>,
54 pub last_decay_epoch: u32,
55 pub garbage_queue: Vec<(*mut Node<K, V>, usize)>,
56 pub worker_states: ArcSlice<WorkerState>,
57 pub daemon_tick: Arc<AtomicTick>,
61 pub is_cold_start: Arc<AtomicBool>,
63}
64
65unsafe impl<K: Send, V: Send, S: Send> Send for Daemon<K, V, S> {}
66
67impl<K, V, S> Daemon<K, V, S>
68where
69 K: Hash + Eq + Send + Sync + Clone + 'static,
70 V: Send + Sync + Clone + 'static,
71 S: BuildHasher + Clone + Send + 'static,
72{
73 #[allow(clippy::too_many_arguments)]
74 pub fn new(
75 hasher: S,
76 capacity: usize,
77 t1: Arc<T1<K, V>>,
78 t2: Arc<T2<K, V>>,
79 cache: Arc<Cache<K, V>>,
80 cmd_rx: Arc<LossyQueue<Command<K, V>>>,
81 hit_rx: Arc<LossyQueue<[usize; 64]>>,
82 epoch: Arc<AtomicU32>,
83 duration: u32,
84 poll_us: u64,
85 worker_states: ArcSlice<WorkerState>,
86 daemon_tick: Arc<AtomicTick>,
87 is_cold_start: Arc<AtomicBool>,
88 ) -> Self {
89 let _ = duration; Self {
91 hasher,
92 arena: Arena::new(capacity),
93 t1,
94 t2,
95 cache,
96 cmd_rx,
97 hit_rx,
98 epoch,
99 poll_us,
100 admission: Arc::new(AdmissionFilter::new(capacity)),
101 hit_accumulator: Vec::with_capacity(8192),
102 last_decay_epoch: 0,
103 garbage_queue: Vec::new(),
104 worker_states,
105 daemon_tick,
106 is_cold_start,
107 }
108 }
109
110 pub fn run(mut self) {
121 #[cfg(feature = "std")]
122 let mut last_epoch_tick = std::time::Instant::now();
123
124 loop {
125 let mut processed = 0u32;
127 loop {
128 match self.cmd_rx.try_recv() {
129 Some(Command::Shutdown) => return,
130 Some(cmd) => {
131 self.process_cmd(cmd);
132 processed += 1;
133 if processed >= 8192 {
134 break;
135 }
136 }
137 None => break,
138 }
139 }
140
141 #[cfg(feature = "std")]
145 {
146 let now = std::time::Instant::now();
147 if now.duration_since(last_epoch_tick)
148 >= std::time::Duration::from_millis(100)
149 {
150 self.epoch.fetch_add(1, Ordering::Relaxed);
151 last_epoch_tick = now;
152 }
153 }
154 #[cfg(not(feature = "std"))]
155 {
156 let tick = self.daemon_tick.load(Ordering::Relaxed);
157 if tick % 100 == 0 {
158 self.epoch.fetch_add(1, Ordering::Relaxed);
159 }
160 }
161
162 self.maintenance();
164
165 #[cfg(any(feature = "loom", loom))]
167 {
168 if processed > 0 {
169 self.daemon_tick.fetch_add(1, Ordering::Relaxed);
170 }
171 }
172 #[cfg(not(any(feature = "loom", loom)))]
173 {
174 self.daemon_tick.fetch_add(1, Ordering::Relaxed);
175 }
176
177 if processed == 0 {
179 #[cfg(any(feature = "loom", loom))]
180 loom::thread::yield_now();
181 #[cfg(all(feature = "std", not(any(feature = "loom", loom))))]
182 std::thread::sleep(std::time::Duration::from_micros(self.poll_us));
183 #[cfg(not(feature = "std"))]
184 core::hint::spin_loop();
185 }
186 }
187 }
188
189 #[inline(always)]
190 fn process_cmd(&mut self, cmd: Command<K, V>) {
191 match cmd {
192 Command::Insert(k, v, hash) => self.handle_admission_insert(k, v, hash),
193 Command::BatchInsert(batch) => {
194 for (k, v, hash) in batch {
195 self.handle_admission_insert(k, v, hash);
196 }
197 }
198 Command::Remove(k, hash) => self.handle_remove(k, hash),
199 Command::Clear(ack) => {
200 self.handle_clear();
201 ack.signal();
202 }
203 Command::Sync(ack) => {
204 self.maintenance();
205 ack.signal();
206 }
207 Command::Shutdown => unreachable!("handled in run()"),
208 }
209 }
210
211 fn handle_admission_insert(&mut self, k: K, v: V, hash: u64) {
215 let cold_start = self.arena.free_list_len() > self.arena.capacity / 20;
216 self.is_cold_start.store(cold_start, Ordering::Relaxed);
217 if cold_start || self.admission.check_ghost(hash) {
218 self.handle_insert_with_hash(k, v, hash);
219 }
220 }
221
222 fn handle_insert_with_hash(&mut self, k: K, v: V, hash: u64) {
223 let tag = (hash >> 48) as u16;
224
225 let global_idx = if let Some(existing_idx) = self.cache.index_probe(hash, tag) {
227 existing_idx
228 } else {
229 if self.arena.free_list_empty() {
231 self.evict_batch();
232 }
233 if let Some(new_idx) = self.arena.pop_free_slot() {
234 new_idx
235 } else {
236 return; }
238 };
239
240 let entry = (tag as u64) << 48 | (global_idx as u64 & 0x0000_FFFF_FFFF_FFFF);
241
242 let node_ptr = Box::into_raw(Box::new(Node {
243 key: k,
244 value: v,
245 expire_at: self.epoch.load(Ordering::Relaxed) + self.get_duration(),
246 g_idx: global_idx as u32,
247 }));
248
249 let old_ptr = self.cache.nodes[global_idx].swap(node_ptr, Ordering::Release);
250 if !old_ptr.is_null() {
251 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
252 self.garbage_queue.push((old_ptr, epoch));
253 }
254
255 self.cache.index_store(hash, tag, entry);
256 self.arena.set_hash(global_idx, hash);
257 self.arena.set_rank(global_idx, MAX_RANK);
259 }
260
261 fn get_duration(&self) -> u32 {
262 10
265 }
266
267 fn handle_remove(&mut self, _k: K, hash: u64) {
268 let tag = (hash >> 48) as u16;
269 if let Some(g_idx) = self.cache.index_probe(hash, tag) {
270 let old_ptr =
271 self.cache.nodes[g_idx].swap(core::ptr::null_mut(), Ordering::Release);
272 if !old_ptr.is_null() {
273 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
274 self.garbage_queue.push((old_ptr, epoch));
275 self.t1.clear_if_matches(hash, old_ptr);
276 self.t2.clear_if_matches(hash, old_ptr);
277 }
278 self.cache.index_remove(hash, tag, g_idx);
279 self.arena.set_rank(g_idx, 0); }
281 }
282
283 fn handle_clear(&mut self) {
284 self.cache.clear();
285 for i in 0..self.t1.len() {
286 self.t1.clear_at(i);
287 }
288 for i in 0..self.t2.len() {
289 self.t2.clear_at(i);
290 }
291 self.admission.clear();
292 self.arena.clear();
293 self.is_cold_start.store(true, Ordering::Relaxed);
294 }
295
296 fn maintenance(&mut self) {
297 if !self.garbage_queue.is_empty() {
299 let current_global = GLOBAL_EPOCH.load(Ordering::Relaxed);
300 GLOBAL_EPOCH.store(current_global + 1, Ordering::Release);
301
302 let mut min_active_epoch = current_global + 1;
303 for state in self.worker_states.iter() {
304 let local = state.local_epoch.load(Ordering::Acquire);
305 if local != 0 && local < min_active_epoch {
306 min_active_epoch = local;
307 }
308 }
309
310 self.garbage_queue.retain(|&(ptr, epoch)| {
311 if epoch < min_active_epoch {
312 unsafe { drop(Box::from_raw(ptr)) };
313 false
314 } else {
315 true
316 }
317 });
318 }
319
320 while let Some(batch) = self.hit_rx.try_recv() {
322 for &g_idx in batch.iter() {
323 if g_idx < self.arena.capacity {
324 self.hit_accumulator.push(g_idx);
325 }
326 }
327 if self.hit_accumulator.len() >= 8192 {
328 break;
329 }
330 }
331
332 if !self.hit_accumulator.is_empty() {
334 self.hit_accumulator.sort_unstable();
335
336 for &g_idx in &self.hit_accumulator {
337 self.arena.set_rank(g_idx, MAX_RANK);
339
340 let hash = self.arena.get_hash(g_idx);
341
342 let ptr = self.cache.nodes[g_idx].load(Ordering::Acquire);
344 if !ptr.is_null() && self.t1.load_slot(hash) != ptr {
345 self.t1.store_slot(hash, ptr);
346 }
347 }
348
349 self.hit_accumulator.clear();
350 }
351
352 if self.arena.free_list_len() < self.arena.capacity / 10 {
353 self.evict_batch();
354 }
355
356 let cold_start = self.arena.free_list_len() > self.arena.capacity / 20;
357 if self.is_cold_start.load(Ordering::Relaxed) != cold_start {
358 self.is_cold_start.store(cold_start, Ordering::Relaxed);
359 }
360 }
361
362 fn evict_batch(&mut self) {
365 let count = 128;
366 let avg = (self.arena.count_sum() / self.arena.capacity as u64) as u8;
367 let threshold = avg.max(1);
368
369 for _ in 0..count {
370 if self.arena.free_list_len() > self.arena.capacity / 10 {
371 break;
372 }
373
374 let idx = self.arena.cursor();
375 let r = self.arena.get_rank(idx);
376
377 if r <= threshold {
378 let hash = self.arena.get_hash(idx);
380 let tag = (hash >> 48) as u16;
381
382 let old_ptr =
383 self.cache.nodes[idx].swap(core::ptr::null_mut(), Ordering::Release);
384 if !old_ptr.is_null() {
385 let epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
386 self.garbage_queue.push((old_ptr, epoch));
387 self.t1.clear_if_matches(hash, old_ptr);
388 self.t2.clear_if_matches(hash, old_ptr);
389 }
390
391 self.cache.index_remove(hash, tag, idx);
392
393 self.admission.record_death(hash);
397 self.arena.push_free_slot(idx);
398 self.arena.set_rank(idx, 0);
399 } else {
400 self.arena.decrement_rank(idx);
402 }
403 self.arena.advance_cursor();
404 }
405 }
406}
407
408impl<K, V, S> Drop for Daemon<K, V, S> {
409 fn drop(&mut self) {
410 for &(ptr, _) in self.garbage_queue.iter() {
411 if !ptr.is_null() {
412 unsafe {
413 let _ = Box::from_raw(ptr);
414 }
415 }
416 }
417 }
418}
419
420pub struct AdmissionFilter {
433 pub ghost_mask: usize,
434 pub ghost_set: ArcSlice<AtomicU16>,
435}
436
437impl AdmissionFilter {
438 pub fn new(capacity: usize) -> Self {
441 let ghost_size = capacity.max(256);
444
445 let mut ghost_vec = Vec::with_capacity(ghost_size);
446 for _ in 0..ghost_size {
447 ghost_vec.push(AtomicU16::new(0));
448 }
449
450 Self {
451 ghost_mask: ghost_size - 1,
452 ghost_set: new_arc_slice(ghost_vec),
453 }
454 }
455
456 #[inline(always)]
458 pub fn record_death(&self, hash: u64) {
459 let fp = (hash >> 48) as u16;
460 let idx = (hash as usize) & self.ghost_mask;
461 self.ghost_set[idx].store(fp, Ordering::Relaxed);
462 }
463
464 #[inline(always)]
467 pub fn check_ghost(&self, hash: u64) -> bool {
468 let fp = (hash >> 48) as u16;
469 let ghost_idx = (hash as usize) & self.ghost_mask;
470 self.ghost_set[ghost_idx].load(Ordering::Relaxed) == fp
471 }
472
473 pub fn clear(&self) {
474 for val in self.ghost_set.iter() {
475 val.store(0, Ordering::Relaxed);
476 }
477 }
478}