Operating System - Peterson Solution in Process Synchronization



The process synchronization is a mechanism of synchronizing multiple processes to ensure that they can run without interfering with each other. If multiple processes access shared resources concurrently, it may lead to data inconsistency and unexpected results. To avoid such issues, various synchronization algorithms have been developed.

Peterson's Algorithm is a classic software-based solution for achieving mutual exclusion and synchronization between two processes. It was proposed by Gary L. Peterson in 1981. This chapter will explain the working of Peterson's Algorithm and how it ensures process synchronization.

The Peterson's Algorithm

The idea behind Peterson's Algorithm is to use two shared variables to indicate the intention of each process to enter its critical section. The two variables are −

  • flag[i] − A boolean variable that indicates whether process i wants to enter its critical section.
  • turn − An integer variable that indicates whose turn it is to enter the critical section.

The following are the steps or processes for Peterson's Algorithm −

  • Set the turn to either 0 or 1 to indicating which process can enter its critical section first.
  • Set flag[i] to true, indicating that process i wants to enter its critical section.
  • Set turn to j, the index of the other process.
  • While flag[j] is true and turn equals j, wait.
  • Enter the critical section.
  • Set flag[i] to false, indicating that process i has finished its critical section.
  • Remainder section.

In this algorithm, each process sets its flag to true when it wants to enter the critical section. It then sets the turn variable to the other process, indicating that it is willing to let the other process enter first. The process then enters a busy-wait loop, checking if the other process is interested in entering its critical section and if it's the other process's turn. If both conditions are true, the process waits. Once it can proceed, it enters its critical section.

Properties of Peterson's Algorithm

Peterson's Algorithm satisfies the following properties −

  • Mutual Exclusion − Only one process can be in its critical section at a time.
  • Progress − If no process is in its critical section, and one or more processes want to enter their critical sections, then the selection of the next process to enter the critical section cannot be postponed indefinitely.
  • Bounded Waiting − There exists a limit on the number of times other processes are allowed to enter their critical sections after a process has made a request to enter its critical section and before that request is granted.

Implementation of Peterson's Algorithm

Peterson's Algorithm can be implemented in various programming languages.

import threading
flag = [False, False]
turn = 0

def peterson_process(i):
    global flag, turn
    j = 1 - i
    count = 0
    while True:
        # Indicate that process i wants to enter its critical section
        flag[i] = True
        # Set turn to the other process
        turn = j
        # Wait until the other process is not interested or it's this process's turn
        while flag[j] and turn == j:
            pass  # Busy wait
        # Critical Section
        print(f"Process {i} in critical section")
        # Exit Section
        flag[i] = False
        # Remainder Section
        print(f"Process {i} in remainder section")
        count += 1
        # Limit the number of iterations for demonstration
        if count >= 5:
            break

# Create two threads for the two processes
thread1 = threading.Thread(target=peterson_process, args=(0,))
thread2 = threading.Thread(target=peterson_process, args=(1,))
thread1.start()
thread2.start()

The output of the above code will be −

Process 0 in critical section
Process 0 in remainder section
Process 1 in critical section
Process 1 in remainder section
...
#include <iostream>
#include <thread>
#include <atomic>
using namespace std;

atomic<bool> flag[2] = {false, false};
atomic<int> turn;

void peterson_process(int i) {
    int j = 1 - i;
    while (true) {
        // Indicate that process i wants to enter its critical section
        flag[i] = true;
        // Set turn to the other process
        turn = j;
        // Wait until the other process is not interested or it's this process's turn
        while (flag[j] && turn == j) {
            // Busy wait
        }
        // Critical Section
        cout << "Process " << i << " in critical section" << endl;
        // Exit Section
        flag[i] = false;
        // Remainder Section
        cout << "Process " << i << " in remainder section" << endl;
    }
}

int main() {
    thread t1(peterson_process, 0);
    thread t2(peterson_process, 1);
    t1.join();
    t2.join();
    return 0;
}

The output of the above code will be −

Process 0 in critical section
Process 0 in remainder section
Process 1 in critical section
Process 1 in remainder section
...
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class PetersonsAlgorithm {
    private static AtomicBoolean[] flag = {new AtomicBoolean(false), new AtomicBoolean(false)};
    private static AtomicInteger turn = new AtomicInteger(0);

    public static void petersonProcess(int i) {
        int j = 1 - i;
        while (true) {
            // Indicate that process i wants to enter its critical section
            flag[i].set(true);
            // Set turn to the other process
            turn.set(j);
            // Wait until the other process is not interested or it's this process's turn
            while (flag[j].get() && turn.get() == j) {
                // Busy wait
            }
            // Critical Section
            System.out.println("Process " + i + " in critical section");
            // Exit Section
            flag[i].set(false);
            // Remainder Section
            System.out.println("Process " + i + " in remainder section");
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> petersonProcess(0));
        Thread t2 = new Thread(() -> petersonProcess(1));
        t1.start();
        t2.start();
    }
}

The output of the above code will be −

Process 0 in critical section
Process 0 in remainder section
Process 1 in critical section
Process 1 in remainder section
...

Example Use Cases of Peterson's Algorithm

Peterson's Algorithm can be used in various situations where process synchronization is required. Some common use cases include −

  • Accessing Shared Resources − When two entities need to access shared resources like files, databases, or memory, Peterson's Algorithm can ensure that only one entity accesses the resource at a time. For example, peterson algorithm can be used to synchronize access of printer to two processes.
  • Prevent Deadlocks − Peterson's Algorithm prevent deadlocks by ensuring mutual exclusion and bounded waiting.
  • Real-Time Systems − In real-time systems, peterson's algorithm can be used to ensure that critical tasks are executed without interference from other tasks.

Conclusion

Peterson's Algorithm is a simple and effective solution for achieving process synchronization and mutual exclusion between two processes. By using shared variables to indicate the intention of each process to enter its critical section, the algorithm ensures that only one process can access the critical section at a time.

Advertisements