Exercise 2: The Race Loses Decrements Just as Easily as Increments — Possible Solution ==================================================================== THE TEST ------------------------------ # shared value starts at 100 (mid-range, so neither direction wraps) incrementer.LOAD # R1 = 100 # -- context switch here -- decrementer.LOAD, DEC, STORE # shared value: 100 -> 99 # -- switch back to incrementer -- incrementer.INC, STORE # R1 was still 100 (stale) -> 101, STORE RESULT ------------------------------ incrementer executes LOAD: R1 = 100 decrementer completes a full decrement: shared value is now 99 incrementer resumes with its OWN stale R1, finishes: shared value is now 101 one +1 and one -1 against a starting value of 100 -- correct result should be 100 -- got 101 The decrementer's own real, completed decrement (100 -> 99) is completely erased. The final value, 101, reflects ONLY the incrementer's own +1 applied to the ORIGINAL value (100), as if the decrementer had never run at all. WHY THE RACE DOESN'T CARE ABOUT DIRECTION ------------------------------ The bug is entirely about WHEN a process's own R1 was populated relative to when other processes modify the shared value -- not about what operation R1 is used for afterward. The incrementer's R1 became stale the instant execution moved away from it (right after its own LOAD), regardless of whether the process holding the CPU next was about to add, subtract, multiply, or do anything else to the shared value. INC and DEC are both just "take whatever R1 currently is, and change it" -- the STORE at the end always overwrites the shared location with a value computed from R1's own stale snapshot, with no awareness that anything else touched the shared location in between. WHY THIS GENERALIZES BEYOND +1/-1 ------------------------------ This is exactly why Chapter 1's own opening claim was framed as "an unprotected read-modify-write," not "an unprotected increment." Any operation that follows the same LOAD -> compute -> STORE shape -- appending to a shared list, updating a shared dictionary entry, toggling a shared flag -- is vulnerable to the identical failure mode, for the identical structural reason. The fix Chapter 2 builds (a real mutex) has to protect the WHOLE read-modify-write sequence as a single indivisible unit, regardless of what specific computation happens in the middle. WHY THIS WORKS AS AN ANSWER ------------------------------ Choosing a starting value (100) safely away from either edge of the byte range, and mixing two genuinely different operations (increment and decrement) rather than repeating the same one twice, isolates whether the bug is about a SPECIFIC operation or about the SHAPE of the access pattern -- confirming it's the shape, not the operation, that causes the lost update.