code wiki / (root) / nx_mutex_race_test.nx

nx_mutex_race_test.nx source

↩ module page · 78 lines · 2761 B

1// nx_mutex_race_test.nx -- prove the mutex provides REAL mutual 2// exclusion under multi-thread contention. 3// 4// hw-derived (cpu_count * STRESS_FACTOR) worker threads each grab 5// the same mutex N times and bump a PLAIN (non-atomic) counter 6// inside the critical section. If the mutex doesn't actually 7// exclude, the non-atomic increment races and final counter < N 8// (lost updates). PASS criterion: final counter == n_threads * N 9// exactly. 10 11import "nx_kernel_v2.nx" 12import "nx_log.nx" 13import "nx_atom.nx" 14import "nx_thread.nx" 15import "nx_mutex.nx" 16import "nx_hw.nx" 17 18const ITERATIONS_PER_THREAD: i64 = 5000 19const STRESS_FACTOR: i64 = 4 20 21struct SharedCounter { 22 mutex_state: i64, // first 8 bytes -- punned to NxMutex.state via cast 23 plain_count: i64, // bumped non-atomically inside critical section 24 done_count: i64, // atomic; signals workers complete 25} 26 27func worker_lock_bump(arg: *u8) -> i64 { 28 let s: *SharedCounter = arg as *SharedCounter 29 let m: *NxMutex = arg as *NxMutex 30 var i: i64 = 0 31 while i < ITERATIONS_PER_THREAD { 32 nx_mutex_lock(m) 33 // Critical section -- non-atomic on purpose to detect races. 34 let cur: i64 = s.plain_count 35 s.plain_count = cur + 1 36 nx_mutex_unlock(m) 37 i = i + 1 38 } 39 let done_addr: *i64 = ((arg as i64) + 16) as *i64 40 nx_atom_faa_i64(done_addr, 1, NX_MO_SEQ_CST) 41 return 0 42} 43 44func main() -> nx_exit { 45 let n_threads: i64 = nx_hw_worker_count() * STRESS_FACTOR 46 println("=== nx_mutex contention smoke (hw-derived workers x 5000 lock/inc/unlock each) ===" as *u8) 47 println("Spawning workers (cpu_count * STRESS_FACTOR):" as *u8) 48 print_i64(n_threads); println(" threads" as *u8) 49 50 let raw: *u8 = sys_mmap(64) 51 let s: *SharedCounter = raw as *SharedCounter 52 s.mutex_state = NX_MUTEX_UNLOCKED 53 s.plain_count = 0 54 s.done_count = 0 55 56 var t: i64 = 0 57 while t < n_threads { 58 let tid: i64 = nx_thread_spawn_fn(worker_lock_bump, raw, 65536) 59 if tid <= 0 { println("FAIL: spawn" as *u8); return 1 } 60 t = t + 1 61 } 62 63 let done_addr: *i64 = ((raw as i64) + 16) as *i64 64 var spins: i64 = 0 65 while nx_atom_load_i64(done_addr, NX_MO_SEQ_CST) < n_threads { 66 spins = spins + 1 67 if spins > 1000000000 { println("FAIL: timeout" as *u8); return 2 } 68 } 69 let expected: i64 = ITERATIONS_PER_THREAD * n_threads 70 let final: i64 = s.plain_count 71 if final != expected { 72 println("FAIL: lost updates -- mutex did not exclude!" as *u8) 73 return 3 74 } 75 println("PASS: plain_count == ITERATIONS * n_threads across hw-sized contention." as *u8) 76 println("Real CAS-based futex mutex actually serializes critical sections." as *u8) 77 return 0 78}