makepad/libs/relax/compiler/tests/atomics.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

125 lines
4.8 KiB
Rust

// Atomics and threads: every atomic operation on every integer width (wrapping, signed
// and unsigned max/min, nand, compare-exchange success and failure), then four threads
// started with pthread_create (C calling Relax `extern "C"` bodies on new threads, which
// compile lazily under the unit lock) incrementing shared counters with fetch_add and with
// CAS loops. Output matches rustc.
use std::sync::atomic::*;
extern "C" {
fn pthread_create(t: *mut usize, attr: *const u8, start: extern "C" fn(*mut u8) -> *mut u8, arg: *mut u8) -> i32;
fn pthread_join(t: usize, ret: *mut *mut u8) -> i32;
}
static COUNTER: AtomicU64 = AtomicU64::new(0);
static CAS_COUNTER: AtomicU32 = AtomicU32::new(0);
static MAXV: AtomicI64 = AtomicI64::new(-1000);
static FLAGS: AtomicU8 = AtomicU8::new(0);
extern "C" fn worker(arg: *mut u8) -> *mut u8 {
let id = arg as usize as u64;
let mut i = 0u64;
while i < 20000 {
COUNTER.fetch_add(1 + id, Ordering::Relaxed);
let mut cur = CAS_COUNTER.load(Ordering::Relaxed);
loop {
match CAS_COUNTER.compare_exchange(cur, cur + 1, Ordering::AcqRel, Ordering::Relaxed) {
Ok(_) => break,
Err(now) => cur = now,
}
}
MAXV.fetch_max((i * 7 + id * 3) as i64 % 1001, Ordering::SeqCst);
i += 1;
}
FLAGS.fetch_or(1 << id, Ordering::Release);
(id * 10) as usize as *mut u8
}
#[test]
fn single_thread_ops() {
let a = AtomicU8::new(250);
let r1 = a.fetch_add(10, Ordering::SeqCst);
let r2 = a.load(Ordering::Acquire);
let r3 = a.fetch_sub(5, Ordering::Relaxed);
let r4 = a.load(Ordering::Relaxed);
println!("u8 {} {} {} {}", r1, r2, r3, r4);
let b = AtomicI8::new(-100);
let s1 = b.fetch_sub(100, Ordering::SeqCst);
let s2 = b.load(Ordering::SeqCst);
let s3 = b.fetch_max(-5, Ordering::SeqCst);
let s4 = b.fetch_min(-120, Ordering::SeqCst);
let s5 = b.load(Ordering::SeqCst);
println!("i8 {} {} {} {} {}", s1, s2, s3, s4, s5);
let c = AtomicU16::new(0xF0F0);
let t1 = c.fetch_and(0x0FF0, Ordering::SeqCst);
let t2 = c.fetch_or(0x000F, Ordering::SeqCst);
let t3 = c.fetch_xor(0xFFFF, Ordering::SeqCst);
let t4 = c.fetch_nand(0x00FF, Ordering::SeqCst);
let t5 = c.load(Ordering::SeqCst);
println!("u16 {} {} {} {} {}", t1, t2, t3, t4, t5);
let d = AtomicI32::new(-7);
let u1 = d.swap(i32::MIN, Ordering::SeqCst);
let u2 = d.fetch_sub(1, Ordering::SeqCst);
let u3 = d.load(Ordering::SeqCst);
let u4 = d.fetch_min(5, Ordering::SeqCst);
let u5 = d.fetch_max(5, Ordering::SeqCst);
println!("i32 {} {} {} {} {}", u1, u2, u3, u4, u5);
let e = AtomicU32::new(4000000000);
let v1 = e.fetch_max(100, Ordering::SeqCst);
let v2 = e.fetch_min(100, Ordering::SeqCst);
let v3 = e.fetch_nand(0xFFFF, Ordering::SeqCst);
let v4 = e.load(Ordering::SeqCst);
println!("u32 {} {} {} {}", v1, v2, v3, v4);
let f = AtomicU64::new(5);
let w1 = match f.compare_exchange(5, 9, Ordering::SeqCst, Ordering::SeqCst) {
Ok(x) => x as i64,
Err(x) => -(x as i64),
};
let w2 = match f.compare_exchange(5, 11, Ordering::SeqCst, Ordering::Relaxed) {
Ok(x) => x as i64,
Err(x) => -(x as i64),
};
f.store(u64::MAX, Ordering::Release);
let w3 = f.fetch_add(2, Ordering::SeqCst);
let w4 = f.load(Ordering::SeqCst);
println!("u64 {} {} {} {}", w1, w2, w3, w4);
let g = AtomicI64::new(i64::MAX);
let x1 = g.fetch_add(1, Ordering::SeqCst);
let x2 = g.load(Ordering::SeqCst);
let i = AtomicIsize::new(-3);
let x3 = i.fetch_max(-4, Ordering::SeqCst);
let x4 = i.fetch_min(-4, Ordering::SeqCst);
let x5 = i.load(Ordering::SeqCst);
let u = AtomicUsize::new(1);
let x6 = u.fetch_sub(2, Ordering::SeqCst);
let x7 = u.load(Ordering::SeqCst);
fence(Ordering::SeqCst);
compiler_fence(Ordering::SeqCst);
let h = AtomicI16::new(-2);
let x8 = h.fetch_and(0x7fff, Ordering::SeqCst);
let x9 = h.load(Ordering::SeqCst);
println!("i64/isize/usize/i16 {} {} {} {} {} {} {} {} {}", x1, x2, x3, x4, x5, x6, x7, x8, x9);
}
#[test]
fn threads_and_counters() {
let mut ts = [0usize; 4];
let mut k = 0;
while k < 4 {
let r = unsafe { pthread_create(&mut ts[k] as *mut usize, 0 as *const u8, worker, k as *mut u8) };
if r != 0 {
println!("pthread_create failed {}", r);
}
k += 1;
}
let mut sum = 0usize;
k = 0;
while k < 4 {
let mut ret = 0 as *mut u8;
unsafe { pthread_join(ts[k], &mut ret as *mut *mut u8) };
sum += ret as usize;
k += 1;
}
println!("joined sum {}", sum);
println!("counter {} cas {} max {} flags {}", COUNTER.load(Ordering::SeqCst), CAS_COUNTER.load(Ordering::SeqCst), MAXV.load(Ordering::SeqCst), FLAGS.load(Ordering::SeqCst));
}