moka/common/concurrent/
entry_info.rs
1use std::sync::atomic::{self, AtomicBool, AtomicU16, AtomicU32, Ordering};
2
3use super::{AccessTime, KeyHash};
4use crate::common::time::{AtomicInstant, Instant};
5
6#[derive(Debug)]
7pub(crate) struct EntryInfo<K> {
8 key_hash: KeyHash<K>,
9 is_admitted: AtomicBool,
13 entry_gen: AtomicU16,
16 policy_gen: AtomicU16,
20 last_accessed: AtomicInstant,
21 last_modified: AtomicInstant,
22 expiration_time: AtomicInstant,
23 policy_weight: AtomicU32,
24}
25
26impl<K> EntryInfo<K> {
27 #[inline]
28 pub(crate) fn new(key_hash: KeyHash<K>, timestamp: Instant, policy_weight: u32) -> Self {
29 #[cfg(feature = "unstable-debug-counters")]
30 super::debug_counters::InternalGlobalDebugCounters::entry_info_created();
31
32 Self {
33 key_hash,
34 is_admitted: AtomicBool::default(),
35 entry_gen: AtomicU16::new(1),
37 policy_gen: AtomicU16::new(0),
38 last_accessed: AtomicInstant::new(timestamp),
39 last_modified: AtomicInstant::new(timestamp),
40 expiration_time: AtomicInstant::default(),
41 policy_weight: AtomicU32::new(policy_weight),
42 }
43 }
44
45 #[inline]
46 pub(crate) fn key_hash(&self) -> &KeyHash<K> {
47 &self.key_hash
48 }
49
50 #[inline]
51 pub(crate) fn is_admitted(&self) -> bool {
52 self.is_admitted.load(Ordering::Acquire)
53 }
54
55 #[inline]
56 pub(crate) fn set_admitted(&self, value: bool) {
57 self.is_admitted.store(value, Ordering::Release);
58 }
59
60 #[inline]
65 pub(crate) fn is_dirty(&self) -> bool {
66 let result =
67 self.entry_gen.load(Ordering::Relaxed) != self.policy_gen.load(Ordering::Relaxed);
68 atomic::fence(Ordering::Acquire);
69 result
70 }
71
72 #[inline]
73 pub(crate) fn entry_gen(&self) -> u16 {
74 self.entry_gen.load(Ordering::Acquire)
75 }
76
77 #[inline]
79 pub(crate) fn incr_entry_gen(&self) -> u16 {
80 let prev = self.entry_gen.fetch_add(1, Ordering::AcqRel);
82 prev.wrapping_add(1)
84 }
85
86 #[inline]
88 pub(crate) fn set_policy_gen(&self, value: u16) {
89 let g = &self.policy_gen;
90 loop {
91 let current = g.load(Ordering::Acquire);
92
93 if current >= value || value.wrapping_sub(current) > u16::MAX / 2 {
98 break;
99 }
100
101 if g.compare_exchange_weak(current, value, Ordering::AcqRel, Ordering::Acquire)
103 .is_ok()
104 {
105 break;
106 }
107 }
108 }
109
110 #[inline]
111 pub(crate) fn policy_weight(&self) -> u32 {
112 self.policy_weight.load(Ordering::Acquire)
113 }
114
115 pub(crate) fn set_policy_weight(&self, size: u32) {
116 self.policy_weight.store(size, Ordering::Release);
117 }
118
119 #[inline]
120 pub(crate) fn expiration_time(&self) -> Option<Instant> {
121 self.expiration_time.instant()
122 }
123
124 pub(crate) fn set_expiration_time(&self, time: Option<Instant>) {
125 if let Some(t) = time {
126 self.expiration_time.set_instant(t);
127 } else {
128 self.expiration_time.clear();
129 }
130 }
131}
132
133#[cfg(feature = "unstable-debug-counters")]
134impl<K> Drop for EntryInfo<K> {
135 fn drop(&mut self) {
136 super::debug_counters::InternalGlobalDebugCounters::entry_info_dropped();
137 }
138}
139
140impl<K> AccessTime for EntryInfo<K> {
141 #[inline]
142 fn last_accessed(&self) -> Option<Instant> {
143 self.last_accessed.instant()
144 }
145
146 #[inline]
147 fn set_last_accessed(&self, timestamp: Instant) {
148 self.last_accessed.set_instant(timestamp);
149 }
150
151 #[inline]
152 fn last_modified(&self) -> Option<Instant> {
153 self.last_modified.instant()
154 }
155
156 #[inline]
157 fn set_last_modified(&self, timestamp: Instant) {
158 self.last_modified.set_instant(timestamp);
159 }
160}
161
162#[cfg(test)]
163mod test {
164 use super::EntryInfo;
165
166 #[cfg_attr(
172 not(all(rustver, any(target_os = "linux", target_os = "macos"))),
173 ignore
174 )]
175 #[test]
176 fn check_struct_size() {
177 use std::mem::size_of;
178
179 #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
180 enum TargetArch {
181 Linux64,
182 Linux32X86,
183 Linux32Arm,
184 Linux32Mips,
185 MacOS64,
186 }
187
188 use TargetArch::*;
189
190 #[allow(clippy::option_env_unwrap)]
191 let ver =
193 option_env!("RUSTC_SEMVER").expect("RUSTC_SEMVER env var was not set at compile time");
194 let arch = if cfg!(target_os = "linux") {
195 if cfg!(target_pointer_width = "64") {
196 Linux64
197 } else if cfg!(target_pointer_width = "32") {
198 if cfg!(target_arch = "x86") {
199 Linux32X86
200 } else if cfg!(target_arch = "arm") {
201 Linux32Arm
202 } else if cfg!(target_arch = "mips") {
203 Linux32Mips
204 } else {
205 unimplemented!();
206 }
207 } else {
208 unimplemented!();
209 }
210 } else if cfg!(target_os = "macos") {
211 MacOS64
212 } else {
213 panic!("Unsupported target architecture");
214 };
215
216 let expected_sizes = match arch {
217 Linux64 | Linux32Arm | Linux32Mips => vec![("1.51", 56)],
218 Linux32X86 => vec![("1.51", 48)],
219 MacOS64 => vec![("1.62", 56)],
220 };
221
222 let mut expected = None;
223 for (ver_str, size) in expected_sizes {
224 expected = Some(size);
225 if ver >= ver_str {
226 break;
227 }
228 }
229
230 if let Some(size) = expected {
231 assert_eq!(size_of::<EntryInfo<()>>(), size);
232 } else {
233 panic!("No expected size for {arch:?} with Rust version {ver}");
234 }
235 }
236}