1#[cfg(not(shuttle))]
11pub mod sync {
12 pub use std::sync::atomic;
13 pub use std::sync::mpsc;
14 #[allow(unused_imports)]
15 pub use std::sync::{Arc, Barrier, Mutex, Weak};
16}
17
18#[cfg(not(shuttle))]
19pub mod thread {
20 #[allow(unused_imports)]
21 pub use std::thread::{JoinHandle, sleep, spawn};
22
23 pub fn spawn_named<F, T>(name: &str, f: F) -> JoinHandle<T>
26 where
27 F: FnOnce() -> T + Send + 'static,
28 T: Send + 'static,
29 {
30 std::thread::Builder::new()
31 .name(name.into())
32 .spawn(f)
33 .expect("failed to spawn thread")
34 }
35}
36
37#[cfg(not(shuttle))]
38#[macro_export]
39macro_rules! define_thread_local {
40 ($($tt:tt)*) => { std::thread_local! { $($tt)* } };
41}
42#[cfg(not(shuttle))]
43pub use crate::define_thread_local as thread_local;
44
45#[cfg(shuttle)]
48pub mod sync {
49 pub use shuttle::sync::atomic;
50 #[allow(unused_imports)]
51 pub use shuttle::sync::{Arc, Barrier, Mutex, Weak};
52
53 pub mod mpsc {
60 pub use shuttle::sync::mpsc::{RecvTimeoutError, SyncSender};
61
62 pub struct Receiver<T> {
63 inner: shuttle::sync::mpsc::Receiver<T>,
64 }
65
66 unsafe impl<T: Send> Send for Receiver<T> {}
69
70 impl<T> Receiver<T> {
71 pub fn recv_timeout(
72 &self,
73 _timeout: std::time::Duration,
74 ) -> Result<T, RecvTimeoutError> {
75 if shuttle::rand::thread_rng().gen_bool(0.8) {
78 match self.inner.try_recv() {
79 Ok(val) => Ok(val),
80 Err(shuttle::sync::mpsc::TryRecvError::Empty) => {
81 Err(RecvTimeoutError::Timeout)
82 }
83 Err(shuttle::sync::mpsc::TryRecvError::Disconnected) => {
84 Err(RecvTimeoutError::Disconnected)
85 }
86 }
87 } else {
88 self.inner
91 .recv()
92 .map_err(|_| RecvTimeoutError::Disconnected)
93 }
94 }
95
96 pub fn recv(&self) -> Result<T, shuttle::sync::mpsc::RecvError> {
97 self.inner.recv()
98 }
99 }
100
101 use shuttle::rand::Rng;
102
103 pub fn sync_channel<T>(bound: usize) -> (SyncSender<T>, Receiver<T>) {
105 let (tx, rx) = shuttle::sync::mpsc::sync_channel(bound);
106 (tx, Receiver { inner: rx })
107 }
108 }
109}
110
111#[cfg(shuttle)]
112pub mod thread {
113 #[allow(unused_imports)]
114 pub use shuttle::thread::{JoinHandle, sleep, spawn};
115
116 pub fn spawn_named<F, T>(_name: &str, f: F) -> JoinHandle<T>
117 where
118 F: FnOnce() -> T + Send + 'static,
119 T: Send + 'static,
120 {
121 spawn(f)
122 }
123}
124
125#[cfg(shuttle)]
126#[macro_export]
127macro_rules! define_thread_local {
128 ($($tt:tt)*) => { shuttle::thread_local! { $($tt)* } };
129}
130#[cfg(shuttle)]
131pub use crate::define_thread_local as thread_local;
132
133#[cfg(not(shuttle))]
134pub mod fs {
135 use std::io::{self, Write};
136 use std::path::Path;
137
138 pub fn create_dir_all(path: &Path) -> io::Result<()> {
139 std::fs::create_dir_all(path)
140 }
141 pub fn rename(from: &Path, to: &Path) -> io::Result<()> {
142 std::fs::rename(from, to)
143 }
144 pub fn remove_file(path: &Path) -> io::Result<()> {
145 std::fs::remove_file(path)
146 }
147 pub fn remove_dir(path: &Path) -> io::Result<()> {
148 std::fs::remove_dir(path)
149 }
150 pub fn read_dir(path: &Path) -> io::Result<std::fs::ReadDir> {
151 std::fs::read_dir(path)
152 }
153 pub fn metadata(path: &Path) -> io::Result<std::fs::Metadata> {
154 std::fs::metadata(path)
155 }
156 pub fn read(path: &Path) -> io::Result<Vec<u8>> {
157 std::fs::read(path)
158 }
159
160 #[derive(Debug)]
162 pub struct File(std::fs::File);
163
164 impl File {
165 pub fn create(path: &Path) -> io::Result<File> {
166 std::fs::File::create(path).map(File)
167 }
168 }
169
170 impl Write for File {
171 #[inline]
172 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
173 self.0.write(buf)
174 }
175 #[inline]
176 fn flush(&mut self) -> io::Result<()> {
177 self.0.flush()
178 }
179 }
180}
181
182#[cfg(shuttle)]
183pub mod fs {
184 use std::cell::Cell;
185 use std::io::{self, ErrorKind, Write};
186 use std::path::Path;
187
188 use shuttle::rand::Rng;
189
190 #[derive(Clone, Copy, Debug)]
192 pub enum FaultPolicy {
193 None,
195 FailAll,
197 FailProb(f64),
200 }
201
202 std::thread_local! {
203 static FAULT: Cell<FaultPolicy> = const { Cell::new(FaultPolicy::None) };
204 }
205
206 #[must_use]
209 pub fn set_fault(policy: FaultPolicy) -> FaultGuard {
210 let prev = FAULT.with(|f| f.replace(policy));
211 FaultGuard { prev }
212 }
213
214 pub struct FaultGuard {
215 prev: FaultPolicy,
216 }
217
218 impl Drop for FaultGuard {
219 fn drop(&mut self) {
220 FAULT.with(|f| f.set(self.prev));
221 }
222 }
223
224 fn check() -> io::Result<()> {
225 let fail = match FAULT.with(|f| f.get()) {
226 FaultPolicy::None => false,
227 FaultPolicy::FailAll => true,
228 FaultPolicy::FailProb(p) => shuttle::rand::thread_rng().gen_bool(p),
229 };
230 if fail {
231 Err(io::Error::from(ErrorKind::PermissionDenied))
232 } else {
233 Ok(())
234 }
235 }
236
237 pub fn create_dir_all(path: &Path) -> io::Result<()> {
238 check()?;
239 std::fs::create_dir_all(path)
240 }
241 pub fn rename(from: &Path, to: &Path) -> io::Result<()> {
242 check()?;
243 std::fs::rename(from, to)
244 }
245 pub fn remove_file(path: &Path) -> io::Result<()> {
246 check()?;
247 std::fs::remove_file(path)
248 }
249 pub fn remove_dir(path: &Path) -> io::Result<()> {
250 check()?;
251 std::fs::remove_dir(path)
252 }
253 pub fn read_dir(path: &Path) -> io::Result<std::fs::ReadDir> {
254 check()?;
255 std::fs::read_dir(path)
256 }
257 pub fn metadata(path: &Path) -> io::Result<std::fs::Metadata> {
258 check()?;
259 std::fs::metadata(path)
260 }
261 pub fn read(path: &Path) -> io::Result<Vec<u8>> {
262 check()?;
263 std::fs::read(path)
264 }
265
266 #[derive(Debug)]
269 pub struct File(std::fs::File);
270
271 impl File {
272 pub fn create(path: &Path) -> io::Result<File> {
273 std::fs::File::create(path).map(File)
274 }
275 }
276
277 impl Write for File {
278 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
279 check()?;
280 self.0.write(buf)
281 }
282 fn flush(&mut self) -> io::Result<()> {
283 check()?;
284 self.0.flush()
285 }
286 }
287}
288
289#[cfg(not(shuttle))]
295pub struct BoundedQueue<T> {
296 inner: crossbeam_queue::ArrayQueue<T>,
297}
298
299#[cfg(not(shuttle))]
300impl<T> BoundedQueue<T> {
301 pub fn new(capacity: usize) -> Self {
302 Self {
303 inner: crossbeam_queue::ArrayQueue::new(capacity),
304 }
305 }
306
307 pub fn force_push(&self, value: T) -> Option<T> {
309 self.inner.force_push(value)
310 }
311
312 pub fn pop(&self) -> Option<T> {
313 self.inner.pop()
314 }
315}
316
317#[cfg(shuttle)]
318pub struct BoundedQueue<T> {
319 inner: shuttle::sync::Mutex<std::collections::VecDeque<T>>,
320 capacity: usize,
321}
322
323#[cfg(shuttle)]
324impl<T> BoundedQueue<T> {
325 pub fn new(capacity: usize) -> Self {
326 Self {
327 inner: shuttle::sync::Mutex::new(std::collections::VecDeque::with_capacity(capacity)),
328 capacity,
329 }
330 }
331
332 pub fn force_push(&self, value: T) -> Option<T> {
333 let mut q = self.inner.lock().unwrap();
334 let evicted = if q.len() >= self.capacity {
335 q.pop_front()
336 } else {
337 None
338 };
339 q.push_back(value);
340 evicted
341 }
342
343 pub fn pop(&self) -> Option<T> {
344 self.inner.lock().unwrap().pop_front()
345 }
346}