loom.rs 7.8 KB

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