= Solution
A <counting semaphore> holds a nonnegative number of permits. `down` waits until a permit is available and consumes one; `up` returns a permit and can release a waiting caller. A <binary semaphore> restricts the number available to zero or one. Unlike a <mutex>, a <semaphore> need not be released by the same <software thread> that acquired it; this permits producer-to-consumer signalling.
Interpret `maxCount` as the initial number of permits and, for this bounded implementation, the maximum permitted number of unmatched releases. An `up` beyond that capacity is a programming error, rather than a blocking operation. Clients obey this release discipline. If an unbounded <counting semaphore> is desired instead, remove the upper-bound requirement and use a sufficiently wide counter; a capacity-one instance is binary only under the corresponding balanced-use discipline.
``
class CountingSemaphore(maxCount: Int) extends Semaphore {
require(maxCount > 0)
private var count = maxCount
def down: Unit = synchronized {
while (count == 0) wait()
count -= 1
}
def up: Unit = synchronized {
require(count < maxCount, "unmatched semaphore release")
count += 1
notify()
}
}
``
All accesses to `count` occur under the same <monitor>. `wait` atomically releases that <monitor> while sleeping, then reacquires it before returning. The `while` is needed because of <spurious wakeups> and because another caller can consume the returned permit before the notified caller reacquires the <monitor>. Only `down` callers wait here, and they all wait on the same predicate; hence waking one with `notify` is sufficient. A later `up` notifies another waiter if necessary. This use of `notify` will not be safe for the mixed predicates in the next part.<Java 8 Object API documentation>[notify and wait: arbitrary notification choice, monitor reacquisition and spurious wakeups]{ref}
A <binary semaphore> initially available is `new CountingSemaphore(1)`. To initialize it unavailable, call its `down` once during construction, before publishing it to other <software threads>. Its state obeys
$$
\boxed{0\leq\mathrm{count}\leq\mathrm{maxCount};\quad\mathrm{down}:c\mapsto c-1\ (c>0),\quad\mathrm{up}:c\mapsto c+1.}
$$
This implementation provides no first-come-first-served guarantee: a new caller can acquire the <monitor> before a notified waiter. The following examples assume ordinary successful execution without cancellation or interruption of a partially completed buffer operation.
Back to article page