### Peterson algorithm (for two processes) Let's try to enforce MUTEX with just 2 processes. ##### 1st attempt: ``` lock(i) := AFTER_YOU <- i wait AFTER_YOU != i return unlock(i) := return ``` This protocol satisfies MUTEX, but suffers from deadlock (if one process never locks). ##### 2nd attempt: ``` Initialize FLAG[0] and FLAG[1] to down lock(i) := FLAG[i] <- up wait FLAG[1-i] = down return unlock(i) := FLAG[i] <- down return ``` Still suffers from deadlock if both processes simultaneously raise their flag. ##### Correct solution: ``` Initialize FLAG[0] and FLAG[1] to down lock(i) := FLAG[i] <- up AFTER_YOU <- i wait FLAG[1-i] = down OR AFTER_YOU != i return unlock(i) := FLAG[i] <- down return ``` **Features:** - it satisfies MUTEX - it satisfies bounded bypass, with bound = 1 - it requires 2 one-bit SRSW registers (the flags and 1 one-bit MRMW registers (AFTER_YOU) - Each lock-unlock requires 5 accesses to the registers (4 for lock and 1 for unlock) ##### MUTEX proof >[!info] >Remember that, in the [[#Correct solution|correct implementation]], there is this line: `wait (FLAG[1-i] = down OR AFTER_YOU != i)`. Thus, to access the critical section (the `return` instruction of the `lock` function), the possibilities are just the two discussed above. **How has p0 entered its CS?** a) `FLAG[1] = down`, this is possible only with the following interleaving: ![[Pasted image 20250303100721.png]] b) `AFTER_YOU = 1`, this is possible only with the following interleaving: ![[Pasted image 20250303100953.png]] ##### Bounded Bypass proof (with bound = 1) - If the wait condition is true, then it wins (and waits 0). - Otherwise, it must be that `FLAG[1] = UP` **AND** `AFTER_YOU = 0` (quindi p1 ha invocato il lock e p0 dovrà aspettare). - If p1 never locks anymore then p0 will eventually read `F[1]` and win (waiting 1). - It is possible tho that p1 locks again: - if p0 reads `F[1]` before p1 locks, then p0 wins (waiting 1) - otherwise p1 sets A_Y to 1 and suspends in its wait (`F[0] = up AND A_Y = 1`), p0 will eventually read `F[1]` and win (waiting 1). ### Peterson algorithm ($n$ processes) - FLAG now has $n$ levels (from 0 to n-1) - level 0 means down - level >0 means involved in the lock - Every level has its own AFTER_YOU ``` Initialize FLAG[i] to 0, for all i lock(i) := for lev = 1 to n-1 do FLAG[i] <- lev AFTER_YOU[lev] <- i wait (∀k!=i, FLAG[k] < lev OR AFTER_YOU[lev] != i) return unlock(i) := FLAG[i] <- 0 return ``` We say that: $p_i$ is at level $h$ when it exits from the $h$-th wait $\to$ a process at level $h$ is at any level $<= h$ >[!info] What is the `wait` condition actually checking? >In the wait condition we check that all other processes are at a lower level. ##### MUTEX proof >[!def] Lemma >For every $ℓ \in \{0,\dots,n-1\}$ , at most n-ℓ processes are at level ℓ, this implies MUTEX by taking ℓ = n-1 Proof by induction on ℓ Base (ℓ=0): trivial Induction (true for ℓ, to be proved for ℓ+1): - p at level ℓ can increase its level by writing its FLAG at ℓ+1 and its index in $A_Y[ℓ+1]$ - let $p_x$ be the last one that writes `A_Y[ℓ+1]`, so `A_Y[ℓ+1]=x` - for $p_x$ to pass at level ℓ+1, it must be that $∀k≠x. F[k] < ℓ+1$, then $p_x$ is the only proc at level ℓ+1 and the thesis holds, since 1<=n-ℓ-1 - otherwise, $p_x$ is blocked in the wait and so we have at most n-ℓ-1 processes at level ℓ+1: those at level ℓ, that by induction are at most n-ℓ, except for px that is blocked in its wait. ##### Starvation freedom proof **Lemma:** every process at level ℓ ($\leq n-1$) eventually wins $\to$ starvation freedom holds by taking $ℓ=0$. Reverse induction on ℓ Base ($ℓ=n-1$): trivial Induction (true for ℓ+1, to be proved for ℓ): - Assume a $p_x$ blocked at level ℓ (aka blocked in its ℓ+1-th wait) $\to \exists k\neq x, F[k]\geqℓ+1 \land A\_Y[ℓ+1]=x$ - If some $p_{y}$ will eventually set $A\_Y[ℓ+1]$ to $y$, then $p_x$ will eventually exit from its wait and pass to level ℓ+1 - Otherwise, let $G = \{p_{i}: F[i] \geq ℓ+1\}$ and $L=\{p_{i}:F[i]<ℓ+1\}$ - if $p \in L$, it will never enter its ℓ+1-th loop (as it would write $A_Y[ℓ+1]$ and it will unblock $p_x$, but we are assuming that it is blocked) - all $p \in G$ will eventually win (by induction) and move to L - $\to$ eventually, $p_{x}$ will be the only one in its ℓ+1-th loop, with all the other processes at level <ℓ+1 - $\to$ $p_{x}$ will eventually pass to level ℓ+1 and win (by induction) ##### Peterson algorithm cost - $n$ MRSW registers of $\lceil \log_{2} n\rceil$ bits (FLAG) - $n-1$ MRMW registers of $\lceil \log_{2}n \rceil$ bits (AFTER_YOU) - $(n-1)\times(n+2)$ accesses for locking and 1 access for unlocking It satisfies MUTEX and starvation freedom. It does not satisfy bounded bypass: - consider 3 processes, one sleeping in its first wait, the others alternating in the CS - when the first process wakes up, it can pass to level 2 and eventually win - but the sleep can be arbitrary long and in the meanwhile the other two processes may have entered an unbounded number of CSs Easy to generalize to k-MUTEX. Peterson's algorithm cost $O(n^2)$ A first way to reduce this cost is by using a tournament of MUTEX between pairs of processes: ![[Pasted image 20250304082459.png|350]] Of course this is a binary tree, and the height of a binary tree is logaritmic to the number of leaves. A process then wins after $\lceil \log_{2}n \rceil$ competitions $\to O(\log n)$ cost. But we can do better. Let's see an idea of a constant-time algorithm.