makepad/libs/relax/compiler/tests/live/rt_lock/v0.rs
Admin b22a714840 makepad run: the Rust JIT (libs/relax) and makepad-remote
The JIT that runs makepad apps from source: lexer, front end, type checker,
lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and
warm/cold caches keyed by compiler stamp, provided crates bound late,
startup images, live patching. User-facing name is `makepad run` (binary
`makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal
library name. `makepad check` reports rustc-style diagnostics with rustc
error codes and checks a whole workspace (`--workspace`). Runs on macOS,
Linux, Windows and Android. makepad-remote: one tool schema for apps and the
WM (app/1): grabs, captures, traces, frame profiles and tweaks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00

85 lines
3.3 KiB
Rust

// G7 / 3.13 (M1, M8): a real-time thread (stand-in for the audio callback) calls `voice` in a
// tight loop and records its longest single call. v1 edits `voice` to call `heavy`, which no one
// has called yet (a lazy stub), and edits `big` (compiled, so the patch compiles it again while
// holding today's unit lock). The real-time thread must never compile and never wait for the
// compiler: the installer compiles every uncompiled callee reachable from patched code before it
// installs, and the patch compiles off the lock. Expected: no call of `voice` takes 5 ms or more
// in any window (`heavy` is built big enough that compiling it takes far longer).
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::Instant;
static STARTED: AtomicBool = AtomicBool::new(false);
static ITERS: AtomicU64 = AtomicU64::new(0);
static MAX_NS: AtomicU64 = AtomicU64::new(0);
static SINK: AtomicU64 = AtomicU64::new(0);
// voice(7) as last computed on the real-time thread: proves it runs the edited code
static LAST7: AtomicU64 = AtomicU64::new(0);
// x4 nested: copies of a few mixing ops, a body that is slow to compile and fast to run (heavy: 4^6 =
// 4096 copies, ~250 ms to compile on node 165; big: 4^5)
macro_rules! x4 {
($($t:tt)*) => { $($t)* $($t)* $($t)* $($t)* };
}
fn heavy(i: u64) -> u64 {
let mut h = i;
x4! { x4! { x4! { x4! { x4! { x4! { h = h.rotate_left(5) ^ (h >> 3).wrapping_mul(0x9E37_79B9_7F4A_7C15); } } } } } }
h
}
fn big(i: u64) -> u64 {
let mut h = i ^ 1;
x4! { x4! { x4! { x4! { x4! { h = h.rotate_left(7) ^ (h >> 5).wrapping_add(0x2545_F491_4F6C_DD1D); } } } } }
h
}
// not inlined into rt_loop (whose frame never returns, so it would keep the old copy): the dead
// locals keep it above O1's source-size inlining gate
fn voice(i: u64) -> u64 {
let _d0 = 0u64; let _d1 = 1u64; let _d2 = 2u64; let _d3 = 3u64; let _d4 = 4u64;
let _d5 = 5u64; let _d6 = 6u64; let _d7 = 7u64; let _d8 = 8u64; let _d9 = 9u64;
let _e0 = 0u64; let _e1 = 1u64; let _e2 = 2u64; let _e3 = 3u64; let _e4 = 4u64;
let _e5 = 5u64; let _e6 = 6u64; let _e7 = 7u64; let _e8 = 8u64; let _e9 = 9u64;
i.wrapping_mul(3) ^ 0x55
}
fn rt_loop() {
let mut i = 0u64;
loop {
let t = Instant::now();
let v = voice(i);
let ns = t.elapsed().as_nanos() as u64;
SINK.fetch_xor(v, Ordering::Relaxed);
MAX_NS.fetch_max(ns, Ordering::SeqCst);
if ITERS.fetch_add(1, Ordering::SeqCst) % 1024 == 0 {
LAST7.store(voice(7), Ordering::SeqCst);
}
i += 1;
}
}
fn wait_iters(n: u64) {
let start = ITERS.load(Ordering::SeqCst);
while ITERS.load(Ordering::SeqCst) < start + n {
std::thread::sleep(std::time::Duration::from_millis(1));
}
}
#[test]
fn run() {
if !STARTED.swap(true, Ordering::SeqCst) {
std::thread::Builder::new().name("rt-voice".to_string()).spawn(rt_loop).unwrap();
wait_iters(100_000);
println!("out big {}", big(1) & 1 < 2);
MAX_NS.store(0, Ordering::SeqCst);
return;
}
wait_iters(100_000);
let max = MAX_NS.swap(0, Ordering::SeqCst);
println!("out voice7 {}", LAST7.load(Ordering::SeqCst));
if max < 5_000_000 {
println!("out rt ok");
} else {
println!("out rt stall {} us", max / 1000);
}
}