Exercise 3: Forgetting to Reset Priority to Base After a Run — Possible Solution ==================================================================== THE TEST ------------------------------ def run_priority_no_reset(cpu, scheduler, current_pcb, current_priority): scheduler.age_waiting() # aging itself IS called correctly picked = scheduler.pick_next() next_pcb, next_priority = picked context_switch_fixed(cpu, current_pcb, next_pcb) scheduler.add(current_pcb, current_priority) # BUG: re-uses the OLD value return next_pcb, next_priority # 3 processes, priorities 10/5/1, run for 200 turns RESULT ------------------------------ turns over 201: {high: 71, med: 67, low: 63} final stored priorities of whichever processes are still waiting: {..., ...: 139, ...: 138} Every process ends up with roughly the same number of turns (71/67/63 -- nearly identical), and the two processes still sitting in the queue at the end have priorities around 138-139, dramatically higher than their original 10/5/1 starting values. WHY THE PRIORITIES GROW WITHOUT BOUND ------------------------------ age_waiting() correctly adds 1 to every WAITING process's own stored priority, every single turn -- that part of the fix is genuinely correct. But scheduler.add(current_pcb, current_priority) re-adds the OUTGOING process using current_priority, which is whatever value it was tracked at coming INTO this function call -- not its own true base value (10, 5, or 1). Since that process was itself aging while it sat in the queue before being picked, current_priority is already inflated above its original base by the time it's handed back to add(). Every process's own priority only ever goes UP, turn after turn, forever -- there is no mechanism anywhere that ever brings a value back down. WHY THIS DEFEATS THE ORIGINAL POINT OF HAVING PRIORITIES ------------------------------ Within roughly 100-150 turns, every process's own priority has grown into the same general range (all climbing at the same rate via aging), completely swamping the original 9-point spread between high (10) and low (1). At that point, the scheduler effectively degrades into "whichever process has been waiting the LONGEST wins" -- a FIFO-like behavior, regardless of what each process's own true, intended importance was supposed to be. The near-equal turn counts (71/67/63) confirm this directly: a correctly working priority system should NOT produce nearly identical turn counts for processes with priorities as different as 10, 5, and 1. THE ACTUAL FIX, FOR CONTRAST ------------------------------ The chapter's own working version tracks each process's true base priority separately (a dict keyed by pid) and always re-adds the outgoing process at ITS OWN BASE VALUE, not whatever value it happened to carry in from the aging process: scheduler.add(current_pcb, base_priorities[current_pcb.pid]) This way, aging only ever affects a process WHILE it's genuinely waiting -- the moment it actually gets to run, its priority resets back to what it's supposed to represent, and the cycle can repeat correctly the next time it has to wait again. WHY THIS WORKS AS AN ANSWER ------------------------------ Isolating exactly ONE change from the chapter's own working fix (the reset-to-base step) and confirming the aging call alone isn't enough demonstrates that starvation and "priority means nothing anymore" are two SEPARATE failure modes a scheduler can fall into -- fixing one without the other just trades one broken behavior for a different one.