Exercise 1: A Spurious release() Is Silently Harmless, or Silently Dangerous — Possible Solution ==================================================================== THE TEST -- PART 1: HARMLESS CASE ------------------------------ mutex.release() # lock was already free mutex.release() # called again -- still free RESULT ------------------------------ calling release() twice on an already-free lock: no error, lock value is 0 No exception, no corruption -- the lock's own stored value is simply set to 0 again, which it already was. release() is defined as `self.mem.data[self.lock_paddr] = 0` -- an unconditional write, not a decrement or a check against any prior state, so calling it redundantly on an already-free lock is a genuine no-op. THE TEST -- PART 2: DANGEROUS CASE ------------------------------ x_got_it = mutex.try_acquire() # X legitimately acquires the lock mutex.release() # a bug in UNRELATED code releases it y_got_it = mutex.try_acquire() # Y acquires it too RESULT ------------------------------ process X acquires the lock: got it = True a spurious release() from unrelated code lets process Y acquire it too: got it = True Both X and Y now believe they hold the lock -- X never released it itself, and is presumably still mid-critical-section. WHY release() CAN'T TELL THE DIFFERENCE ------------------------------ release() has no concept of "who" is calling it. It doesn't check whether the calling process is the one that originally succeeded at try_acquire(), and it has no record of who that was at all -- the lock is just a single shared byte, 0 or 1, with no ownership information attached. Any code with access to the shared physical frame can call release() at any time, whether or not it's the legitimate holder. WHY THIS MATTERS ------------------------------ This is a genuinely real category of bug in actual synchronization code -- an unmatched acquire/release pair (a release without a corresponding acquire, or a release triggered by the wrong code path, often from a mistake in exception handling) silently reopens a critical section while another piece of code still believes it's protected. The lock's own binary correctness (it's always cleanly 0 or 1, never some invalid third state) says nothing about whether the HIGHER-LEVEL guarantee -- "only one piece of code is inside the critical section at a time" -- still holds. That guarantee depends entirely on every caller's own acquire/release discipline being correct, which the lock itself has no way to verify or enforce. WHY THIS WORKS AS AN ANSWER ------------------------------ Testing both the harmless case (release on an already-free lock, in isolation) and the dangerous case (a spurious release while a legitimate holder is still active) separates two genuinely different questions -- "does this corrupt the lock's own data" (no) from "does this defeat the guarantee the lock exists to provide" (yes) -- which a single test of either case alone wouldn't have distinguished.