Dekker’s Algorithm - Critical Section

The development of code that eventually leads to Dekker's Algorithm is used to illustrate M. Ben-Ari's methods of verifying the correctness of concurrent algorithms.

One thread will be executing the ThreadZero() function, and the other will execute the ThreadOne() function. Critical regions will be represented as calls to the functions CriticalRegionZero and CriticalRegionOne. When a thread is not within the critical region, there are other computations to be done (OtherStuffZero, OtherStuffOne).

First Attempt

int threadNumber = 0;
void ThreadZero()
{
while (TRUE) do {
while (threadNumber == 1) do {} //spin-wait
CriticalRegionZero;
threadNumber = 1;
OtherStuffZero;
}
}
void ThreadOne()
{
while (TRUE) do {
while (threadNumber == 0) do {} //spin-wait
CriticalRegionOne;
threadNumber = 0;
OtherStuffOne;
}
}

Compounding this problem is the possibility of one thread terminating (whether by design, by accident, or by foul play) before the other. If the surviving thread attempts to enter the critical region without the dead thread having a chance to change the value of the threadNumber flag, this will create a deadlock on the surviving thread.

Second Attempt

int Thread0inside = 0;
int Thread1inside = 0;

void ThreadZero()
{
while (TRUE) do {
while (Thread1inside) do {} // spin-wait
Thread0inside = 1;
CriticalRegionZero;
Thread0inside = 0;
OtherStuffZero;
}
}
void ThreadOne()
{
while (TRUE) do {
while (Thread0inside) do {}
Thread1inside = 1;
CriticalRegionOne;
Thread1inside = 0;
OtherStuffOne;
}
}

This interleaving forces us to conclude that the second proposed solution will not keep two threads out of the critical region. One obvious fix is to disallow a thread from accessing the while loop conditional evaluation whenever the other thread is running the while loop test within its own function.

Third Attempt

int Thread0WantsToEnter = 0;
int Thread1WantsToEnter = 0;
void ThreadZero()
{
while (TRUE) do {
Thread0WantsToEnter = 1;
while (Thread1WantsToEnter) do {} // spin-wait
CriticalRegionZero;
Thread0WantsToEnter = 0;
OtherStuffZero;
}
}
void ThreadOne()
{
while (TRUE) do {
Thread1WantsToEnter = 1;
while (Thread0WantsToEnter) do {}
CriticalRegionOne;
Thread1WantsToEnter = 0;
OtherStuffOne;
}
}

deadlock!!!

The threads T0 and T1 can both state the desire to enter the critical region before testing the intentions of the other thread. In that case, both threads will go into their spin-wait loops and never have the chance to reset the desire flag that will release the other thread. This is a classic deadlock situation. Each thread is waiting for an event (the reset of the desire flag of the other thread) that will never occur.

Fourth Attempt

int Thread0WantsToEnter = 0;
int Thread1WantsToEnter = 0;
void ThreadZero()
{
while (TRUE) do {
Thread0WantsToEnter = 1;
while (Thread1WantsToEnter) do { // not quite a spin-wait
Thread0WantsToEnter = 0;
delay(someRandomCycles);
Thread0WantsToEnter = 1;
}
CriticalRegionZero;
Thread0WantsToEnter = 0;
OtherStuffZero;
}
}
void ThreadOne()
{
while (TRUE) do {
Thread1WantsToEnter = 1;
while (Thread0WantsToEnter) do {
Thread1WantsToEnter = 0;
delay(someRandomCycles);
Thread1WantsToEnter = 1;
}
CriticalRegionOne;
Thread1WantsToEnter = 0;
OtherStuffOne; }
}

starvation!!!!

This interleaving can go on indefinitely, stranding the unlucky T1 in a perpetual state of starvation.

Dekker's Algorithm

int favored;
int Thread0WantsToEnter, Thread1WantsToEnter;
void ThreadZero()
{
while (TRUE) do {
Thread0WantsToEnter = 1;
while (Thread1WantsToEnter) do {
if (favored == 1) {
Thread0WantsToEnter = 0;
while (favored == 1) do {} // spin-wait
Thread0WantsToEnter = 1;
}
}
CriticalRegionZero;
favored = 1;
Thread0WantsToEnter = 0;
OtherStuffZero;
}
}
void ThreadOne()
{
while (TRUE) do {
Thread1WantsToEnter = 1;
while (Thread0WantsToEnter) do {
if (favored == 0) {
Thread1WantsToEnter = 0;
while (favored == 0) do {}
Thread1WantsToEnter = 1;
}
}
CriticalRegionOne;
favored = 0;
Thread1WantsToEnter = 0;
OtherStuffOne;
}
}


What Did You Learn?

The point of all of this was to show you how to identify concurrency errors or demonstrate correctness using the concurrency abstraction of Ben-Ari and the interleaving of atomic statement executions between the two threads. I hope that you tried the interleaving analysis on your own for each solution attempt before reading about the faults that each attempt contained. Some of these were subtle, particularly in the starvation case. This is not something that a software tool would be able to identify, and this is why the interleaving analysis is important and can assist you in designing correct concurrent solutions before we ever attempt to alter a line of code.

I'll be using the interleaving analysis during the discussions and implementation of several algorithms in the later chapters of this book. You might want to practice your analysis skills as you cover a new algorithm, especially those codes for which I don't have analysis already.

posted on 2010-09-12 10:19  胡是  阅读(491)  评论(0编辑  收藏  举报

导航