Nothing to see here, move along
1use crate::cap::pool::POOL;
2use crate::mem::phys::BitmapFrameAllocator;
3use crate::proc::{PROCESSES, ProcessState};
4use crate::types::Priority;
5use lancer_core::header::KernelObjectHeader;
6use lancer_core::object_layout::{KernelObject, SchedContextObject};
7use lancer_core::object_tag::ObjectTag;
8
9crate::kernel_test!(
10 fn zombify_transitions_to_zombie() {
11 let mut allocator = BitmapFrameAllocator;
12 let mut ptable = PROCESSES.lock();
13
14 let created = ptable.allocate(&mut allocator).expect("alloc");
15 ptable.start(created).expect("start");
16 let pid = created.pid();
17
18 assert!(ptable.zombify(pid), "zombify should succeed");
19 assert_eq!(
20 ptable[pid].state(),
21 ProcessState::Zombie,
22 "state should be Zombie after zombify"
23 );
24
25 ptable.destroy(pid, &mut allocator);
26 }
27);
28
29crate::kernel_test!(
30 fn reap_transitions_zombie_to_free() {
31 let mut allocator = BitmapFrameAllocator;
32 let mut ptable = PROCESSES.lock();
33
34 let created = ptable.allocate(&mut allocator).expect("alloc");
35 ptable.start(created).expect("start");
36 let pid = created.pid();
37
38 ptable.zombify(pid);
39 assert_eq!(ptable[pid].state(), ProcessState::Zombie);
40
41 ptable.reap(pid, &mut allocator);
42 assert!(ptable.get(pid).is_none(), "slot should be freed after reap");
43 }
44);
45
46crate::kernel_test!(
47 fn destroy_equivalent_to_zombify_then_reap() {
48 let mut allocator = BitmapFrameAllocator;
49 let mut ptable = PROCESSES.lock();
50
51 let created = ptable.allocate(&mut allocator).expect("alloc");
52 ptable.start(created).expect("start");
53 let pid = created.pid();
54
55 assert!(
56 ptable.destroy(pid, &mut allocator),
57 "destroy should succeed"
58 );
59 assert!(
60 ptable.get(pid).is_none(),
61 "slot should be freed after destroy"
62 );
63 }
64);
65
66crate::kernel_test!(
67 fn zombify_free_process_returns_false() {
68 let ptable = PROCESSES.lock();
69 let free_pid = (1..crate::types::MAX_PIDS as u32)
70 .filter_map(crate::types::Pid::try_new)
71 .find(|&pid| ptable.get(pid).is_none());
72 drop(ptable);
73
74 let Some(pid) = free_pid else { return };
75
76 let mut ptable = PROCESSES.lock();
77 assert!(
78 !ptable.zombify(pid),
79 "zombify on Free process should return false"
80 );
81 }
82);
83
84crate::kernel_test!(
85 fn reap_non_zombie_is_noop() {
86 let mut allocator = BitmapFrameAllocator;
87 let mut ptable = PROCESSES.lock();
88
89 let created = ptable.allocate(&mut allocator).expect("alloc");
90 ptable.start(created).expect("start");
91 let pid = created.pid();
92
93 ptable.reap(pid, &mut allocator);
94 assert_eq!(
95 ptable[pid].state(),
96 ProcessState::Ready,
97 "reap on non-Zombie should be a noop"
98 );
99
100 ptable.destroy(pid, &mut allocator);
101 }
102);
103
104crate::kernel_test!(
105 fn double_zombify_returns_false() {
106 let mut allocator = BitmapFrameAllocator;
107 let mut ptable = PROCESSES.lock();
108
109 let created = ptable.allocate(&mut allocator).expect("alloc");
110 ptable.start(created).expect("start");
111 let pid = created.pid();
112
113 assert!(ptable.zombify(pid), "first zombify should succeed");
114 assert!(
115 !ptable.zombify(pid),
116 "second zombify should return false (already Zombie)"
117 );
118
119 ptable.destroy(pid, &mut allocator);
120 }
121);
122
123crate::kernel_test!(
124 fn zombify_blocked_process() {
125 let mut allocator = BitmapFrameAllocator;
126 let mut ptable = PROCESSES.lock();
127
128 let created = ptable.allocate(&mut allocator).expect("alloc");
129 ptable.start(created).expect("start");
130 let pid = created.pid();
131
132 ptable.simulate_dispatch(pid);
133
134 let (ep_id, ep_gen) =
135 crate::tests::helpers::alloc_endpoint(&mut POOL.lock()).expect("alloc ep");
136
137 let _ = ptable[pid]
138 .block_on(crate::proc::BlockedReason::Sending(ep_id, ep_gen))
139 .expect("block");
140
141 assert_eq!(ptable[pid].state(), ProcessState::Blocked);
142 assert!(ptable.zombify(pid), "zombify Blocked should succeed");
143 assert_eq!(ptable[pid].state(), ProcessState::Zombie);
144
145 ptable.reap(pid, &mut allocator);
146 let _ = POOL.lock().dec_ref_phys(ep_id, ep_gen);
147 }
148);
149
150crate::kernel_test!(
151 fn zombify_preserves_generation() {
152 let mut allocator = BitmapFrameAllocator;
153 let mut ptable = PROCESSES.lock();
154
155 let created = ptable.allocate(&mut allocator).expect("alloc");
156 ptable.start(created).expect("start");
157 let pid = created.pid();
158
159 let gen_before = ptable[pid].generation;
160 ptable.zombify(pid);
161 let gen_after = ptable[pid].generation;
162
163 assert_eq!(
164 gen_before, gen_after,
165 "zombify should not change generation"
166 );
167
168 ptable.destroy(pid, &mut allocator);
169 }
170);
171
172crate::kernel_test!(
173 fn zombie_not_runnable() {
174 let mut allocator = BitmapFrameAllocator;
175 let mut ptable = PROCESSES.lock();
176
177 let created = ptable.allocate(&mut allocator).expect("alloc");
178 ptable.start(created).expect("start");
179 let pid = created.pid();
180
181 assert!(
182 ptable[pid].is_runnable(),
183 "Ready process should be runnable"
184 );
185
186 ptable.zombify(pid);
187 assert!(
188 !ptable[pid].is_runnable(),
189 "Zombie process should not be runnable"
190 );
191
192 ptable.destroy(pid, &mut allocator);
193 }
194);
195
196crate::kernel_test!(
197 fn zombie_skipped_by_scheduler() {
198 let mut allocator = BitmapFrameAllocator;
199 let mut ptable = PROCESSES.lock();
200
201 let a_created = ptable.allocate(&mut allocator).expect("alloc A");
202 let b_created = ptable.allocate(&mut allocator).expect("alloc B");
203 ptable.start(a_created).expect("start A");
204 ptable.start(b_created).expect("start B");
205 let a_pid = a_created.pid();
206 let b_pid = b_created.pid();
207 drop(ptable);
208
209 let header_a = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64);
210 let mut sc_a = SchedContextObject::init_default(header_a);
211 sc_a.budget_us = 10_000;
212 sc_a.period_us = 100_000;
213 sc_a.priority = Priority::new(200).raw();
214 sc_a.attached_pid = lancer_core::header::NONE_SENTINEL;
215 let (sc_a_id, sc_a_gen) =
216 crate::tests::helpers::alloc_typed(&mut POOL.lock(), ObjectTag::SchedContext, sc_a)
217 .expect("alloc sc A");
218
219 let header_b = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64);
220 let mut sc_b = SchedContextObject::init_default(header_b);
221 sc_b.budget_us = 10_000;
222 sc_b.period_us = 100_000;
223 sc_b.priority = Priority::new(50).raw();
224 sc_b.attached_pid = lancer_core::header::NONE_SENTINEL;
225 let (sc_b_id, sc_b_gen) =
226 crate::tests::helpers::alloc_typed(&mut POOL.lock(), ObjectTag::SchedContext, sc_b)
227 .expect("alloc sc B");
228
229 let mut ptable = PROCESSES.lock();
230 ptable[a_pid].attach_sched_context(sc_a_id, sc_a_gen, Priority::new(200));
231 ptable[b_pid].attach_sched_context(sc_b_id, sc_b_gen, Priority::new(50));
232
233 ptable.zombify(a_pid);
234
235 let best = (0..crate::types::MAX_PIDS as u32).fold(
236 None::<(crate::types::Pid, Priority)>,
237 |best, idx| {
238 let pid = crate::types::Pid::new(idx);
239 let proc = match ptable.get(pid) {
240 Some(p) => p,
241 None => return best,
242 };
243 if !proc.is_runnable() {
244 return best;
245 }
246 let prio = proc.effective_priority();
247 match best {
248 Some((_, best_prio)) if prio <= best_prio => best,
249 _ => Some((pid, prio)),
250 }
251 },
252 );
253
254 assert!(
255 best.is_none_or(|(pid, _)| pid != a_pid),
256 "zombie process A should not be selected by scheduler"
257 );
258
259 ptable.destroy(a_pid, &mut allocator);
260 ptable.destroy(b_pid, &mut allocator);
261 }
262);
263
264crate::kernel_test!(
265 fn reap_with_sched_context_detaches() {
266 let mut allocator = BitmapFrameAllocator;
267 let mut ptable = PROCESSES.lock();
268
269 let created = ptable.allocate(&mut allocator).expect("alloc");
270 ptable.start(created).expect("start");
271 let pid = created.pid();
272
273 let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64);
274 let mut sc_obj = SchedContextObject::init_default(header);
275 sc_obj.budget_us = 10_000;
276 sc_obj.period_us = 100_000;
277 sc_obj.priority = Priority::new(100).raw();
278 sc_obj.attached_pid = lancer_core::header::NONE_SENTINEL;
279 let (sc_id, sc_gen) =
280 crate::tests::helpers::alloc_typed(&mut POOL.lock(), ObjectTag::SchedContext, sc_obj)
281 .expect("alloc sc");
282 ptable[pid].attach_sched_context(sc_id, sc_gen, Priority::new(100));
283
284 ptable.zombify(pid);
285 ptable.reap(pid, &mut allocator);
286
287 let pool = POOL.lock();
288 let sc = pool
289 .read_as::<SchedContextObject>(sc_id, sc_gen)
290 .expect("sc should still exist");
291 assert_eq!(
292 sc.attached_pid,
293 lancer_core::header::NONE_SENTINEL,
294 "sched context attached_pid should be None after reap"
295 );
296 drop(pool);
297
298 let _ = POOL.lock().dec_ref_phys(sc_id, sc_gen);
299 }
300);