makepad/tools/run/fixture/diff/tls_const.rs

93 lines
4.3 KiB
Rust

// diff_fast corpus program: `#[test] run` calls work() in rounds (the O3 tier installs between rounds); every
// result line is `D ...`. rustc --test -O and `makepad test` (baseline and O3) must print identical D lines.
// `const`-initialised thread-locals of types without drop glue (std: a `#[thread_local]` static; Relax: static
// TLS at a fixed offset from the thread pointer when the value starts as zeros, else the thread-local block
// with the value copied in on a thread's first access): zero and non-zero starts, pointers to read-only data
// and to a static, over-aligned values, many threads, each thread's own values, values read in a destructor.
macro_rules! d { ($($a:tt)*) => { println!("D {}", format!($($a)*)) } }
fn rounds(f: fn()) {
let ms: u64 = std::env::var("DIFF_SLEEP_MS").ok().and_then(|v| v.parse().ok()).unwrap_or(0);
for r in 0..4 { f(); if r < 3 && ms > 0 { std::thread::sleep(std::time::Duration::from_millis(ms)); } }
}
#[test]
fn run() { rounds(work); }
use std::cell::Cell;
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(5);
#[derive(Clone, Copy)]
#[repr(align(32))]
struct Wide([u64; 4]);
thread_local! {
static ZERO: Cell<u64> = const { Cell::new(0) };
static SEVEN: Cell<u32> = const { Cell::new(7) };
static FLAG: Cell<bool> = const { Cell::new(true) };
static NAME: Cell<&'static str> = const { Cell::new("start") };
static TARGET: Cell<&'static AtomicU32> = const { Cell::new(&COUNTER) };
static WIDE: Cell<Wide> = const { Cell::new(Wide([1, 2, 3, 4])) };
static ZWIDE: Cell<Wide> = const { Cell::new(Wide([0; 4])) };
static TABLE: [Cell<u16>; 64] = const { [const { Cell::new(0) }; 64] };
static OPT: Cell<Option<u32>> = const { Cell::new(None) };
}
struct Tail(u32);
impl Drop for Tail {
fn drop(&mut self) {
// a thread-local without drop glue stays readable while other destructors run
let s = SEVEN.with(|c| c.get());
let z = ZERO.with(|c| c.get());
if s != 7 + self.0 || z != self.0 as u64 { println!("D tail {} sees seven={} zero={}", self.0, s, z); }
}
}
thread_local! { static TAIL: std::cell::RefCell<Option<Tail>> = const { std::cell::RefCell::new(None) }; }
#[inline(never)]
fn touch(i: u32) -> u64 {
ZERO.with(|c| c.set(c.get() + i as u64));
SEVEN.with(|c| c.set(c.get().wrapping_mul(3).wrapping_add(i)));
FLAG.with(|c| c.set(!c.get()));
if i % 7 == 0 { NAME.with(|c| c.set(if c.get() == "start" { "seven" } else { "start" })); }
TABLE.with(|t| t[(i % 64) as usize].set(t[(i % 64) as usize].get().wrapping_add(i as u16)));
WIDE.with(|c| { let mut w = c.get(); w.0[(i % 4) as usize] += i as u64; c.set(w) });
ZWIDE.with(|c| { let mut w = c.get(); w.0[(i % 4) as usize] ^= i as u64; c.set(w) });
OPT.with(|c| c.set(Some(c.get().unwrap_or(0) + 1)));
ZERO.with(|c| c.get())
}
fn report(tag: &str) -> String {
let t: u64 = TABLE.with(|t| t.iter().map(|c| c.get() as u64).sum());
let w = WIDE.with(|c| c.get().0);
let z = ZWIDE.with(|c| c.get().0);
let aligned = WIDE.with(|c| c as *const _ as usize % 32 == 0) && ZWIDE.with(|c| c as *const _ as usize % 32 == 0);
format!("{} zero={} seven={} flag={} name={} target={} table={} wide={:?} zwide={:?} opt={:?} aligned={}", tag,
ZERO.with(|c| c.get()), SEVEN.with(|c| c.get()), FLAG.with(|c| c.get()), NAME.with(|c| c.get()),
std::ptr::eq(TARGET.with(|c| c.get()), &COUNTER), t, w, z, OPT.with(|c| c.get()), aligned)
}
fn work() {
for c in [&ZERO] { c.with(|c| c.set(0)); }
SEVEN.with(|c| c.set(7));
d!("{}", report("fresh-main"));
let mut s = 0u64;
for i in 0..1000u32 { s = s.wrapping_add(touch(i)); }
d!("main s={} {}", s, report("main"));
let hs: Vec<_> = (0..6u32).map(|t| std::thread::spawn(move || {
let fresh = report("fresh");
let mut s = 0u64;
for i in 0..(300 + 50 * t) { s = s.wrapping_add(touch(i)); }
TARGET.with(|c| c.get().fetch_add(1, Ordering::Relaxed));
ZERO.with(|c| c.set(t as u64));
SEVEN.with(|c| c.set(7 + t));
TAIL.with(|c| *c.borrow_mut() = Some(Tail(t)));
(t, fresh, s, report("thread"))
})).collect();
for h in hs {
let (t, fresh, s, r) = h.join().unwrap();
d!("t{} {}", t, fresh);
d!("t{} s={} {}", t, s, r);
}
d!("counter={}", COUNTER.load(Ordering::Relaxed) >= 5);
}