1extern crate alloc;
2#[cfg(not(feature = "std"))]
3use alloc::{vec, vec::Vec, boxed::Box};
4
5use crate::cache_padded::CachePadded;
6use crate::daemon::{Command, Daemon};
7use crate::lossy_queue::{LossyQueue, OneshotAck};
8use crate::unsafe_core::{Cache, T1, T2, WorkerSlot};
9use ahash::RandomState;
10use core::hash::{BuildHasher, Hash};
11use crate::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
12use crate::sync::index_types::{AtomicTick, TickType};
13use crate::sync::{Arc, ArcSlice, new_arc_slice};
14use crate::config::Config;
15
16#[cfg(any(feature = "loom", loom))]
22loom::lazy_static! {
23 pub static ref GLOBAL_EPOCH: loom::sync::atomic::AtomicUsize = loom::sync::atomic::AtomicUsize::new(1);
24}
25
26#[cfg(not(any(feature = "loom", loom)))]
27pub static GLOBAL_EPOCH: AtomicUsize = AtomicUsize::new(1);
28
29pub struct WorkerState {
32 pub local_epoch: CachePadded<AtomicUsize>,
33}
34
35impl WorkerState {
36 pub fn new() -> Self {
37 Self {
38 local_epoch: CachePadded::new(AtomicUsize::new(0)),
39 }
40 }
41}
42
43use crate::spawner::{DaemonSpawner, DefaultSpawner};
44use crate::tls::{TlsProvider, DefaultTls};
45
46pub struct DualCacheFF<K, V, S = RandomState, Tls: TlsProvider = DefaultTls> {
49 pub hasher: S,
50 pub t1: Arc<T1<K, V>>,
51 pub t2: Arc<T2<K, V>>,
52 pub cache: Arc<Cache<K, V>>,
53 pub cmd_tx: Arc<LossyQueue<Command<K, V>>>,
54 pub hit_tx: Arc<LossyQueue<[usize; 64]>>,
55 pub epoch: Arc<AtomicU32>,
56 pub worker_states: ArcSlice<WorkerState>,
58 pub miss_buffers: ArcSlice<WorkerSlot<K, V>>,
60 pub daemon_tick: Arc<AtomicTick>,
63 pub flush_tick_threshold: TickType,
65 pub is_cold_start: Arc<AtomicBool>,
67 pub tls: Tls,
69}
70
71impl<K, V, S: Clone, Tls: TlsProvider + Clone> Clone for DualCacheFF<K, V, S, Tls> {
72 fn clone(&self) -> Self {
73 Self {
74 hasher: self.hasher.clone(),
75 t1: self.t1.clone(),
76 t2: self.t2.clone(),
77 cache: self.cache.clone(),
78 cmd_tx: self.cmd_tx.clone(),
79 hit_tx: self.hit_tx.clone(),
80 epoch: self.epoch.clone(),
81 worker_states: self.worker_states.clone(),
82 miss_buffers: self.miss_buffers.clone(),
83 daemon_tick: self.daemon_tick.clone(),
84 flush_tick_threshold: self.flush_tick_threshold,
85 is_cold_start: self.is_cold_start.clone(),
86 tls: self.tls.clone(),
87 }
88 }
89}
90
91#[cfg(any(feature = "std", feature = "loom", loom))]
94impl<K, V> DualCacheFF<K, V, RandomState, DefaultTls>
95where
96 K: Hash + Eq + Send + Sync + Clone + 'static,
97 V: Send + Sync + Clone + 'static,
98{
99 #[inline]
103 pub fn new(config: Config) -> Self {
104 Self::new_with_spawner(config, DefaultSpawner)
105 }
106}
107
108#[cfg(any(feature = "std", feature = "loom", loom))]
109impl<K, V, Tls> DualCacheFF<K, V, RandomState, Tls>
110where
111 K: Hash + Eq + Send + Sync + Clone + 'static,
112 V: Send + Sync + Clone + 'static,
113 Tls: TlsProvider + 'static,
114{
115 #[inline]
117 pub fn new_with_tls(config: Config, tls: Tls) -> Self {
118 Self::new_with_tls_and_spawner(config, tls, DefaultSpawner)
119 }
120}
121
122impl<K, V> DualCacheFF<K, V, RandomState, DefaultTls>
125where
126 K: Hash + Eq + Send + Sync + Clone + 'static,
127 V: Send + Sync + Clone + 'static,
128{
129 pub fn new_with_spawner<Sp: DaemonSpawner + 'static>(config: Config, spawner: Sp) -> Self {
131 let (cache, daemon) = Self::new_headless(config);
132 spawner.spawn(alloc::boxed::Box::new(move || daemon.run()));
133 cache
134 }
135
136 pub fn new_headless(config: Config) -> (Self, Daemon<K, V, RandomState>) {
149 Self::new_headless_with_tls(config, DefaultTls)
150 }
151}
152
153impl<K, V, Tls> DualCacheFF<K, V, RandomState, Tls>
154where
155 K: Hash + Eq + Send + Sync + Clone + 'static,
156 V: Send + Sync + Clone + 'static,
157 Tls: TlsProvider + 'static,
158{
159 pub fn new_headless_with_tls(
161 config: Config,
162 tls: Tls,
163 ) -> (Self, Daemon<K, V, RandomState>) {
164 let hasher = RandomState::new();
165 let t1 = Arc::new(T1::new(config.t1_slots));
166 let t2 = Arc::new(T2::new(config.t2_slots));
167 let cache = Arc::new(Cache::new(config.capacity));
168 let cmd_q: Arc<LossyQueue<Command<K, V>>> = Arc::new(LossyQueue::new(8192));
169 let hit_q: Arc<LossyQueue<[usize; 64]>> = Arc::new(LossyQueue::new(1024));
170 let epoch = Arc::new(AtomicU32::new(0));
171 let daemon_tick = Arc::new(AtomicTick::new(0));
172 let is_cold_start = Arc::new(AtomicBool::new(true));
173
174 let mut buffers = Vec::with_capacity(config.threads);
175 let mut states = Vec::with_capacity(config.threads);
176 for _ in 0..config.threads {
177 buffers.push(WorkerSlot::new());
178 states.push(WorkerState::new());
179 }
180 let miss_buffers = new_arc_slice(buffers);
181 let worker_states = new_arc_slice(states);
182
183 let daemon = Daemon::new(
184 hasher.clone(),
185 config.capacity,
186 t1.clone(),
187 t2.clone(),
188 cache.clone(),
189 cmd_q.clone(),
190 hit_q.clone(),
191 epoch.clone(),
192 config.duration,
193 config.poll_us,
194 worker_states.clone(),
195 daemon_tick.clone(),
196 is_cold_start.clone(),
197 );
198
199 let this = Self {
200 hasher,
201 t1,
202 t2,
203 cache,
204 cmd_tx: cmd_q,
205 hit_tx: hit_q,
206 epoch,
207 worker_states,
208 miss_buffers,
209 daemon_tick,
210 flush_tick_threshold: (config.poll_us as TickType).max(1),
211 is_cold_start,
212 tls,
213 };
214
215 (this, daemon)
216 }
217
218 pub fn new_with_tls_and_spawner<Sp: DaemonSpawner + 'static>(config: Config, tls: Tls, spawner: Sp) -> Self
220 {
221 let (cache, daemon) = Self::new_headless_with_tls(config, tls);
222 spawner.spawn(alloc::boxed::Box::new(move || daemon.run()));
223 cache
224 }
225
226}
227
228impl<K, V, S, Tls: TlsProvider> DualCacheFF<K, V, S, Tls>
231where
232 K: Hash + Eq + Send + Sync + Clone + 'static,
233 V: Send + Sync + Clone + 'static,
234 S: BuildHasher + Clone + Send + 'static,
235{
236 pub fn sync(&self) {
240 self.with_hit_buf(|state| {
242 if state.1 > 0_usize {
243 let _ = self.hit_tx.try_send(state.0);
244 state.1 = 0;
245 }
246 });
247
248 for slot in self.miss_buffers.iter() {
250 let buf: &mut crate::unsafe_core::BatchBuf<K, V> = slot.get_mut_safe();
251 if buf.len() > 0 {
252 let batch = buf.drain_to_vec();
253 let _ = self.cmd_tx.try_send(Command::BatchInsert(batch));
254 }
255 }
256
257 let ack = OneshotAck::new();
259 self.cmd_tx.send_blocking(Command::Sync(ack.clone()));
260 ack.wait();
261 }
262
263 pub fn get(&self, key: &K) -> Option<V> {
268 let hash = self.hash(key);
269 let current_epoch_cache = self.epoch.load(Ordering::Relaxed);
270
271 let mut id_opt = None;
273 let global_epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
274 self.with_worker_id(|id| {
275 if id < self.worker_states.len() {
276 self.worker_states[id]
277 .local_epoch
278 .store(global_epoch, Ordering::Relaxed);
279 id_opt = Some(id);
280 }
281 });
282
283 let has_epoch = id_opt.is_some() || {
284 #[cfg(not(feature = "std"))]
285 { true }
286 #[cfg(feature = "std")]
287 { false }
288 };
289
290 let mut res: Option<V> = None;
291 let mut hit_g_idx: Option<u32> = None;
292
293 if has_epoch {
294 if let Some(node) = self.t1.get_node(hash) {
296 if node.key == *key
297 && (node.expire_at == 0 || node.expire_at >= current_epoch_cache)
298 {
299 res = Some(node.value.clone());
300 hit_g_idx = Some(node.g_idx);
301 self.tls.with_warmup_state(&mut |s| *s = s.saturating_add(10));
302 }
303 }
304
305 if res.is_none() {
307 if let Some(node) = self.t2.get_node(hash) {
308 if node.key == *key
309 && (node.expire_at == 0 || node.expire_at >= current_epoch_cache)
310 {
311 res = Some(node.value.clone());
312 hit_g_idx = Some(node.g_idx);
313 self.tls.with_warmup_state(&mut |s| *s = s.saturating_sub(5));
314 }
315 }
316 }
317
318 if res.is_none() {
320 let tag = (hash >> 48) as u16;
321 if let Some(global_idx) = self.cache.index_probe(hash, tag) {
322 if let Some(v) = self
323 .cache
324 .node_get_full(global_idx, key, current_epoch_cache)
325 {
326 res = Some(v);
327 hit_g_idx = Some(global_idx as u32);
328 self.tls.with_warmup_state(&mut |s| *s = s.saturating_sub(10));
329 }
330 }
331 }
332 }
333
334 if let Some(id) = id_opt {
336 self.worker_states[id]
337 .local_epoch
338 .store(0, Ordering::Relaxed);
339 }
340
341 if let Some(g_idx) = hit_g_idx {
342 self.record_hit(g_idx as usize);
343 }
344
345 res
346 }
347
348 pub fn insert(&self, key: K, value: V) {
362 let hash = self.hash(&key);
363
364 let mut id_opt = None;
365 let is_cold = self.is_cold_start.load(Ordering::Relaxed);
366 let mut bypass = is_cold;
367
368 if !bypass {
369 let global_epoch = GLOBAL_EPOCH.load(Ordering::Relaxed);
372 self.with_worker_id(|id| {
373 if id < self.worker_states.len() {
374 self.worker_states[id]
375 .local_epoch
376 .store(global_epoch, Ordering::Relaxed);
377 id_opt = Some(id);
378 }
379 });
380
381 if id_opt.is_some() {
382 if let Some(node) = self.t1.get_node(hash) {
384 if node.key == key {
385 bypass = true;
386 }
387 }
388
389 if !bypass {
391 if let Some(node) = self.t2.get_node(hash) {
392 if node.key == key {
393 bypass = true;
394 }
395 }
396 }
397
398 if !bypass {
400 let tag = (hash >> 48) as u16;
401 if let Some(global_idx) = self.cache.index_probe(hash, tag) {
402 if let Some(node) = self.cache.get_node(global_idx) {
403 if node.key == key {
404 bypass = true;
405 }
406 }
407 }
408 }
409 }
410
411 if let Some(id) = id_opt {
413 self.worker_states[id]
414 .local_epoch
415 .store(0, Ordering::Relaxed);
416 }
417 }
418
419 let pass = if bypass {
420 true
421 } else {
422 self.with_l1_filter(|state| {
424 let idx = (hash as usize) & 4095_usize;
425 let val = state.0[idx];
426
427 state.1 += 1;
428 if state.1 >= 4096_usize {
429 for x in state.0.iter_mut() {
430 *x >>= 1;
431 }
432 state.1 = 0;
433 }
434
435 if val < 1_u8 {
436 state.0[idx] = 1;
437 false
438 } else {
439 if val < 2_u8 {
440 state.0[idx] = 2;
441 }
442 true
443 }
444 }).unwrap_or(true) };
446
447 if !pass {
448 return;
449 }
450
451 let mut warmup_state = 255;
452 self.tls.with_warmup_state(&mut |s| warmup_state = *s);
453 let is_t1 = warmup_state < 100;
454
455 let current_tick = self.daemon_tick.load(Ordering::Relaxed);
457 let mut should_time_flush = false;
458 self.with_last_flush_tick(|tick| {
459 should_time_flush = current_tick.wrapping_sub(*tick) >= self.flush_tick_threshold;
460 });
461
462 let mut option_kv = Some((key, value));
464 let pushed_to_buf = self.with_worker_id(|id| {
465 let (k, v) = option_kv.take().unwrap();
466 if id >= self.miss_buffers.len() {
467 let _ = self.cmd_tx.try_send(Command::Insert(k, v, hash, is_t1));
469 return;
470 }
471
472 let buf: &mut crate::unsafe_core::BatchBuf<K, V> = self.miss_buffers[id].get_mut_safe();
474 let capacity_flush = buf.push((k, v, hash, is_t1));
475
476 if capacity_flush || (should_time_flush && !buf.is_empty()) {
477 let batch = buf.drain_to_vec();
478 let _ = self.cmd_tx.try_send(Command::BatchInsert(batch));
479 self.with_last_flush_tick(|tick| {
480 *tick = current_tick;
481 });
482 }
483 });
484
485 if pushed_to_buf.is_none() {
486 if let Some((k, v)) = option_kv {
487 let _ = self.cmd_tx.try_send(Command::Insert(k, v, hash, is_t1));
488 }
489 }
490 }
491
492 pub fn begin_cold_start_session(&self) -> ColdStartSession<'_, K, V, S, Tls> {
494 ColdStartSession { cache: self }
495 }
496
497 pub fn remove(&self, key: &K) {
499 let hash = self.hash(key);
500
501 self.with_worker_id(|id| {
503 if id < self.miss_buffers.len() {
504 let buf: &mut crate::unsafe_core::BatchBuf<K, V> = self.miss_buffers[id].get_mut_safe();
505 if buf.len() > 0 {
506 let batch = buf.drain_to_vec();
507 let _ = self.cmd_tx.try_send(Command::BatchInsert(batch));
508 let tick = self.daemon_tick.load(Ordering::Relaxed);
509 self.with_last_flush_tick(|c| *c = tick);
510 }
511 }
512 });
513
514 self.cmd_tx.send_blocking(Command::Remove(key.clone(), hash));
515 }
516
517 pub fn clear(&self) {
519 let ack = OneshotAck::new();
520 self.cmd_tx.send_blocking(Command::Clear(ack.clone()));
521 ack.wait();
522 }
523
524 #[inline(always)]
527 fn with_worker_id<F, R>(&self, f: F) -> Option<R>
528 where
529 F: FnOnce(usize) -> R,
530 {
531 self.tls.get_worker_id().map(f)
532 }
533
534 #[inline(always)]
535 fn with_hit_buf<F, R>(&self, mut f: F) -> Option<R>
536 where
537 F: FnMut(&mut ([usize; 64], usize)) -> R,
538 {
539 let mut res = None;
540 self.tls.with_hit_buf(&mut |buf| {
541 res = Some(f(buf));
542 });
543 res
544 }
545
546 #[inline(always)]
547 fn with_l1_filter<F, R>(&self, mut f: F) -> Option<R>
548 where
549 F: FnMut(&mut ([u8; 4096], usize)) -> R,
550 {
551 let mut res = None;
552 self.tls.with_l1_filter(&mut |filter| {
553 res = Some(f(filter));
554 });
555 res
556 }
557
558 #[inline(always)]
559 fn with_last_flush_tick<F, R>(&self, mut f: F) -> Option<R>
560 where
561 F: FnMut(&mut TickType) -> R,
562 {
563 let mut res = None;
564 self.tls.with_last_flush_tick(&mut |tick| {
565 res = Some(f(tick));
566 });
567 res
568 }
569
570 #[inline(always)]
571 fn hash(&self, key: &K) -> u64 {
572 self.hasher.hash_one(key)
573 }
574
575 #[inline(always)]
580 fn record_hit(&self, global_idx: usize) {
581 let opt = self.with_hit_buf(|state| {
582 let idx = state.1;
583 state.0[idx] = global_idx;
584 state.1 += 1;
585 if state.1 == 64_usize {
586 let _ = self.hit_tx.try_send(state.0);
587 state.0 = [usize::MAX; 64];
588 state.1 = 0;
589 }
590 });
591
592 if opt.is_none() {
593 let mut batch = [usize::MAX; 64];
594 batch[0] = global_idx;
595 let _ = self.hit_tx.try_send(batch);
596 }
597 }
598}
599
600impl<K, V, S, Tls: TlsProvider> Drop for DualCacheFF<K, V, S, Tls> {
601 fn drop(&mut self) {
602 if Arc::strong_count(&self.cmd_tx) <= 2 {
603 let _ = self.cmd_tx.try_send(Command::Shutdown);
604 }
605 }
606}
607
608
609pub struct ColdStartSession<'a, K, V, S, Tls: TlsProvider> {
613 cache: &'a DualCacheFF<K, V, S, Tls>,
614}
615
616impl<'a, K, V, S, Tls: TlsProvider> ColdStartSession<'a, K, V, S, Tls>
617where
618 K: Hash + Eq + Send + Sync + Clone + 'static,
619 V: Send + Sync + Clone + 'static,
620 S: BuildHasher + Clone + Send + 'static,
621{
622 pub fn warmup(&self, key: K, value: V) {
626 let hash = self.cache.hash(&key);
627 let _ = self.cache.cmd_tx.try_send(Command::InsertT1(key, value, hash));
628 }
629}