= Solution
Use one <monitor> to protect both the occupied flag and the slot. `Option` supplies an empty marker without confusing an empty slot with a valid `null` payload.
``
class MonitorMemory[T] extends Memory[T] {
private var slot: Option[T] = None
def write(msg: T): Unit = synchronized {
while (slot.nonEmpty) wait()
slot = Some(msg)
notifyAll()
}
def read: T = synchronized {
while (slot.isEmpty) wait()
val result = slot.get
slot = None
notifyAll()
result
}
}
``
A writer changes empty to full while holding the <monitor>; a reader changes full to empty while holding the same <monitor>. The corresponding successful assignment or removal is its <linearization point>. No second writer can overwrite a full slot, and no two readers can remove one value. Rechecking in a `while` handles <spurious wakeups> and competition after notification.
There are two different waiting predicates on the same <monitor>: writers need empty, readers need full. A single arbitrary `notify` could wake a caller of the wrong kind. For example, after a reader empties the slot, waking another reader leaves it waiting again while a writer remains asleep, potentially stranding the computation. `notifyAll` prevents that <lost wake-up> pattern by allowing every waiter to recheck. Thus,
$$
\boxed{\text{one protected slot, two while-guarded waits, and notifyAll after either state change}.}
$$
With many users, each change wakes numerous <software threads> even though at most one can use that new slot state. Most reacquire the <monitor>, find their predicate false and sleep again. This <thundering herd> wastes <context switches> and lock acquisitions. It also supplies no fairness or arrival-order guarantee.
Back to article page