Prerequisite – Process Synchronization, Inter Process Communication
To obtain such a mutual exclusion, bounded waiting, and progress there have been several algorithms implemented, one of which is Dekker’s Algorithm. To understand the algorithm let’s understand the solution to the critical section problem first.
A process is generally represented as :
do {
//entry section
critical section
//exit section
remainder section
} while (TRUE);
The solution to the critical section problem must ensure the following three conditions:
- Mutual Exclusion
- Progress
- Bounded Waiting
One of the solutions for ensuring above all factors is Peterson’s solution.
Another one is Dekker’s Solution. Dekker’s algorithm was the first probably-correct solution to the critical section problem. It allows two threads to share a single-use resource without conflict, using only shared memory for communication. It avoids the strict alternation of a naïve turn-taking algorithm, and was one of the first mutual exclusion algorithms to be invented.
Although there are many versions of Dekker’s Solution, the final or 5th version is the one that satisfies all of the above conditions and is the most efficient of them all.
Note – Dekker’s Solution, mentioned here, ensures mutual exclusion between two processes only, it could be extended to more than two processes with the proper use of arrays and variables.
Algorithm – It requires both an array of Boolean values and an integer variable:
var flag: array [0..1] of boolean;
turn: 0..1;
repeat
flag[i] := true;
while flag[j] do
if turn = j then
begin
flag[i] := false;
while turn = j do no-op;
flag[i] := true;
end;
critical section
turn := j;
flag[i] := false;
remainder section
until false;
First Version of Dekker’s Solution – The idea is to use a common or shared thread number between processes and stop the other process from entering its critical section if the shared thread indicates the former one already running.
CPP
Main()
{
int thread_number = 1;
startThreads();
}
Thread1()
{
do {
while (threadnumber == 2)
;
threadnumber = 2;
} while (completed == false )
}
Thread2()
{
do {
while (threadnumber == 1)
;
threadnumber = 1;
} while (completed == false )
}
|
Java
import java.lang.Thread;
public class ThreadExample {
static boolean completed = false ;
static int threadNumber = 1 ;
static void Thread1()
{
boolean doWhile = false ;
while (!completed || !doWhile) {
doWhile = true ;
while (threadNumber == 2 ) {
Thread.yield();
}
threadNumber = 2 ;
}
}
static void Thread2()
{
boolean doWhile = false ;
while (!completed || !doWhile) {
doWhile = true ;
while (threadNumber == 1 ) {
Thread.yield();
}
threadNumber = 1 ;
}
}
static void StartThreads()
{
Thread t1 = new Thread(ThreadExample::Thread1);
Thread t2 = new Thread(ThreadExample::Thread2);
t1.start();
t2.start();
}
public static void main(String[] args)
{
threadNumber = 1 ;
StartThreads();
}
}
|
Python3
def Thread1():
doWhile = False
while not completed or not doWhile:
doWhile = True
while (threadnumber = = 2 ):
pass
threadnumber = 2
def Thread2():
doWhile = False
while not completed or not doWhile:
doWhile = True
while (threadnumber = = 1 ):
pass
threadnumber = 1
if __name__ = = '__main__' :
thread_number = 1
startThreads()
|
C#
using System.Threading;
class ThreadExample {
static bool completed = false ;
static int threadNumber = 1;
static void Thread1()
{
bool doWhile = false ;
while (!completed || !doWhile) {
doWhile = true ;
while (threadNumber == 2) {
Thread.Yield();
}
threadNumber = 2;
}
}
static void Thread2()
{
bool doWhile = false ;
while (!completed || !doWhile) {
doWhile = true ;
while (threadNumber == 1) {
Thread.Yield();
}
threadNumber = 1;
}
}
static void StartThreads()
{
Thread t1 = new Thread(Thread1);
Thread t2 = new Thread(Thread2);
t1.Start();
t2.Start();
}
static void Main( string [] args)
{
threadNumber = 1;
StartThreads();
}
}
|
Javascript
let thread_number = 1;
function Thread1() {
let doWhile = false ;
while (!completed || !doWhile) {
doWhile = true ;
while (thread_number === 2) {
}
thread_number = 2;
}
}
function Thread2() {
let doWhile = false ;
while (!completed || !doWhile) {
doWhile = true ;
while (thread_number === 1) {
}
thread_number = 1;
}
}
startThreads();
|
The problem arising in the above implementation is lockstep synchronization, i.e each thread depends on the other for its execution. If one of the processes completes, then the second process runs, gives access to the completed one, and waits for its turn, however, the former process is already completed and would never run to return the access back to the latter one. Hence, the second process waits infinitely then.
Second Version of Dekker’s Solution – To remove lockstep synchronization, it uses two flags to indicate its current status and updates them accordingly at the entry and exit section.
CPP
Main()
{
boolean thread1 = false ;
boolean thread2 = false ;
startThreads();
}
Thread1()
{
do {
while (thread2 == true )
;
thread1 = true ;
thread1 = false ;
} while (completed == false )
}
Thread2()
{
do {
while (thread1 == true )
;
thread2 = true ;
thread2 = false ;
} while (completed == false )
}
|
Java
public class MutualExclusion {
boolean thread1 = false ;
boolean thread2 = false ;
public void startThreads() {
new Thread( new Runnable() {
public void run() {
thread1();
}
}).start();
new Thread( new Runnable() {
public void run() {
thread2();
}
}).start();
}
public void thread1() {
do {
while (thread2 == true );
thread1 = true ;
thread1 = false ;
} while (completed == false );
}
public void thread2() {
do {
while (thread1 == true );
thread2 = true ;
thread2 = false ;
} while (completed == false );
}
public static void main(String[] args) {
MutualExclusion me = new MutualExclusion();
me.startThreads();
}
}
|
Python3
def Thread1():
doWhile = False
while not completed or not doWhile:
doWhile = True
while (thread2):
pass
thread1 = True
thread1 = False
def Thread2():
doWhile = False
while not completed or not doWhile:
doWhile = True
while (thread1):
pass
thread2 = True
thread2 = False
if __name__ = = '__main__' :
thread1 = False
thread2 = False
startThreads()
|
C#
using System;
using System.Threading;
class Program
{
static bool thread1 = false ;
static bool thread2 = false ;
static bool completed = false ;
static void Main( string [] args)
{
Thread threadOne = new Thread( new ThreadStart(Thread1));
Thread threadTwo = new Thread( new ThreadStart(Thread2));
threadOne.Start();
threadTwo.Start();
}
static void Thread1()
{
do
{
while (thread2)
{
}
thread1 = true ;
thread1 = false ;
}
while (!completed);
}
static void Thread2()
{
do
{
while (thread1)
{
}
thread2 = true ;
thread2 = false ;
}
while (!completed);
}
}
|
Javascript
let thread1InCriticalSection = false ;
let thread2InCriticalSection = false ;
function thread1() {
Promise.resolve().then(() => {
while (thread2InCriticalSection) {}
thread1InCriticalSection = true ;
thread1InCriticalSection = false ;
thread1();
});
}
function thread2() {
Promise.resolve().then(() => {
while (thread1InCriticalSection) {}
thread2InCriticalSection = true ;
thread2InCriticalSection = false ;
thread2();
});
}
thread1();
thread2();
|
The problem arising in the above version is mutual exclusion itself. If threads are preempted (stopped) during flag updation ( i.e during current_thread = true ) then, both the threads enter their critical section once the preempted thread is restarted, also the same can be observed at the start itself, when both the flags are false.
Third Version of Dekker’s Solution – To re-ensure mutual exclusion, it sets the flags before the entry section itself.
C++
Main()
{
boolean thread1wantstoenter = false ;
boolean thread2wantstoenter = false ;
startThreads();
}
Thread1()
{
do {
thread1wantstoenter = true ;
while (thread2wantstoenter == true )
;
thread1wantstoenter = false ;
} while (completed == false )
}
Thread2()
{
do {
thread2wantstoenter = true ;
while (thread1wantstoenter == true )
;
thread2wantstoenter = false ;
} while (completed == false )
}
|
Python3
if __name__ = = '__main__' :
thread1wantstoenter = False
thread2wantstoenter = False
startThreads()
def Thread1():
doWhile = False
while (completed = = False or not doWhile):
doWhile = True
thread1wantstoenter = True
while (thread2wantstoenter = = True ):
pass
thread1wantstoenter = False
def Thread2():
doWhile = False
while (completed = = False or not doWhile) :
doWhile = True
thread2wantstoenter = True
while (thread1wantstoenter = = True ):
pass
thread2wantstoenter = False
|
The problem with this version is a deadlock possibility. Both threads could set their flag as true simultaneously and both will wait infinitely later on.
Fourth Version of Dekker’s Solution – Uses small time interval to recheck the condition, eliminates deadlock, and ensures mutual exclusion as well.
CPP
Main()
{
boolean thread1wantstoenter = false ;
boolean thread2wantstoenter = false ;
startThreads();
}
Thread1()
{
do {
thread1wantstoenter = true ;
while (thread2wantstoenter == true ) {
thread1wantstoenter = false ;
thread1wantstoenter = true ;
}
thread1wantstoenter = false ;
} while (completed == false )
}
Thread2()
{
do {
thread2wantstoenter = true ;
while (thread1wantstoenter == true ) {
thread2wantstoenter = false ;
thread2wantstoenter = true ;
}
thread2wantstoenter = false ;
} while (completed == false )
}
|
Python3
if __name__ = = '__main__' :
thread1wantstoenter = False
thread2wantstoenter = False
startThreads()
def Thread1():
doWhile = False
while (completed = = False or not doWhile):
doWhile = True
thread1wantstoenter = True
while (thread2wantstoenter = = True ) :
thread1wantstoenter = False
thread1wantstoenter = True
thread1wantstoenter = False
def Thread2():
doWhile = False
while (completed = = False or not doWhile):
doWhile = True
thread2wantstoenter = True
while (thread1wantstoenter = = True ) :
thread2wantstoenter = False
thread2wantstoenter = True
thread2wantstoenter = False
|
Java
public class TwoThreadMutex {
private static boolean thread1wantstoenter = false ;
private static boolean thread2wantstoenter = false ;
public static void main(String[] args) {
startThreads();
}
private static void startThreads() {
Thread t1 = new Thread(TwoThreadMutex::Thread1);
Thread t2 = new Thread(TwoThreadMutex::Thread2);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void Thread1() {
do {
thread1wantstoenter = true ;
while (thread2wantstoenter == true ) {
thread1wantstoenter = false ;
thread1wantstoenter = true ;
}
thread1wantstoenter = false ;
} while (completed == false );
}
private static void Thread2() {
do {
thread2wantstoenter = true ;
while (thread1wantstoenter == true ) {
thread2wantstoenter = false ;
thread2wantstoenter = true ;
}
thread2wantstoenter = false ;
} while (completed == false );
}
}
|
The problem with this version is the indefinite postponement. Also, a random amount of time is erratic depending upon the situation in which the algorithm is being implemented, hence not an acceptable solution in business critical systems.
Dekker’s Algorithm: 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.
CPP
Main()
{
int favouredthread = 1;
boolean thread1wantstoenter = false ;
boolean thread2wantstoenter = false ;
startThreads();
}
Thread1()
{
do {
thread1wantstoenter = true ;
while (thread2wantstoenter == true ) {
if (favaouredthread == 2) {
thread1wantstoenter = false ;
while (favouredthread == 2)
;
thread1wantstoenter = true ;
}
}
favouredthread = 2;
thread1wantstoenter = false ;
} while (completed == false )
}
Thread2()
{
do {
thread2wantstoenter = true ;
while (thread1wantstoenter == true ) {
if (favaouredthread == 1) {
thread2wantstoenter = false ;
while (favouredthread == 1)
;
thread2wantstoenter = true ;
}
}
favouredthread = 1;
thread2wantstoenter = false ;
} while (completed == false )
}
|
Python3
if __name__ = = '__main__' :
favouredthread = 1
thread1wantstoenter = False
thread2wantstoenter = False
startThreads()
def Thread1():
doWhile = False
while (completed = = False or not doWhile) :
doWhile = True
thread1wantstoenter = True
while (thread2wantstoenter = = True ) :
if (favaouredthread = = 2 ) :
thread1wantstoenter = False
while (favouredthread = = 2 ):
pass
thread1wantstoenter = True
favouredthread = 2
thread1wantstoenter = False
def Thread2():
doWhile = False
while (completed = = False or not doWhile) :
doWhile = True
thread2wantstoenter = True
while (thread1wantstoenter = = True ) :
if (favaouredthread = = 1 ) :
thread2wantstoenter = False
while (favouredthread = = 1 ):
pass
thread2wantstoenter = True
favouredthread = 1
thread2wantstoenter = False
|
This version guarantees a complete solution to the critical solution problem.
References –
Dekker’s Algorithm -csisdmz.ul.ie
Dekker’s algorithm – Wikipedia