Fairness

Unfair semaphore

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))
   

Another unfair semaphore

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))
    

Fair semaphore

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.

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