loom.rs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. pub(crate) use self::inner::*;
  2. #[cfg(all(test, loom))]
  3. mod inner {
  4. pub(crate) mod atomic {
  5. pub use loom::sync::atomic::*;
  6. pub use std::sync::atomic::Ordering;
  7. }
  8. pub(crate) use loom::{cell::UnsafeCell, future, hint, sync, thread};
  9. use std::{cell::RefCell, fmt::Write};
  10. pub(crate) mod model {
  11. #[allow(unused_imports)]
  12. pub(crate) use loom::model::Builder;
  13. }
  14. std::thread_local! {
  15. static TRACE_BUF: RefCell<String> = RefCell::new(String::new());
  16. }
  17. pub(crate) fn traceln(args: std::fmt::Arguments) {
  18. let mut args = Some(args);
  19. TRACE_BUF
  20. .try_with(|buf| {
  21. let mut buf = buf.borrow_mut();
  22. let _ = buf.write_fmt(args.take().unwrap());
  23. let _ = buf.write_char('\n');
  24. })
  25. .unwrap_or_else(|_| println!("{}", args.take().unwrap()))
  26. }
  27. #[track_caller]
  28. pub(crate) fn run_builder(
  29. builder: loom::model::Builder,
  30. model: impl Fn() + Sync + Send + std::panic::UnwindSafe + 'static,
  31. ) {
  32. use std::{
  33. env, io,
  34. sync::{
  35. atomic::{AtomicBool, AtomicUsize, Ordering},
  36. Once,
  37. },
  38. };
  39. use tracing_subscriber::{filter::Targets, fmt, prelude::*};
  40. static IS_NOCAPTURE: AtomicBool = AtomicBool::new(false);
  41. static SETUP_TRACE: Once = Once::new();
  42. SETUP_TRACE.call_once(|| {
  43. // set up tracing for loom.
  44. const LOOM_LOG: &str = "LOOM_LOG";
  45. struct TracebufWriter;
  46. impl io::Write for TracebufWriter {
  47. fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
  48. let len = buf.len();
  49. let s = std::str::from_utf8(buf)
  50. .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
  51. TRACE_BUF.with(|buf| buf.borrow_mut().push_str(s));
  52. Ok(len)
  53. }
  54. fn flush(&mut self) -> io::Result<()> {
  55. Ok(())
  56. }
  57. }
  58. let filter = env::var(LOOM_LOG)
  59. .ok()
  60. .and_then(|var| match var.parse::<Targets>() {
  61. Err(e) => {
  62. eprintln!("invalid {}={:?}: {}", LOOM_LOG, var, e);
  63. None
  64. }
  65. Ok(targets) => Some(targets),
  66. })
  67. .unwrap_or_else(|| Targets::new().with_target("loom", tracing::Level::INFO));
  68. fmt::Subscriber::builder()
  69. .with_writer(|| TracebufWriter)
  70. .without_time()
  71. .finish()
  72. .with(filter)
  73. .init();
  74. if std::env::args().any(|arg| arg == "--nocapture") {
  75. IS_NOCAPTURE.store(true, Ordering::Relaxed);
  76. }
  77. let default_hook = std::panic::take_hook();
  78. std::panic::set_hook(Box::new(move |panic| {
  79. // try to print the trace buffer.
  80. TRACE_BUF
  81. .try_with(|buf| {
  82. if let Ok(mut buf) = buf.try_borrow_mut() {
  83. eprint!("{}", buf);
  84. buf.clear();
  85. } else {
  86. eprint!("trace buf already mutably borrowed?");
  87. }
  88. })
  89. .unwrap_or_else(|e| eprintln!("trace buf already torn down: {}", e));
  90. // let the default panic hook do the rest...
  91. default_hook(panic);
  92. }))
  93. });
  94. // wrap the loom model with `catch_unwind` to avoid potentially losing
  95. // test output on double panics.
  96. let current_iteration = std::sync::Arc::new(AtomicUsize::new(1));
  97. let iteration = current_iteration.clone();
  98. let test_name = match std::thread::current().name() {
  99. Some("main") | None => "test".to_string(),
  100. Some(name) => name.to_string(),
  101. };
  102. builder.check(move || {
  103. let iteration = current_iteration.fetch_add(1, Ordering::Relaxed);
  104. traceln(format_args!(
  105. "\n---- {} iteration {} ----",
  106. test_name, iteration,
  107. ));
  108. model();
  109. // if this iteration succeeded, clear the buffer for the
  110. // next iteration...
  111. TRACE_BUF.with(|buf| buf.borrow_mut().clear());
  112. });
  113. // Only print iterations on test completion in nocapture mode; otherwise
  114. // they'll just get all mangled.
  115. if IS_NOCAPTURE.load(Ordering::Relaxed) {
  116. print!("({} iterations) ", iteration.load(Ordering::Relaxed));
  117. }
  118. }
  119. #[track_caller]
  120. pub(crate) fn model(model: impl Fn() + std::panic::UnwindSafe + Sync + Send + 'static) {
  121. run_builder(Default::default(), model)
  122. }
  123. pub(crate) mod alloc {
  124. #![allow(dead_code)]
  125. use loom::alloc;
  126. use std::fmt;
  127. /// Track allocations, detecting leaks
  128. ///
  129. /// This is a version of `loom::alloc::Track` that adds a missing
  130. /// `Default` impl.
  131. pub struct Track<T>(alloc::Track<T>);
  132. impl<T> Track<T> {
  133. /// Track a value for leaks
  134. #[inline(always)]
  135. pub fn new(value: T) -> Track<T> {
  136. Track(alloc::Track::new(value))
  137. }
  138. /// Get a reference to the value
  139. #[inline(always)]
  140. pub fn get_ref(&self) -> &T {
  141. self.0.get_ref()
  142. }
  143. /// Get a mutable reference to the value
  144. #[inline(always)]
  145. pub fn get_mut(&mut self) -> &mut T {
  146. self.0.get_mut()
  147. }
  148. /// Stop tracking the value for leaks
  149. #[inline(always)]
  150. pub fn into_inner(self) -> T {
  151. self.0.into_inner()
  152. }
  153. }
  154. impl<T: fmt::Debug> fmt::Debug for Track<T> {
  155. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  156. self.0.fmt(f)
  157. }
  158. }
  159. impl<T: Default> Default for Track<T> {
  160. fn default() -> Self {
  161. Self::new(T::default())
  162. }
  163. }
  164. }
  165. }
  166. #[cfg(not(all(loom, test)))]
  167. mod inner {
  168. #![allow(dead_code)]
  169. pub(crate) mod sync {
  170. pub use core::sync::*;
  171. #[cfg(feature = "alloc")]
  172. pub use alloc::sync::*;
  173. }
  174. pub(crate) use core::sync::atomic;
  175. #[cfg(feature = "std")]
  176. pub use std::thread;
  177. pub(crate) mod hint {
  178. #[inline(always)]
  179. pub(crate) fn spin_loop() {
  180. // MSRV: std::hint::spin_loop() stabilized in 1.49.0
  181. #[allow(deprecated)]
  182. super::atomic::spin_loop_hint()
  183. }
  184. }
  185. #[derive(Debug)]
  186. pub(crate) struct UnsafeCell<T>(core::cell::UnsafeCell<T>);
  187. impl<T> UnsafeCell<T> {
  188. pub const fn new(data: T) -> UnsafeCell<T> {
  189. UnsafeCell(core::cell::UnsafeCell::new(data))
  190. }
  191. #[inline(always)]
  192. pub fn with<F, R>(&self, f: F) -> R
  193. where
  194. F: FnOnce(*const T) -> R,
  195. {
  196. f(self.0.get())
  197. }
  198. #[inline(always)]
  199. pub fn with_mut<F, R>(&self, f: F) -> R
  200. where
  201. F: FnOnce(*mut T) -> R,
  202. {
  203. f(self.0.get())
  204. }
  205. }
  206. pub(crate) mod alloc {
  207. /// Track allocations, detecting leaks
  208. #[derive(Debug, Default)]
  209. pub struct Track<T> {
  210. value: T,
  211. }
  212. impl<T> Track<T> {
  213. /// Track a value for leaks
  214. #[inline(always)]
  215. pub fn new(value: T) -> Track<T> {
  216. Track { value }
  217. }
  218. /// Get a reference to the value
  219. #[inline(always)]
  220. pub fn get_ref(&self) -> &T {
  221. &self.value
  222. }
  223. /// Get a mutable reference to the value
  224. #[inline(always)]
  225. pub fn get_mut(&mut self) -> &mut T {
  226. &mut self.value
  227. }
  228. /// Stop tracking the value for leaks
  229. #[inline(always)]
  230. pub fn into_inner(self) -> T {
  231. self.value
  232. }
  233. }
  234. }
  235. #[cfg(feature = "std")]
  236. pub(crate) fn traceln(args: std::fmt::Arguments) {
  237. eprintln!("{}", args);
  238. }
  239. #[cfg(not(feature = "std"))]
  240. pub(crate) fn traceln(_: core::fmt::Arguments) {}
  241. }