nx_batch_scheduler_test.nx source
↩ module page · 164 lines · 7615 B
1// nx_batch_scheduler_test.nx -- smoke for H2 continuous batching.
2
3import "nx_syscalls.nx"
4import "nx_batch_scheduler.nx"
5
6func main() -> i64 {
7 // 4-slot scheduler with 8-slot pending queue.
8 let sch: *NxBatchScheduler = nx_batch_scheduler_new(4, 8)
9 if nx_batch_scheduler_is_valid(sch) != 1 { return 1 }
10 if nx_batch_scheduler_n_running(sch) != 0 { return 2 }
11 if nx_batch_scheduler_n_pending(sch) != 0 { return 3 }
12 if nx_batch_scheduler_n_done(sch) != 0 { return 4 }
13
14 // ----- 1. Admit 3 requests directly into slots -----
15 let s0: i64 = nx_batch_scheduler_admit(sch, 1001, 16)
16 if s0 < 0 { return 5 }
17 if nx_batch_scheduler_slot_state(sch, s0) != NX_SLOT_RUNNING { return 6 }
18 if nx_batch_scheduler_slot_request_id(sch, s0) != 1001 { return 7 }
19 if nx_batch_scheduler_slot_tokens_remaining(sch, s0) != 16 { return 8 }
20
21 let s1: i64 = nx_batch_scheduler_admit(sch, 1002, 8)
22 if s1 < 0 { return 9 }
23 if s1 == s0 { return 10 }
24
25 let s2: i64 = nx_batch_scheduler_admit(sch, 1003, 4)
26 if s2 < 0 { return 11 }
27 if nx_batch_scheduler_n_running(sch) != 3 { return 12 }
28
29 // ----- 2. step() reports the 3 running slots -----
30 let active: *i64 = (sys_mmap(64)) as *i64
31 let n_active: i64 = nx_batch_scheduler_step(sch, active, 8)
32 if n_active != 3 { return 13 }
33
34 // ----- 3. Fill the 4th slot directly -----
35 let s3: i64 = nx_batch_scheduler_admit(sch, 1004, 32)
36 if s3 < 0 { return 14 }
37 if nx_batch_scheduler_n_running(sch) != 4 { return 15 }
38
39 // ----- 4. 5th admission goes to QUEUE -----
40 let q_v: i64 = nx_batch_scheduler_admit(sch, 1005, 2)
41 if q_v != (0 - NX_BATCH_QUEUED) { return 16 }
42 if nx_batch_scheduler_n_pending(sch) != 1 { return 17 }
43 if nx_batch_scheduler_n_running(sch) != 4 { return 18 }
44
45 // ----- 5. Add more to queue -----
46 let q2: i64 = nx_batch_scheduler_admit(sch, 1006, 5)
47 if q2 != (0 - NX_BATCH_QUEUED) { return 19 }
48 let q3: i64 = nx_batch_scheduler_admit(sch, 1007, 5)
49 if q3 != (0 - NX_BATCH_QUEUED) { return 20 }
50 if nx_batch_scheduler_n_pending(sch) != 3 { return 21 }
51
52 // ----- 6. tick() decrements token count -----
53 let rem: i64 = nx_batch_scheduler_tick(sch, s0, 1)
54 if rem != 15 { return 22 }
55 if nx_batch_scheduler_slot_state(sch, s0) != NX_SLOT_RUNNING { return 23 }
56
57 // ----- 7. tick() through to DONE -----
58 let rem2: i64 = nx_batch_scheduler_tick(sch, s2, 4)
59 if rem2 != 0 { return 24 }
60 if nx_batch_scheduler_slot_state(sch, s2) != NX_SLOT_DONE { return 25 }
61 if nx_batch_scheduler_n_running(sch) != 3 { return 26 }
62 if nx_batch_scheduler_n_done(sch) != 1 { return 27 }
63
64 // ----- 8. recycle drains queue into freed slot -----
65 let admitted: i64 = nx_batch_scheduler_recycle(sch)
66 if admitted != 1 { return 28 } // only 1 slot freed; 1 queued admitted
67 if nx_batch_scheduler_n_running(sch) != 4 { return 29 } // back to 4
68 if nx_batch_scheduler_n_pending(sch) != 2 { return 30 } // 3 - 1
69 if nx_batch_scheduler_n_done(sch) != 0 { return 31 }
70
71 // The newly-admitted slot is request 1005 (FCFS order).
72 // It must be in some slot in RUNNING state with that request_id.
73 var found_1005: i64 = 0
74 var i: i64 = 0
75 while i < sch.max_slots {
76 if nx_batch_scheduler_slot_request_id(sch, i) == 1005 {
77 if nx_batch_scheduler_slot_state(sch, i) == NX_SLOT_RUNNING {
78 found_1005 = 1
79 }
80 }
81 i = i + 1
82 }
83 if found_1005 != 1 { return 32 }
84
85 // ----- 9. complete() forces slot DONE -----
86 if nx_batch_scheduler_complete(sch, s1) != NX_BATCH_OK { return 33 }
87 if nx_batch_scheduler_slot_state(sch, s1) != NX_SLOT_DONE { return 34 }
88 if nx_batch_scheduler_n_running(sch) != 3 { return 35 }
89 if nx_batch_scheduler_n_done(sch) != 1 { return 36 }
90
91 // Complete a non-running slot -> SLOT_NOT_RUNNING
92 if nx_batch_scheduler_complete(sch, s1) != NX_BATCH_SLOT_NOT_RUNNING { return 37 }
93
94 // ----- 10. Recycle drains the rest -----
95 let admitted_2: i64 = nx_batch_scheduler_recycle(sch)
96 if admitted_2 != 1 { return 38 }
97 if nx_batch_scheduler_n_pending(sch) != 1 { return 39 }
98
99 // ----- 11. Empty the rest by completing all -----
100 var j: i64 = 0
101 while j < sch.max_slots {
102 let st: i64 = nx_batch_scheduler_slot_state(sch, j)
103 if st == NX_SLOT_RUNNING {
104 nx_batch_scheduler_complete(sch, j)
105 }
106 j = j + 1
107 }
108 if nx_batch_scheduler_n_running(sch) != 0 { return 40 }
109 // Recycle drains the last queued -> 1 admitted, 0 pending.
110 let admitted_3: i64 = nx_batch_scheduler_recycle(sch)
111 if admitted_3 != 1 { return 41 }
112 if nx_batch_scheduler_n_pending(sch) != 0 { return 42 }
113
114 // ----- 12. Queue-FULL rejection -----
115 // Fresh 1-slot scheduler with 2-slot queue.
116 let sch2: *NxBatchScheduler = nx_batch_scheduler_new(1, 2)
117 let a0: i64 = nx_batch_scheduler_admit(sch2, 200, 5)
118 if a0 < 0 { return 43 } // direct admit
119 let a1: i64 = nx_batch_scheduler_admit(sch2, 201, 5)
120 if a1 != (0 - NX_BATCH_QUEUED) { return 44 } // queued
121 let a2: i64 = nx_batch_scheduler_admit(sch2, 202, 5)
122 if a2 != (0 - NX_BATCH_QUEUED) { return 45 } // queued
123 let a3: i64 = nx_batch_scheduler_admit(sch2, 203, 5)
124 if a3 != (0 - NX_BATCH_FULL) { return 46 } // REJECTED
125
126 // ----- 13. Bad-input gates -----
127 if nx_batch_scheduler_admit(sch, 999, 0) != (0 - NX_BATCH_BAD_INPUT) { return 47 }
128 if nx_batch_scheduler_admit(sch, 999, -5) != (0 - NX_BATCH_BAD_INPUT) { return 48 }
129 if nx_batch_scheduler_tick(sch, -1, 1) != (0 - NX_BATCH_BAD_SLOT) { return 49 }
130 if nx_batch_scheduler_tick(sch, 999, 1) != (0 - NX_BATCH_BAD_SLOT) { return 50 }
131 if nx_batch_scheduler_complete(sch, -1) != NX_BATCH_BAD_SLOT { return 51 }
132 if nx_batch_scheduler_complete(sch, 999) != NX_BATCH_BAD_SLOT { return 52 }
133 let null_buf: *i64 = (0 as i64) as *i64
134 if nx_batch_scheduler_step(sch, null_buf, 8) != (0 - NX_BATCH_BAD_INPUT) { return 53 }
135 if nx_batch_scheduler_step(sch, active, 0) != (0 - NX_BATCH_BAD_INPUT) { return 54 }
136
137 // ----- 14. Tamper -----
138 let tamper_sch: *NxBatchScheduler = nx_batch_scheduler_new(2, 4)
139 tamper_sch.canary_post = 0xDEADBEEF
140 if nx_batch_scheduler_is_valid(tamper_sch) != 0 { return 55 }
141 if nx_batch_scheduler_admit(tamper_sch, 1, 1) != (0 - NX_BATCH_TAMPER) { return 56 }
142 if nx_batch_scheduler_recycle(tamper_sch) != (0 - NX_BATCH_TAMPER) { return 57 }
143 if nx_batch_scheduler_n_running(tamper_sch) != -1 { return 58 }
144
145 // ----- 15. Sealed-enum gates -----
146 if nx_slot_state_is_valid(NX_SLOT_FREE) != 1 { return 59 }
147 if nx_slot_state_is_valid(NX_SLOT_RUNNING) != 1 { return 60 }
148 if nx_slot_state_is_valid(NX_SLOT_DONE) != 1 { return 61 }
149 if nx_slot_state_is_valid(-1) != 0 { return 62 }
150 if nx_slot_state_is_valid(NX_SLOT_N_STATES) != 0 { return 63 }
151 if nx_batch_verdict_is_valid(NX_BATCH_OK) != 1 { return 64 }
152 if nx_batch_verdict_is_valid(NX_BATCH_QUEUED) != 1 { return 65 }
153 if nx_batch_verdict_is_valid(-1) != 0 { return 66 }
154 if nx_batch_verdict_is_valid(NX_BATCH_N_VERDICTS) != 0 { return 67 }
155
156 // ----- 16. Bad new() inputs -----
157 if (nx_batch_scheduler_new(0, 4) as i64) != 0 { return 68 }
158 if (nx_batch_scheduler_new(-1, 4) as i64) != 0 { return 69 }
159 if (nx_batch_scheduler_new(NX_BATCH_MAX_SLOTS + 1, 4) as i64) != 0 { return 70 }
160 if (nx_batch_scheduler_new(4, -1) as i64) != 0 { return 71 }
161 if (nx_batch_scheduler_new(4, NX_BATCH_MAX_PENDING + 1) as i64) != 0 { return 72 }
162
163 return 0
164}