Exercise 2: Switching a Process to Itself — Possible Solution ==================================================================== THE TEST ------------------------------ p = kernel.create_process(num_pages=1) cpu = CPU() p.transition(ProcessState.RUNNING) cpu.current_pid = p.pid cpu.registers['PC'] = 77 context_switch_fixed(cpu, p, p) # the SAME process on both sides print(cpu.registers) print(p.state) RESULT ------------------------------ CPU registers after context_switch_fixed(cpu, p, p): {'PC': 77, 'ACC': 0, 'R1': 0, 'R2': 0} process p's own final state: RUNNING No exception, no corruption. The CPU's registers are completely unchanged (still PC=77), and the process ends the call back in RUNNING -- exactly where it started. WHY IT DOESN'T RAISE AN ERROR ------------------------------ A first guess might reasonably be that this should fail -- the function's own docstring-level intent is "switch AWAY from one process, TO a different one," and passing the same process twice seems like it should trip up the RUNNING -> READY -> RUNNING sequence somehow. It doesn't, because Chapter 4's own ALLOWED_TRANSITIONS genuinely permits RUNNING -> READY -> RUNNING as two separate, valid steps -- there's no rule against a process passing back through READY and immediately becoming RUNNING again; that's precisely what happens every time a process gets preempted and then immediately rescheduled with nothing else having run in between. WHY THE REGISTERS END UP GENUINELY UNCHANGED ------------------------------ Trace through context_switch_fixed(cpu, old_pcb=p, new_pcb=p) step by step: 1. old_pcb.registers = dict(cpu.registers) -- p.registers now holds {'PC': 77, ...} 2. old_pcb.transition(READY) -- p is now READY 3. cpu.registers = dict(new_pcb.registers) -- new_pcb IS p, so this reads BACK the exact dict just saved in step 1 4. new_pcb.transition(RUNNING) -- p (still the same object) goes READY -> RUNNING Because old_pcb and new_pcb are literally the same Python object, step 3 reads the exact value step 1 just wrote. The "save" and the "load" cancel out perfectly, and the two state transitions (RUNNING -> READY, READY -> RUNNING) both happen to be legal on their own. WHY THIS IS HONESTLY DESCRIBED AS AN ACCIDENT, NOT A DESIGN ------------------------------ Nothing in context_switch_fixed()'s own code checks "are old_pcb and new_pcb the same object" and special-cases it -- the function has no idea this situation is even possible. It happens to behave correctly here purely because of the specific order the four steps run in. A real scheduler should still never intentionally call this function with the same process on both sides -- relying on an accidental, unexamined correctness is a fragile habit, even when, as here, it happens to hold up. WHY THIS WORKS AS AN ANSWER ------------------------------ Reporting the ACTUAL observed behavior -- a real no-op -- rather than the more "obvious"-sounding guess (an error, or silent corruption) demonstrates the value of testing an edge case directly instead of reasoning about what "should" happen from the function's own stated purpose alone.