Exercise 1: A Semaphore(initial_value=1) Behaves Exactly Like a Mutex — Possible Solution ==================================================================== THE TEST ------------------------------ sem_as_mutex = Semaphore(mem, counter_paddr, lock_paddr, initial_value=1) first = sem_as_mutex.try_acquire() second = sem_as_mutex.try_acquire() # while still held sem_as_mutex.release() third = sem_as_mutex.try_acquire() RESULT ------------------------------ Semaphore(initial_value=1): first acquire=True, second acquire (while held)=False after release(), a new acquire: True The pattern is identical to Chapter 2's own Mutex: exactly one holder at a time, a second attempt fails while the first is still active, and release() genuinely frees it for the next caller. WHY THIS ISN'T A COINCIDENCE ------------------------------ Semaphore.try_acquire()'s own logic is: if self.mem.data[self.counter_paddr] > 0: self.mem.data[self.counter_paddr] -= 1 ... return True return False With counter starting at 1: the first call sees 1 > 0, decrements to 0, and succeeds. The second call sees 0 > 0 (False), and fails -- exactly the binary "free or held" behavior a mutex needs. release() increments the counter back to 1, exactly restoring the "one permit available" state. There's no special-cased "binary mode" anywhere in Semaphore's own code -- a mutex is mathematically just the specific case of "how many things can hold this at once" being exactly 1, and a semaphore already IS "how many things can hold this at once," generalized to any starting number. Mutual exclusion isn't a different mechanism from counting permits; it's counting permits where the count happens to be 1. WHY THIS MATTERS BEYOND A NEAT OBSERVATION ------------------------------ It means Chapter 2's own Mutex is genuinely a special case of this chapter's own Semaphore, not two unrelated primitives that happen to look similar. A real kernel (or the site's own Semaphore built here) could implement Mutex as `class Mutex(Semaphore): def __init__(self, mem, lock_paddr): super().__init__(mem, ..., initial_value=1)` instead of maintaining two separate, independently-written implementations -- though this chapter deliberately keeps Chapter 2's own Mutex as its own standalone class, since Semaphore itself is built ON TOP of it (using Mutex to protect the semaphore's own counter), so collapsing the two together would create a circular dependency. WHY THIS WORKS AS AN ANSWER ------------------------------ Testing initial_value=1 directly against the exact same acquire/acquire-fails/release/acquire-succeeds sequence Chapter 2 used for its own Mutex -- rather than just asserting the equivalence in prose -- confirms the claim is a genuine, verified behavioral fact, not just a plausible-sounding analogy.