cs431_homework/hazard_pointer/
hazard.rs1use core::ptr::{self, NonNull};
2#[cfg(not(feature = "check-loom"))]
3use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering, fence};
4use std::collections::HashSet;
5use std::fmt;
6
7#[cfg(feature = "check-loom")]
8use loom::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering, fence};
9
10use super::HAZARDS;
11
12pub struct Shield {
14 slot: NonNull<HazardSlot>,
15}
16
17impl Shield {
18 pub fn new(hazards: &HazardBag) -> Self {
20 let slot = hazards.acquire_slot().into();
21 Self { slot }
22 }
23
24 pub fn set<T>(&self, pointer: *mut T) {
26 todo!()
27 }
28
29 pub fn clear(&self) {
31 self.set(ptr::null_mut::<()>())
32 }
33
34 pub fn validate<T>(pointer: *mut T, src: &AtomicPtr<T>) -> Result<(), *mut T> {
39 todo!()
40 }
41
42 pub fn try_protect<T>(&self, pointer: *mut T, src: &AtomicPtr<T>) -> Result<(), *mut T> {
47 self.set(pointer);
48 Self::validate(pointer, src).inspect_err(|_| self.clear())
49 }
50
51 pub fn protect<T>(&self, src: &AtomicPtr<T>) -> *mut T {
55 let mut pointer = src.load(Ordering::Relaxed);
56 while let Err(new) = self.try_protect(pointer, src) {
57 pointer = new;
58 #[cfg(feature = "check-loom")]
59 loom::sync::atomic::spin_loop_hint();
60 }
61 pointer
62 }
63}
64
65impl Default for Shield {
66 fn default() -> Self {
67 Self::new(&HAZARDS)
68 }
69}
70
71impl Drop for Shield {
72 fn drop(&mut self) {
74 todo!()
75 }
76}
77
78impl fmt::Debug for Shield {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 f.debug_struct("Shield")
81 .field("slot address", &self.slot)
82 .field("slot data", unsafe { self.slot.as_ref() })
83 .finish()
84 }
85}
86
87#[derive(Debug)]
91pub struct HazardBag {
92 head: AtomicPtr<HazardSlot>,
93}
94
95#[derive(Debug)]
97struct HazardSlot {
98 active: AtomicBool,
100 hazard: AtomicPtr<()>,
102 next: *const HazardSlot,
104}
105
106impl HazardSlot {
107 fn new() -> Self {
108 todo!()
109 }
110}
111
112impl HazardBag {
113 #[cfg(not(feature = "check-loom"))]
114 pub const fn new() -> Self {
116 Self {
117 head: AtomicPtr::new(ptr::null_mut()),
118 }
119 }
120
121 #[cfg(feature = "check-loom")]
122 pub fn new() -> Self {
124 Self {
125 head: AtomicPtr::new(ptr::null_mut()),
126 }
127 }
128
129 fn acquire_slot(&self) -> &HazardSlot {
132 todo!()
133 }
134
135 fn try_acquire_inactive(&self) -> Option<&HazardSlot> {
137 todo!()
138 }
139
140 pub fn all_hazards(&self) -> HashSet<*mut ()> {
142 todo!()
143 }
144}
145
146impl Default for HazardBag {
147 fn default() -> Self {
148 Self::new()
149 }
150}
151
152impl Drop for HazardBag {
153 fn drop(&mut self) {
155 todo!()
156 }
157}
158
159unsafe impl Send for HazardSlot {}
160unsafe impl Sync for HazardSlot {}
161
162#[cfg(all(test, not(feature = "check-loom")))]
163mod tests {
164 use std::collections::HashSet;
165 use std::ops::Range;
166 use std::sync::Arc;
167 use std::sync::atomic::AtomicPtr;
168 use std::{mem, thread};
169
170 use super::{HazardBag, Shield};
171
172 const THREADS: usize = 8;
173 const VALUES: Range<usize> = 1..1024;
174
175 #[test]
177 fn all_hazards_protected() {
178 let hazard_bag = Arc::new(HazardBag::new());
179 (0..THREADS)
180 .map(|_| {
181 let hazard_bag = hazard_bag.clone();
182 thread::spawn(move || {
183 for data in VALUES {
184 let src = AtomicPtr::new(data as *mut ());
185 let shield = Shield::new(&hazard_bag);
186 let _ = shield.protect(&src);
187 mem::forget(shield);
189 }
190 })
191 })
192 .collect::<Vec<_>>()
193 .into_iter()
194 .for_each(|th| th.join().unwrap());
195 let all = hazard_bag.all_hazards();
196 let values = VALUES.map(|data| data as *mut ()).collect();
197 assert!(all.is_superset(&values))
198 }
199
200 #[test]
202 fn all_hazards_unprotected() {
203 let hazard_bag = Arc::new(HazardBag::new());
204 (0..THREADS)
205 .map(|_| {
206 let hazard_bag = hazard_bag.clone();
207 thread::spawn(move || {
208 for data in VALUES {
209 let src = AtomicPtr::new(data as *mut ());
210 let shield = Shield::new(&hazard_bag);
211 let _ = shield.protect(&src);
212 }
213 })
214 })
215 .collect::<Vec<_>>()
216 .into_iter()
217 .for_each(|th| th.join().unwrap());
218 let all = hazard_bag.all_hazards();
219 let values = VALUES.map(|data| data as *mut ()).collect();
220 let intersection: HashSet<_> = all.intersection(&values).collect();
221 assert!(intersection.is_empty())
222 }
223
224 #[test]
226 fn recycle_slots() {
227 let hazard_bag = HazardBag::new();
228 let shields = (0..1024)
230 .map(|_| Shield::new(&hazard_bag))
231 .collect::<Vec<_>>();
232 let old_slots = shields
234 .iter()
235 .map(|s| s.slot.as_ptr() as usize)
236 .collect::<HashSet<_>>();
237 drop(shields);
239
240 let shields = (0..128)
241 .map(|_| Shield::new(&hazard_bag))
242 .collect::<Vec<_>>();
243 let new_slots = shields
244 .iter()
245 .map(|s| s.slot.as_ptr() as usize)
246 .collect::<HashSet<_>>();
247
248 assert!(new_slots.is_subset(&old_slots));
250 }
251}