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>
125 lines
4.8 KiB
Rust
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));
|
|
}
|