refaults_vmo/
atomic_vec.rs1use std::cmp::min;
6use std::sync::OnceLock;
7use std::sync::atomic::{AtomicU64, Ordering};
8
9const BITS: u64 = u64::BITS as u64;
10
11fn new_words(nwords: u64) -> Box<[AtomicU64]> {
12 (0..nwords).map(|_| AtomicU64::new(0)).collect()
13}
14
15mod private {
16 pub trait Sealed {}
19}
20
21pub trait Overflow: private::Sealed + Default {
26 fn word(&self, index: u64) -> Option<&AtomicU64>;
29}
30
31#[derive(Default)]
33pub struct NoOverflow;
34
35impl private::Sealed for NoOverflow {}
36
37impl Overflow for NoOverflow {
38 fn word(&self, _index: u64) -> Option<&AtomicU64> {
39 None
40 }
41}
42
43#[derive(Default)]
51pub struct Growable {
52 head: OnceLock<Box<Segment>>,
53}
54
55impl private::Sealed for Growable {}
56
57impl Overflow for Growable {
58 fn word(&self, mut index: u64) -> Option<&AtomicU64> {
59 let nwords = index + 1;
63 let mut next = &self.head;
64 loop {
65 let segment = next.get_or_init(|| Box::new(Segment::new(nwords)));
68 match segment.words.get(index as usize) {
69 Some(word) => return Some(word),
70 None => index -= segment.words.len() as u64,
71 }
72 next = &segment.next;
73 }
74 }
75}
76
77struct Segment {
79 words: Box<[AtomicU64]>,
80 next: OnceLock<Box<Segment>>,
81}
82
83impl Segment {
84 fn new(nwords: u64) -> Self {
85 Self { words: new_words(nwords), next: OnceLock::new() }
86 }
87}
88
89pub struct AtomicBitVec<O: Overflow = NoOverflow> {
96 storage: Box<[AtomicU64]>,
98 overflow: O,
100}
101
102impl<O: Overflow> AtomicBitVec<O> {
103 pub fn new(nbits: u64) -> Self {
105 Self { storage: new_words(nbits.div_ceil(BITS)), overflow: O::default() }
106 }
107
108 pub fn test_and_set_range(&self, start_bit: u64, end_bit: u64) -> u64 {
111 if start_bit >= end_bit {
112 return 0;
113 }
114
115 let mut counter = 0;
116 let mut current_bit = start_bit;
117
118 while current_bit < end_bit {
119 let current_word_index = current_bit / BITS;
120 let current_word_start_bit = current_word_index * BITS;
121 let mask =
122 Self::get_mask(current_bit % BITS, min(end_bit - current_word_start_bit, BITS));
123 let old_word = self.word(current_word_index).fetch_or(mask, Ordering::Relaxed);
124 counter += (old_word & mask).count_ones();
125 current_bit = current_word_start_bit + BITS;
126 }
127
128 counter.into()
129 }
130
131 pub fn to_vec(&self, nbits: u64) -> Vec<bool> {
133 let fetch = |bit: u64| {
134 let bit_mask = 1 << (bit % BITS);
135 self.word(bit / BITS).load(Ordering::Relaxed) & bit_mask != 0
136 };
137 (0..nbits).map(fetch).collect()
138 }
139
140 fn word(&self, index: u64) -> &AtomicU64 {
143 match self.storage.get(index as usize) {
144 Some(word) => word,
145 None => self.overflow_word(index - self.storage.len() as u64),
146 }
147 }
148
149 #[cold]
152 fn overflow_word(&self, index: u64) -> &AtomicU64 {
153 self.overflow.word(index).expect("word index outside of the vector")
154 }
155
156 fn get_mask(start_bit: u64, end_bit: u64) -> u64 {
157 let left_mask = u64::MAX << start_bit;
158 let right_mask = u64::MAX >> (BITS - end_bit);
159 left_mask & right_mask
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_new() {
169 let vec = AtomicBitVec::<NoOverflow>::new(0);
170 assert_eq!(vec.storage.len(), 0);
171
172 let vec = AtomicBitVec::<NoOverflow>::new(1);
173 assert_eq!(vec.storage.len(), 1);
174 assert_eq!(vec.storage[0].load(Ordering::Relaxed), 0);
175
176 let vec = AtomicBitVec::<NoOverflow>::new(64);
177 assert_eq!(vec.storage.len(), 1);
178 assert_eq!(vec.storage[0].load(Ordering::Relaxed), 0);
179
180 let vec = AtomicBitVec::<NoOverflow>::new(65);
181 assert_eq!(vec.storage.len(), 2);
182 assert_eq!(vec.storage[0].load(Ordering::Relaxed), 0);
183 assert_eq!(vec.storage[1].load(Ordering::Relaxed), 0);
184 }
185
186 #[test]
187 fn test_size() {
188 assert!(size_of::<AtomicBitVec>() <= 16);
191 assert!(size_of::<AtomicBitVec<Growable>>() <= 32);
192 }
193
194 #[test]
195 fn test_test_and_set() {
196 let vec = AtomicBitVec::<NoOverflow>::new(320);
197
198 assert_eq!(vec.test_and_set_range(10, 20), 0);
200 assert_eq!(vec.test_and_set_range(10, 20), 10);
202
203 assert_eq!(vec.test_and_set_range(15, 25), 5);
205
206 assert_eq!(vec.test_and_set_range(60, 70), 0);
208 assert_eq!(vec.test_and_set_range(55, 75), 10);
210
211 assert_eq!(vec.test_and_set_range(50, 300), 20);
213 assert_eq!(vec.test_and_set_range(50, 300), 250);
214 }
215
216 #[test]
217 fn test_test_and_set2() {
218 let vec = AtomicBitVec::<NoOverflow>::new(320);
219 assert_eq!(vec.test_and_set_range(64, 128), 0);
220 assert_eq!(vec.test_and_set_range(64, 128), 64);
221 }
222
223 #[test]
224 fn test_test_and_set3() {
225 let vec = AtomicBitVec::<NoOverflow>::new(150);
226
227 assert_eq!(vec.test_and_set_range(0, 1), 0);
228 assert_eq!(vec.test_and_set_range(63, 64), 0);
229 assert_eq!(vec.test_and_set_range(0, 64), 2);
231 assert_eq!(vec.test_and_set_range(64, 65), 0);
232 assert_eq!(vec.test_and_set_range(63, 65), 2);
234
235 assert_eq!(vec.test_and_set_range(63, 128), 2);
237 assert_eq!(vec.test_and_set_range(63, 129), 65);
239 }
240
241 #[test]
243 #[should_panic]
244 fn test_and_set_out_of_bounds() {
245 let vec = AtomicBitVec::<NoOverflow>::new(10);
246 vec.test_and_set_range(64, 65);
247 }
248
249 #[test]
250 fn test_grow() {
251 let vec = AtomicBitVec::<Growable>::new(10);
253 vec.test_and_set_range(0, 10);
254 assert_eq!(vec.test_and_set_range(10, 64), 0);
255 assert_eq!(vec.test_and_set_range(0, 64), 64);
256
257 assert_eq!(vec.test_and_set_range(64, 65), 0);
259 assert_eq!(vec.test_and_set_range(0, 65), 65);
260 }
261
262 #[test]
263 fn test_grow_across_segments() {
264 let vec = AtomicBitVec::<Growable>::new(64);
265
266 assert_eq!(vec.test_and_set_range(60, 200), 0);
268
269 assert_eq!(vec.test_and_set_range(60, 200), 140);
271
272 assert_eq!(vec.test_and_set_range(0, 60), 0);
274 }
275
276 #[test]
277 fn test_concurrent_grow() {
278 const THREADS: u64 = 4;
279 const ROUNDS: u64 = 1000;
280
281 let vec = std::sync::Arc::new(AtomicBitVec::<Growable>::new(1));
282 let threads: Vec<_> = (0..THREADS)
283 .map(|_| {
284 let vec = vec.clone();
285 std::thread::spawn(move || {
286 let mut bits_set = 0;
289 for round in 1..=ROUNDS {
290 bits_set += round - vec.test_and_set_range(0, round);
291 }
292 bits_set
293 })
294 })
295 .collect();
296
297 let bits_set: u64 = threads.into_iter().map(|thread| thread.join().unwrap()).sum();
298
299 assert_eq!(bits_set, ROUNDS);
301 assert!(vec.to_vec(ROUNDS).into_iter().all(|bit| bit));
302 }
303}