Exercise 2: A TERMINATED Process Can Never Be Scheduled — Possible Solution ==================================================================== THE TEST ------------------------------ dead_proc = kernel.create_process(num_pages=1) dead_proc.transition(ProcessState.RUNNING) dead_proc.transition(ProcessState.TERMINATED) scheduler.add(dead_proc) RESULT ------------------------------ adding a TERMINATED process correctly raised: cannot schedule PID 6: not READY (state=TERMINATED) add() rejects the process immediately, with a clear message naming its actual current state. WHY NO SPECIAL-CASE CODE WAS NEEDED ------------------------------ Scheduler.add()'s own guard, from Finding 2, is: if pcb.state != ProcessState.READY: raise ValueError(f"cannot schedule PID {pcb.pid}: not READY (state={pcb.state.value})") This check was never written as "reject BLOCKED processes" -- it was written as "reject anything that isn't READY." BLOCKED and TERMINATED are just two different values that both fail that same single comparison. The guard doesn't need to know or care which non-READY state a process is in, or why it's in that state, to do its job correctly -- it only needs to know that READY is the one state that's actually safe to hand a CPU turn to. WHY THIS MATTERS ------------------------------ A guard written as an ALLOW-list of exactly one correct value (state == READY, inverted to state != READY) automatically covers every future state anyone might add to ProcessState later, with zero additional code. A guard written instead as a DENY-list ("reject BLOCKED, reject TERMINATED, reject ...") would need a new line added by hand every time a new state was introduced, and would silently admit any state nobody remembered to list -- exactly the kind of gap this course has already found more than once (Chapter 4's own memory-leak bug, Chapter 6's own quota gap) when a rule was enforced on some code paths but not others. WHY THIS WORKS AS AN ANSWER ------------------------------ Testing TERMINATED specifically, rather than assuming Finding 2's own BLOCKED test "probably covers this too," confirms the guard's actual shape -- an ALLOW-list of one, not a DENY-list that happens to include two entries -- which is the real reason no special-casing was ever required.