Software Based Solutions in Process Synchronization
Last Updated :
06 Sep, 2025
A software-based solution is a method of solving problems using software programs instead of additional hardware. In operating systems, it means handling tasks like security, error detection, or resource management through software techniques, making the system more flexible and cost-effective.
Various software solutions to the Critical Section Problem are:
- Lock Variable
- Strict Alternation
- Peterson's Algorithm
- Dekker's Algorithm
1. Lock Variable
This is a simple synchronization method that works in user mode and is a busy-waiting solution for multiple processes. It uses a lock variable, lock to manage access to the critical section.
The lock variable can have two values:
- 0: Indicates the critical section is free.
- 1: Indicates the critical section is in use.
When a process wants to enter the critical section, it first checks the lock variable:
- If the value is 0, the process sets it to 1 and enters the critical section.
- If the value is 1, the process waits until it becomes 0.
Entry Section
While (lock! = 0);
Lock = 1;
//Critical Section
Exit Section
Lock =0;
Initially, the lock variable is set to 0. When a process wants to enter the critical section, it goes through an entry section and checks a condition in a while loop.
- The process keeps waiting in the loop until the lock value changes to 1.
- If the critical section is free (lock is 0), the process enters the critical section and sets the lock to 1 to indicate it is in use.
When the process finishes its work in the critical section, it goes through the exit section and sets the lock back to 0, making the critical section available for other processes.
Lock Variable fails to satisfy Bounded Wait. This may be because a process can enter CS multiple times successively while other processes are waiting for their turn to enter CS.
Read more about Lock Variable Synchronization Mechanism.
2. Strict Alternation
The Turn Variable or Strict Alternation Approach is a simple software mechanism used in user mode. It is a busy-waiting solution designed specifically for two processes.
- A turn variable acts as a lock, determining which process can access the critical section.
- The turn alternates between the two processes, ensuring they take turns to enter the critical section.
This method works only for two processes and is not suitable for systems with more than two processes.
For Process Pi: Non - CS while (turn ! = i); Critical Section turn = j; Non - CS | For Process Pj: Non - CS while (turn ! = j); Critical Section turn = i; Non - CS |
|---|
A process can enter the critical section only when the turn variable matches the process's ID (PID). The turn variable can have only two values: i or j.
- In the entry section, process Pi cannot enter the critical section until the turn variable is i. Similarly, process Pj cannot enter until the turn variable is j.
- Initially, the turn variable is set to i, so Pi gets the first chance to enter the critical section. The turn variable stays i while Pi is in the critical section.
- Once Pi finishes its work in the critical section, it sets the turn variable to j. This allows Pj to enter the critical section, and the turn variable stays j until Pj finishes.
This ensures that the two processes alternate access to the critical section.
Strict Alternation never guarantees progress.
3. Peterson’s Algorithm
To handle the problem of Critical Section (CS) Peterson gave an algorithm with a bounded waiting.
Suppose there are N processes (P1, P2, … PN) and each of them at some point need to enter the Critical Section.
A flag[] array of size N is maintained which is by default false and whenever a process need to enter the critical section it has to set its flag as true, i.e. suppose Pi wants to enter so it will set flag[i]=TRUE.
There is another variable called turn which indicates the process number which is currently to enter into the CS. The process that enters into the CS while exiting would change the turn to another number from among the list of ready processes.
var flag[]: array [0..1] of boolean;
turn: 0..1;
%flag[k] means that process[k] is interested in the critical section
flag[0] := FALSE;
flag[1] := FALSE;
turn := random(0..1)
After initialization, each process, which is called process i in the code (the other process is process j), runs the following code:
repeat
flag[i] := TRUE;
turn := j;
while (flag[j] and turn=j) do no-op;
CRITICAL SECTION
flag[i] := FALSE;
REMAINDER SECTION
until FALSE;
Information common to both processes:
turn = 0
flag[0] = FALSE
flag[1] = FALSE
EXAMPLE |
Process 0 | Process 1 |
i = 0, j = 1 | i = 1, j = 0 |
flag[0] := TRUE turn := 1 check (flag[1] = TRUE and turn = 1) - Condition is false because flag[1] = FALSE - Since condition is false, no waiting in while loop - Enter the critical section - Process 0 happens to lose the processor | |
| flag[1] := TRUE turn := 0 check (flag[0] = TRUE and turn = 0) - Since condition is true, it keeps busy waiting until it loses the processor |
- Process 0 resumes and continues until it finishes in the critical section - Leave critical section flag[0] := FALSE - Start executing the remainder (anything else a process does besides using the critical section) - Process 0 happens to lose the processor | |
| check (flag[0] = TRUE and turn = 0) - This condition fails because flag[0] = FALSE - No more busy waiting - Enter the critical section |
Read more about Peterson's Solution.
4. Dekker's Algorithm
Dekker’s Algorithm is one of the earliest known solutions to the Critical Section (CS) problem. It ensures mutual exclusion, progress, and bounded waiting for two processes trying to access a shared resource. Unlike Peterson’s, it was designed before modern atomic instructions and uses shared variables with busy waiting.
Suppose there are two processes (P0 and P1). Each process uses a flag[] array and a shared variable turn.
- flag[i] indicates whether process i wants to enter the CS.
- turn indicates whose turn it is to enter the CS if both want to enter simultaneously.
Initialization:
var flag[]: array [0..1] of boolean;
turn: 0..1;
flag[0] := FALSE;
flag[1] := FALSE;
turn := 0
Algorithm (for each process i):
Let the other process be j.
repeat
flag[i] := TRUE;
while flag[j] do
if turn = j then
begin
flag[i] := FALSE;
while turn = j do
no-op; % wait
flag[i] := TRUE;
end;
CRITICAL SECTION
turn := j;
flag[i] := FALSE;
REMAINDER SECTION
until FALSE;
Working:
1. Intention to enter CS: Each process sets its flag[i] := TRUE to indicate that it wants to enter the Critical Section (CS).
2. Check if the other process wants to enter: If the other process (j) has also set flag[j] = TRUE, then both want to enter at the same time.
3. Turn mechanism
- The
turn variable decides which process gets priority. - If it is the other process’s turn (
turn = j), the current process backs off by setting flag[i] := FALSE and waits until turn ≠ j. - After waiting, it again sets
flag[i] := TRUE and tries to enter.
4. Critical Section (CS): Once the conditions are satisfied, the process enters the CS safely, ensuring only one process executes inside CS at a time.
5. Exit section: After finishing CS, the process
- Gives priority to the other process by setting
turn := j. - Resets its flag with
flag[i] := FALSE.
6. Remainder Section: The process executes its normal code (non-critical part) and then repeats the cycle.
Before this many
Final and completed Solution: Idea is to use favoured thread notion to determine entry to the critical section. Favoured thread alternates between the thread providing mutual exclusion and avoiding deadlock, indefinite postponement, or lockstep synchronization.
C++
Main()
{
// to denote which thread will enter next
int favouredthread = 1;
// flags to indicate if each thread is in
// queue to enter its critical section
boolean thread1wantstoenter = false;
boolean thread2wantstoenter = false;
startThreads();
}
Thread1()
{
do {
thread1wantstoenter = true;
// entry section
// wait until thread2 wants to enter
// its critical section
while (thread2wantstoenter == true) {
// if 2nd thread is more favored
if (favaouredthread == 2) {
// gives access to other thread
thread1wantstoenter = false;
// wait until this thread is favored
while (favouredthread == 2)
;
thread1wantstoenter = true;
}
}
// critical section
// favor the 2nd thread
favouredthread = 2;
// exit section
// indicate thread1 has completed
// its critical section
thread1wantstoenter = false;
// remainder section
} while (completed == false)
}
Thread2()
{
do {
thread2wantstoenter = true;
// entry section
// wait until thread1 wants to enter
// its critical section
while (thread1wantstoenter == true) {
// if 1st thread is more favored
if (favaouredthread == 1) {
// gives access to other thread
thread2wantstoenter = false;
// wait until this thread is favored
while (favouredthread == 1)
;
thread2wantstoenter = true;
}
}
// critical section
// favour the 1st thread
favouredthread = 1;
// exit section
// indicate thread2 has completed
// its critical section
thread2wantstoenter = false;
// remainder section
} while (completed == false)
}
C
#include <stdio.h>
#include <pthread.h>
// to denote which thread will enter next
int favouredthread = 1;
// flags to indicate if each thread is in
// queue to enter its critical section
int thread1wantstoenter = 0;
int thread2wantstoenter = 0;
int completed = 0;
void* Thread1() {
while (!completed) {
thread1wantstoenter = 1;
// entry section
// wait until thread2 wants to enter
// its critical section
while (thread2wantstoenter == 1) {
// if 2nd thread is more favored
if (favouredthread == 2) {
// gives access to other thread
thread1wantstoenter = 0;
// wait until this thread is favored
while (favouredthread == 2) {
;
}
thread1wantstoenter = 1;
}
}
// critical section
// favor the 2nd thread
favouredthread = 2;
// exit section
// indicate thread1 has completed
// its critical section
thread1wantstoenter = 0;
// remainder section
}
return NULL;
}
void* Thread2() {
while (!completed) {
thread2wantstoenter = 1;
// entry section
// wait until thread1 wants to enter
// its critical section
while (thread1wantstoenter == 1) {
// if 1st thread is more favored
if (favouredthread == 1) {
// gives access to other thread
thread2wantstoenter = 0;
// wait until this thread is favored
while (favouredthread == 1) {
;
}
thread2wantstoenter = 1;
}
}
// critical section
// favour the 1st thread
favouredthread = 1;
// exit section
// indicate thread2 has completed
// its critical section
thread2wantstoenter = 0;
// remainder section
}
return NULL;
}
void startThreads() {
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, Thread1, NULL);
pthread_create(&thread2, NULL, Thread2, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
}
int main() {
startThreads();
return 0;
}
Java
public class Main {
// to denote which thread will enter next
static int favouredthread = 1;
// flags to indicate if each thread is in
// queue to enter its critical section
static boolean thread1wantstoenter = false;
static boolean thread2wantstoenter = false;
public static void main(String[] args) {
startThreads();
}
static void startThreads() {
Thread thread1 = new Thread(new Thread1());
Thread thread2 = new Thread(new Thread2());
thread1.start();
thread2.start();
}
}
class Thread1 implements Runnable {
@Override
public void run() {
do {
Main.thread1wantstoenter = true;
// entry section
// wait until thread2 wants to enter
// its critical section
while (Main.thread2wantstoenter == true) {
// if 2nd thread is more favored
if (Main.favouredthread == 2) {
// gives access to other thread
Main.thread1wantstoenter = false;
// wait until this thread is favored
while (Main.favouredthread == 2);
Main.thread1wantstoenter = true;
}
}
// critical section
// favor the 2nd thread
Main.favouredthread = 2;
// exit section
// indicate thread1 has completed
// its critical section
Main.thread1wantstoenter = false;
// remainder section
} while (completed == false);
}
}
class Thread2 implements Runnable {
@Override
public void run() {
do {
Main.thread2wantstoenter = true;
// entry section
// wait until thread1 wants to enter
// its critical section
while (Main.thread1wantstoenter == true) {
// if 1st thread is more favored
if (Main.favouredthread == 1) {
// gives access to other thread
Main.thread2wantstoenter = false;
// wait until this thread is favored
while (Main.favouredthread == 1);
Main.thread2wantstoenter = true;
}
}
// critical section
// favour the 1st thread
Main.favouredthread = 1;
// exit section
// indicate thread2 has completed
// its critical section
Main.thread2wantstoenter = false;
// remainder section
} while (completed == false);
}
}
Python
import threading
# to denote which thread will enter next
favouredthread = 1
# flags to indicate if each thread is in
# queue to enter its critical section
thread1wantstoenter = False
thread2wantstoenter = False
def Thread1():
global favouredthread, thread1wantstoenter, thread2wantstoenter, completed
while not completed:
thread1wantstoenter = True
# entry section
# wait until thread2 wants to enter
# its critical section
while thread2wantstoenter == True:
# if 2nd thread is more favored
if favouredthread == 2:
# gives access to other thread
thread1wantstoenter = False
# wait until this thread is favored
while favouredthread == 2:
pass
thread1wantstoenter = True
# critical section
# favor the 2nd thread
favouredthread = 2
# exit section
# indicate thread1 has completed
# its critical section
thread1wantstoenter = False
# remainder section
def Thread2():
global favouredthread, thread1wantstoenter, thread2wantstoenter, completed
while not completed:
thread2wantstoenter = True
# entry section
# wait until thread1 wants to enter
# its critical section
while thread1wantstoenter == True:
# if 1st thread is more favored
if favouredthread == 1:
# gives access to other thread
thread2wantstoenter = False
# wait until this thread is favored
while favouredthread == 1:
pass
thread2wantstoenter = True
# critical section
# favour the 1st thread
favouredthread = 1
# exit section
# indicate thread2 has completed
# its critical section
thread2wantstoenter = False
# remainder section
def startThreads():
thread1 = threading.Thread(target=Thread1)
thread2 = threading.Thread(target=Thread2)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
if __name__ == "__main__":
startThreads()
This version guarantees a complete solution to the critical solution problem.
Read more about Dekker's Algorithm.
Explore
GATE Syllabus
GATE CS Tutorials
GATE DA Tutorials
Aptitude
Practice Content