Exercise 1: Preemption With Only One Process in the System — Possible Solution ==================================================================== THE TEST ------------------------------ solo = kernel.create_process(num_pages=1) solo.transition(ProcessState.RUNNING) fpk = FixedPreemptiveKernel(cpu, scheduler) # scheduler.ready_queue is empty fpk.current_pcb = solo for _ in range(20): fpk.run_instruction() RESULT ------------------------------ quantum lengths logged: [5, 5, 5, 5] process still running the whole time: True ProcessState.RUNNING Four real timer interrupts fire (20 instructions / QUANTUM 5 = 4), each one logged with a full, real quantum length of 5. solo is still the exact same object (fpk.current_pcb is solo), and its own state never left RUNNING. WHY THE INTERRUPT STILL FIRES ON SCHEDULE ------------------------------ run_instruction() has no awareness of how many processes exist -- it only counts raw instructions and compares against QUANTUM: self.ticks_since_switch += 1 if self.ticks_since_switch >= self.QUANTUM: self.interrupts.dispatch(TIMER_INTERRUPT) The timer doesn't check "is there anyone else waiting" before firing -- it fires purely on its own schedule, every single time. WHY NOTHING OBSERVABLE CHANGES ------------------------------ Inside _on_timer(), the very next thing that happens is: next_pcb = self.scheduler.pick_next() self.ticks_since_switch = 0 if next_pcb is None: return pick_next() returns None because scheduler.ready_queue is empty -- there is genuinely no one else to switch to. The tick counter is still reset (this is the fixed version), and the quantum length is still logged, but the function returns immediately afterward. It never reaches context_switch_fixed(), so solo's own registers are never saved, its own state never transitions to READY and back, and cpu.current_pid never changes. The interrupt is real, it's handled, and its effect is exactly nothing -- which is the correct behavior when there's genuinely nothing to switch to. WHY THIS WORKS AS AN ANSWER ------------------------------ Logging the quantum length independently of whether a real switch happens (rather than only logging when context_switch_fixed() runs) proves the interrupt is genuinely firing on schedule 4 separate times, not merely "not needed" -- distinguishing "the interrupt didn't fire" from "the interrupt fired but correctly found nothing to do" is exactly the distinction this exercise is testing.