From the counter-examples provided by the TLC model-checker, explain why
the algorithm does not satisfy property `NoStarvation`

without
fairness hypothesis (i.e. when PlusCal option `-sf`

is removed)?
And with weak fairness hypothesis (with PlusCal option `-wf` instead
of `-sf`

)?

EXTENDS Naturals, TLC CONSTANTS N (* Number of processes *) (* PlusCal options (-sf) *) (* --algorithm Semaphore { variables s = 1; (* Shared semaphore counter *) macro P(sem) { await sem > 0; sem := sem-1 } macro V(sem) { sem := sem+1 } process (P \in 1..N) { loop: while (TRUE) { lock: P(s); cs: skip; (* In the critical section *) unlock: V(s) } } } *) \* BEGIN TRANSLATION \* END TRANSLATION (* Atomic propositions *) CS(p) == pc[p] = "cs" REQ(p) == pc[p] = "lock" (* In every state, there is at most one process in the critical section *) Mutex == [](\A p \in 1..N: \A q \in (1..N \{p}): ~(CS(p) /\ CS(q))) (* Every process that tries to enter the critical section is eventually granted the access to the critical section *) NoStarvation == \A p \in 1..N: [](REQ(p) => <> CS(p))

Explain why the implementation of a semaphore below does not guarantee
requirement `NoStarvation`

, even under strong fairness hypothesis
(i.e. -sf).

EXTENDS Naturals, TLC CONSTANTS N (* Number of processes *) (* PlusCal options (-sf) *) (* --algorithm Semaphore { variables s = 1; (* Shared semaphore counter *) process (P \in 1..N) variables go = FALSE; (* Clearing flag *) { loop: while (TRUE) { lock: if (s > 0) { (* Atomic test-and-set *) s := s-1; go := TRUE }; testing: if (go = FALSE) { goto lock }; cs: skip; (* In the critical section *) unlock: s := s+1; go := FALSE } } } *) \* BEGIN TRANSLATION \* END TRANSLATION (* Atomic propositions *) CS(p) == pc[p] = "cs" REQ(p) == pc[p] = "lock" (* In every state, there is at most one process in the critical section *) Mutex == [](\A p \in 1..N: \A q \in (1..N \{p}): ~(CS(p) /\ CS(q))) (* Every process that tries to enter the critical section is eventually granted the access to the critical section *) NoStarvation == \A p \in 1..N: [](REQ(p) => <> CS(p))

In real life implementations, semaphores have to guarantee fairness. Hence, semaphores are not implemented as a simple integer variable. They keep track of waiting processes and use an access policy that guarantee that every waiting process eventually accesses the critical section.

- Implement a fair semaphore using the access policy of your choice.
- Check that the resulting algorithm satisfies all requirements under
weak fairness assumption on the transition relation (i.e.
`-wfNext`

).