Nothing to see here, move along
at main 300 lines 9.8 kB view raw
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);