master-degree-notes/Concurrent Systems/notes/1b - Peterson algorithm.md

5.5 KiB
Raw Blame History

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

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.