metrale_telemetry/
seqcell.rs1use std::sync::atomic::{AtomicU64, Ordering, fence};
16
17#[derive(Debug)]
18pub struct SeqCell<const W: usize> {
19 seq: AtomicU64,
20 words: [AtomicU64; W],
21}
22
23impl<const W: usize> Default for SeqCell<W> {
24 fn default() -> Self {
25 Self::new()
26 }
27}
28
29impl<const W: usize> SeqCell<W> {
30 pub const fn new() -> Self {
31 Self {
32 seq: AtomicU64::new(0),
33 words: [const { AtomicU64::new(0) }; W],
34 }
35 }
36
37 pub fn store(&self, value: [u64; W]) {
41 let mut s = self.seq.load(Ordering::Relaxed);
42 loop {
43 if s & 1 == 1 {
44 std::hint::spin_loop();
45 s = self.seq.load(Ordering::Relaxed);
46 continue;
47 }
48 match self
49 .seq
50 .compare_exchange_weak(s, s + 1, Ordering::Acquire, Ordering::Relaxed)
51 {
52 Ok(_) => break,
53 Err(now) => s = now,
54 }
55 }
56 fence(Ordering::Release);
57 for (w, v) in self.words.iter().zip(value) {
58 w.store(v, Ordering::Relaxed);
59 }
60 self.seq.store(s + 2, Ordering::Release);
61 }
62
63 pub fn load(&self) -> (u64, [u64; W]) {
66 loop {
67 let s1 = self.seq.load(Ordering::Acquire);
68 if s1 & 1 == 1 {
69 std::hint::spin_loop();
70 continue;
71 }
72 let mut out = [0u64; W];
73 for (o, w) in out.iter_mut().zip(&self.words) {
74 *o = w.load(Ordering::Relaxed);
75 }
76 fence(Ordering::Acquire);
77 if self.seq.load(Ordering::Relaxed) == s1 {
78 return (s1 / 2, out);
79 }
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use std::sync::Arc;
88 use std::sync::atomic::AtomicBool;
89
90 #[test]
91 fn a_fresh_cell_reports_no_stores() {
92 let c = SeqCell::<3>::new();
93 assert_eq!(c.load(), (0, [0; 3]));
94 c.store([1, 2, 3]);
95 assert_eq!(c.load(), (1, [1, 2, 3]));
96 }
97
98 #[test]
102 fn concurrent_readers_never_see_a_torn_value() {
103 const W: usize = 15;
104 let cell = Arc::new(SeqCell::<W>::new());
105 let stop = Arc::new(AtomicBool::new(false));
106 let writers: Vec<_> = (0..2u64)
107 .map(|id| {
108 let cell = cell.clone();
109 std::thread::spawn(move || {
110 for k in 0..100_000u64 {
111 let v = k * 2 + id + 1;
112 cell.store([v; W]);
113 }
114 })
115 })
116 .collect();
117 let readers: Vec<_> = (0..4)
118 .map(|_| {
119 let (cell, stop) = (cell.clone(), stop.clone());
120 std::thread::spawn(move || {
121 let mut reads = 0u64;
122 while !stop.load(Ordering::Relaxed) {
123 let (_, w) = cell.load();
124 assert!(w.iter().all(|x| *x == w[0]), "torn read: {w:?}");
125 reads += 1;
126 }
127 reads
128 })
129 })
130 .collect();
131 for w in writers {
132 w.join().unwrap();
133 }
134 stop.store(true, Ordering::Relaxed);
135 let reads: u64 = readers.into_iter().map(|r| r.join().unwrap()).sum();
136 assert!(reads > 0, "the readers ran");
137 assert_eq!(cell.load().0, 200_000, "every store is counted once");
138 }
139}