Peterson's algorithm

Peterson's Algorithm is a classic software-based solution used for process synchronization, specifically to solve the Critical Section Problem for two processes.

1. Critical Section Problem Requirements

To ensure proper synchronization, any solution to the Critical Section Problem must satisfy three requirements:

  1. Mutual Exclusion: Only one process can be inside the critical section at any given time.

  2. Progress: If no process is in the critical section and some processes want to enter, only those processes not in their remainder section can participate in the decision of which process will enter the critical section next. This decision cannot be postponed indefinitely.

  3. Bounded Waiting: There must be a limit on the number of times other processes are allowed to enter their critical section after a process has made a request to enter its critical section and before that request is granted.

2. Algorithm Variables

Peterson's Algorithm uses two shared variables, initialized globally:

  • boolean flag[2]: An array where flag[i] = true indicates that process PiP_i wants to enter the critical section. Initially, both flag[0] and flag[1] are false.

  • int turn: An integer variable, initialized to either 00 or 11. It indicates whose turn it is to enter the critical section if both processes want to enter simultaneously.

3. Algorithm for Process PiP_i

Let's consider process P<em>iP<em>i and the other process as P</em>jP</em>j. The algorithm for process PiP_i is as follows:

do {
    flag[i] = true;
    turn = j; // Indicate that it's Pj's turn
    while (flag[j] && turn == j); // Wait if Pj wants to enter AND it's Pj's turn

    // CRITICAL SECTION

    flag[i] = false; // Indicate Pi is done with critical section

    // REMAINDER SECTION
} while (true);
4. How it Satisfies Requirements
  • Mutual Exclusion: If process P<em>iP<em>i is in its critical section, it means it broke out of the while loop. This could only happen if either flag[j] was false (meaning P</em>jP</em>j didn't want to enter) or turn was i (meaning P<em>jP<em>j yielded its turn). If both P</em>iP</em>i and PjP_j try to enter simultaneously, one will set turn to the other's index, and the other will wait.

  • Progress: If P<em>iP<em>i wants to enter and P</em>jP</em>j doesn't (flag[j] is false), then PiP_i proceeds directly. If both want to enter, the turn variable resolves the contention, allowing one to proceed.

  • Bounded Waiting: If P<em>iP<em>i is waiting, it means flag[j] is true and turn == j. This state means P</em>jP</em>j is either in its critical section or about to enter. Once P<em>jP<em>j exits its critical section, it sets flag[j] = false, allowing P</em>iP</em>i to enter. If P<em>jP<em>j tries to re-enter, it will set turn = i, giving P</em>iP</em>i priority. Thus, waiting is bounded.

5. Advantages
  • Simple to understand and implement.

  • Guarantees mutual exclusion, progress, and bounded waiting.

6. Disadvantages
  • Limited to Two Processes: It cannot be easily extended to synchronize more than two processes.

  • Busy Waiting (Spin Lock): Processes actively wait in a loop while checking conditions, consuming CPU cycles unnecessarily. This is inefficient, especially in multi-programming environments where a process could yield the CPU instead.