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}