flag[i] = 1;
while (flag[j])
{
if (turn == j)
{
flag[i] = 0;
while(turn == j)
;
flag[i] = 1;
}
}
/* enter C.S. */
/* exit C.S. */
turn = j;
flag[i] = 0;
This algorithm is called Dekker's solution.
Solution
1. Mutual exclusion:
There are two cases to consider:
a. A process is inside the C.S.: Without loss
of generality, assume process j is inside the C.S. Before entering
the C.S. the process sets its own flag to 1.
If process i tries to enter the C.S. it will see that flag[j] is
up and gets caught up in the while loop. It will continue in the while
loop until the other process sets its own flag to 0, which happens only
at the end of the C.S.
b. Two processes are trying to enter simultaneously:
In this situation, if both processes reach their respective while loop
at the top, then the variable turn will ensure that only one of them passes
through. The variable turn is alternating between the allowing either process,
and is only modified at the exit of a C.S.
2. Progress:
There are two cases to consider:
a. One process is trying to enter with no competition:
In such a case, the flag of the other process is down, and the process
goes past the while look into the critical section directly.
b. Two processes are trying to enter simultaneously.
In this case if the first process is trapped into the while loop, then
the variable turn will make one of the two variables lower its flag and
goes into a loop waiting for the variable turn to change (the inner while
loop). The other process whose turn is set by the variable turn will be
able to get through.
3. Bounded Waiting:
Assume there is a process blocked inside the
inner while loop, while another process is in C.S. In such a case, if the
process inside the critical section tries to re-enter, it will be blocked
because on exit of the C.S. it has already set the variable turn to point
to the other process. Therefore, the process that just got out of the C.S.
will be forced to wait for its own turn. So, bounded waiting is taken care
of.
j = a++; // execute in
one atomic operation
a = 0;
Either come up with a solution to the C.S. problem using this facility or show that it cannot help.
Solution:
This hardware solution can help implement a solution to the C.S. as follows:
atomic inside = 0;
// inside is an atomic counter
int a;
retry:
a = inside++;
if(a != 1)
goto retry;
/* enter C.S. */
...
/* exit C.S. */
inside = 0;
A uniprocessor kernel can be entered simultaneously through a trap, and through interrupts. When a thread is running inside the kernel, it could get pre-empted if the machine receives an interrupt. Thus, the interrupt handlers may call functions that access and modify global data structures. There may be a situation where the thread that was running inside the kernel when the interrupt occurred may be modifying the same data structures. This creates a nasty form of concurrency. A standard solution thus is to cut the reason for this concurrency at the source, i.e. disable interrupts which are the cause for pre-empting a thread when it is running inside the kernel. Needless to say, this form of concurrency control is rather crude, and it cannot scale to multiprocessors.
#include "Exception.h"
#include "Semaphore.h"
#include <iostream.h>
const MaxStackSize = 100;
class Stack
// throws an exception object when popping an empty stack, and when pushing
into a full stack
{
private:
int s[MaxStackSize];
int stackp;
// stack pointer
Exception * e;
// For error handling
Semaphore * sem;
// For mutual exclusion
public:
Stack();
~Stack()
{};
int Pop(void);
void Push(int item);
};
Stack::Stack()
{
stackp = MaxStackSize;
e = new Exception();
sem = new Semaphore(1);
}
int Stack::Pop(void)
{
P(sem)
if(stackp == MaxStackSize)
{
e->SetErrorMsg("Popping empty stack");
e->SetErrorLocation("Stack::Pop()");
throw(e);
Error: Before throwing the exception, we must
release the lock (i.e. V(sem)), or
the stack object will not be accessible to any
process any time in the future.
}
V(sem);
return s[stackp++];
Error: We are incrementing stackp after releasing
the lock!!
}
void Stack::Push(int item)
{
P(sem)
if(stackp == 0)
{
e->SetErrorMsg("Pushing to a full stack");
e->SetErrorLocation("Stack::Push()");
throw(e);
Error: Before throwing the exception, we must
release the lock (i.e. V(sem)), or
the stack object will not be accessible to any
process any time in the future.
}
s[--stackp] = item;
V(sem);
}