1#[cfg(not(feature = "std"))]
2use alloc::{boxed::Box, vec::Vec};
3
4use crate::sync::{Arc, ArcSlice};
5use crate::sync::atomic::{AtomicBool, AtomicU32, Ordering};
6use crate::sync::index_types::AtomicTick;
7use core::hash::{Hash, BuildHasher};
8
9use crate::storage::Cache;
10use crate::filters::{T1, T2};
11use crate::lossy_queue::{LossyQueue, OneshotAck};
12use crate::cache::WorkerState;
13use crate::core_cache::CoreCache;
14
15pub enum Command<K, V> {
18 Insert(K, V, u64, bool),
20 BatchInsert(Vec<(K, V, u64, bool)>),
22 InsertT1(K, V, u64),
24 Remove(K, u64),
26 Clear(Arc<OneshotAck>),
28 Sync(Arc<OneshotAck>),
30 Shutdown,
32}
33
34pub struct Daemon<K, V, S> {
37 pub hasher: S,
38 pub core: CoreCache<K, V>,
39 pub cmd_rx: Arc<LossyQueue<Command<K, V>>>,
40 pub hit_rx: Arc<LossyQueue<[usize; 64]>>,
41 pub poll_us: u64,
43 pub daemon_tick: Arc<AtomicTick>,
45}
46
47unsafe impl<K: Send, V: Send, S: Send> Send for Daemon<K, V, S> {}
48
49impl<K, V, S> Daemon<K, V, S>
50where
51 K: Hash + Eq + Send + Sync + Clone + 'static,
52 V: Send + Sync + Clone + 'static,
53 S: BuildHasher + Clone + Send + 'static,
54{
55 #[allow(clippy::too_many_arguments)]
56 pub fn new(
57 hasher: S,
58 capacity: usize,
59 t1: Arc<T1<K, V>>,
60 t2: Arc<T2<K, V>>,
61 cache: Arc<Cache<K, V>>,
62 cmd_rx: Arc<LossyQueue<Command<K, V>>>,
63 hit_rx: Arc<LossyQueue<[usize; 64]>>,
64 epoch: Arc<AtomicU32>,
65 duration: u32,
66 poll_us: u64,
67 worker_states: ArcSlice<WorkerState>,
68 daemon_tick: Arc<AtomicTick>,
69 is_cold_start: Arc<AtomicBool>,
70 ) -> Self {
71 Self {
72 hasher,
73 core: CoreCache::new(
74 capacity,
75 t1,
76 t2,
77 cache,
78 epoch,
79 duration,
80 worker_states,
81 is_cold_start,
82 ),
83 cmd_rx,
84 hit_rx,
85 poll_us,
86 daemon_tick,
87 }
88 }
89
90 pub fn run(mut self) {
92 #[cfg(feature = "std")]
93 let mut last_epoch_tick = std::time::Instant::now();
94
95 loop {
96 let mut processed = 0u32;
98 loop {
99 match self.cmd_rx.try_recv() {
100 Some(Command::Shutdown) => return,
101 Some(cmd) => {
102 self.process_cmd(cmd);
103 processed += 1;
104 if processed >= 8192 {
105 break;
106 }
107 }
108 None => break,
109 }
110 }
111
112 #[cfg(feature = "std")]
114 {
115 let now = std::time::Instant::now();
116 if now.duration_since(last_epoch_tick)
117 >= std::time::Duration::from_millis(100)
118 {
119 self.core.epoch.fetch_add(1, Ordering::Relaxed);
120 last_epoch_tick = now;
121 }
122 }
123 #[cfg(not(feature = "std"))]
124 {
125 let tick = self.daemon_tick.load(Ordering::Relaxed);
126 if tick % 100 == 0 {
127 self.core.epoch.fetch_add(1, Ordering::Relaxed);
128 }
129 }
130
131 while let Some(batch) = self.hit_rx.try_recv() {
133 self.core.process_hits(&batch);
134 if self.core.hit_accumulator.len() >= 8192 {
135 break;
136 }
137 }
138
139 self.core.maintenance();
141
142 #[cfg(any(feature = "loom", loom))]
144 {
145 if processed > 0 {
146 self.daemon_tick.fetch_add(1, Ordering::Relaxed);
147 }
148 }
149 #[cfg(not(any(feature = "loom", loom)))]
150 {
151 self.daemon_tick.fetch_add(1, Ordering::Relaxed);
152 }
153
154 if processed == 0 {
156 #[cfg(any(feature = "loom", loom))]
157 loom::thread::yield_now();
158 #[cfg(all(feature = "std", not(any(feature = "loom", loom))))]
159 std::thread::sleep(std::time::Duration::from_micros(self.poll_us));
160 #[cfg(not(feature = "std"))]
161 core::hint::spin_loop();
162 }
163 }
164 }
165
166 #[inline(always)]
167 fn process_cmd(&mut self, cmd: Command<K, V>) -> bool {
168 match cmd {
169 Command::Insert(k, v, hash, is_t1) => {
170 self.core.handle_admission_insert(k, v, hash, is_t1);
171 true
172 }
173 Command::BatchInsert(batch) => {
174 for (k, v, hash, is_t1) in batch {
175 self.core.handle_admission_insert(k, v, hash, is_t1);
176 }
177 true
178 }
179 Command::InsertT1(k, v, hash) => {
180 self.core.handle_insert_t1(k, v, hash);
181 true
182 }
183 Command::Remove(k, hash) => {
184 self.core.handle_remove(k, hash);
185 true
186 }
187 Command::Clear(ack) => {
188 self.core.handle_clear();
189 ack.signal();
190 true
191 }
192 Command::Sync(ack) => {
193 self.core.maintenance();
194 ack.signal();
195 true
196 }
197 Command::Shutdown => unreachable!("handled in run()"),
198 }
199 }
200}