clawft_kernel/
chain_rule_hash.rs1use std::sync::Mutex;
13use std::sync::atomic::{AtomicBool, AtomicU64, Ordering, fence};
14
15#[derive(Debug, Default)]
17pub struct RuleHashCell {
18 seq: AtomicU64,
20 present: AtomicBool,
21 words: [AtomicU64; 4],
22 writers: Mutex<()>,
24}
25
26impl RuleHashCell {
27 pub fn new() -> Self {
29 Self::default()
30 }
31
32 pub fn set(&self, hash: Option<[u8; 32]>) {
34 let _w = self.writers.lock().unwrap_or_else(|e| e.into_inner());
35 self.seq.fetch_add(1, Ordering::Relaxed); fence(Ordering::Release); let h = hash.unwrap_or([0; 32]);
38 for (i, w) in self.words.iter().enumerate() {
39 let mut b = [0u8; 8];
40 b.copy_from_slice(&h[i * 8..i * 8 + 8]);
41 w.store(u64::from_le_bytes(b), Ordering::Relaxed);
42 }
43 self.present.store(hash.is_some(), Ordering::Relaxed);
44 self.seq.fetch_add(1, Ordering::Release); }
46
47 pub fn get(&self) -> Option<[u8; 32]> {
50 let mut spins = 0u32;
51 loop {
52 let s1 = self.seq.load(Ordering::Acquire);
53 if s1 & 1 == 1 {
54 spins += 1;
55 if spins < 16 {
56 std::hint::spin_loop();
57 } else {
58 std::thread::yield_now();
59 }
60 continue;
61 }
62 let mut out = [0u8; 32];
63 for (i, w) in self.words.iter().enumerate() {
64 out[i * 8..i * 8 + 8].copy_from_slice(&w.load(Ordering::Relaxed).to_le_bytes());
65 }
66 let present = self.present.load(Ordering::Relaxed);
67 fence(Ordering::Acquire);
68 if self.seq.load(Ordering::Relaxed) == s1 {
69 return present.then_some(out);
70 }
71 }
72 }
73
74 pub fn provider(self: &std::sync::Arc<Self>) -> crate::chain::RuleHashProvider {
76 let cell = std::sync::Arc::clone(self);
77 std::sync::Arc::new(move || cell.get())
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn set_get_clear() {
87 let c = RuleHashCell::new();
88 assert_eq!(c.get(), None);
89 c.set(Some([7; 32]));
90 assert_eq!(c.get(), Some([7; 32]));
91 c.set(None);
92 assert_eq!(c.get(), None);
93 }
94
95 #[test]
96 fn readers_never_see_a_torn_value() {
97 let c = std::sync::Arc::new(RuleHashCell::new());
98 let w = {
99 let c = c.clone();
100 std::thread::spawn(move || {
101 for i in 0..20_000u32 {
102 c.set(Some([(i % 251) as u8; 32]));
103 }
104 })
105 };
106 for _ in 0..20_000 {
107 if let Some(h) = c.get() {
108 assert!(h.iter().all(|b| *b == h[0]), "torn read {h:?}");
109 }
110 }
111 w.join().unwrap();
112 }
113}