next up previous contents
Next: Monitors Up: Concurrent Programming Previous: Protocols based on shared   Contents

Programming primitives for mutual exclusion

Peterson's algorithm does not generalize easily to a situation where there are more than two concurrent processes. For $n$ processes, there are other solutions, such as Lamport's Bakery algorithm. However, the problem with these protocols is that, in general, they have to be designed anew for different situations and checked for correctness.

A better solution is to include support in the programming language for mutual exclusion. The first person to propose such primitive was Edsger Dijkstra, who suggested a new datatype called a semaphore.

A semaphore is an integer variable that supports an atomic test-and-set operation. More specifically, if a S is a variable of type semaphore, then two atomic operations are supported on S: P(S) and V(S). (The letters P and V come from the Dutch words passeren, to pass, and vrygeven, to release.)

The operation P(S) achieves the following in an atomic manner:

    if (S > 0)
      decrement S;
    else
      wait for S to become positive;

The operation V(S) is defined as follows:

    if (there are threads waiting for S to become positive)
      wake one of them up; //choice is nondeterministic
    else
      increment S;

Using semaphores, we can now easily program mutual exclusion to critical sections, as follows:

    Thread 1                          Thread 2

    ...                               ...
    P(S);                             P(S);
    // Enter critical section         // Enter critical section
       ...                               ...
    // Leave critical section         // Leave critical section
    V(S);                             V(S);
    ...                               ...

Mutual exclusion, starvation freedom and deadlock freedom are all guaranteed by the definition of a semaphore.

One problem with semaphores are that they are rather ``low-level''. The definition of a semaphore is orthogonal to the identification of the critical region. Thus, we connect critical regions across threads in a somewhat arbitrary fashion by using a shared semaphore. Correctness requires cooperation from all participating threads in resetting the semaphore. For instance, if any one thread forgets to execute V(S), all other threads get blocked. Further, it is not required that each V(S) match a preceding P(S). This could lead to a semaphore being ``preloaded'' to an unsafe value.


next up previous contents
Next: Monitors Up: Concurrent Programming Previous: Protocols based on shared   Contents
Madhavan Mukund 2004-04-29