B424 Parallel and Distributed Programming

Semaphores

Semaphore: a synchronization tool based on the value of a variable, Dijkstra (1968).

In theory there must be two atomic operations on a semaphore:

In practice a semaphore has 2 or more states.

The functions are usually implemented as Test_and_set... or Set... .

Thread() {
  Noncritical section
  ...
  P(s);
  Critical section
  V(s);
  ...
  Noncritical section
}

Categories of Semaphores

Binary versus general. A binary semaphore can take only 2 values: 0 or 1 (also called red and green). A general semaphore can take any kind of value > 0.

Binary semaphore: asymmetric versus symmetric.

For both types, the value can be decreased only if it is >0 and it is done with a Test_and_set... function.

For an asymmetric semaphore, increasing the value is non-blocking and done with a function Set... .

For a symmetric semaphore, the value can be increased only if it is 0 and it's also done with a Test_and_set... .

Asymmetrical semaphores are equivalent to mutexes.

Symmetric-Asymmetric Semaphores

Asymetric semaphore: red = 0, green = 1.

    
Test_and_set_red(&sem);  // while (sem == 0 ); sem = 0;
Set_green(&sem);         // sem = 1;

Symmetric semaphore:

Test_and_set_red(&sem);   // while (sem == 0 ); sem = 0;
Test_and_set_green(&sem); // while (sem == 1 ); sem = 1;

The type of entry protocol for the critical section with a loop is called a spin lock.

Mutex Based Semaphores

const int red=0, green = 1;
struct semaphore
{
  int light;
  pthread_mutex_t mutex;
};

void Semaphore_init(semaphore &sem, int value)
{
  light = value;
  pthread_mutex_init(&sem.mutex, NULL);
}

Asymmetric Semaphore

void Test_and_set_red(semaphore &sem) 
{
  pthread_mutex_lock(&sem.mutex);
  while (sem.light == red) {
  pthread_mutex_unlock(&sem.mutex);
  pthread_mutex_lock(&sem.mutex);
}

void Set_green(semaphore &sem) 
{
  pthread_mutex_lock(&sem.mutex);
  sem.light = green;
  pthread_mutex_unlock(&sem.mutex);
}

Symmetric Semaphore

void Test_and_set_green(semaphore &sem) 
{
  pthread_mutex_lock(&sem.mutex);
  while (sem.light == green) {
    pthread_mutex_unlock(&sem.mutex);
    pthread_mutex_lock(&sem.mutex);
  }
  sem.light = green;
  pthread_mutex_unlock(&sem.mutex);
}

POSIX Semaphores

#include 

// If pshared is 0, the semaphore is local to the current process.
// The semaphore is then shared only by the threads created
// by that process.
int sem_init(sem_t *sem, int pshared, unsigned int value);

int sem_wait(sem_t * sem);    // P(sem);
int sem_trywait(sem_t * sem); // V(sem);

int sem_post(sem_t * sem);
int sem_getvalue(sem_t * sem, int * sval);
int sem_destroy(sem_t * sem);

Dependency Graphs

Dependency graph: given a set of processes, each process must wait for a number of other processes to finish their task before it can start its own task.

Solution with General Semaphores

Introduce a semaphore sj corresponding to each non-initial process Pj.

Let the initial value of sj be defined by dj, the number of immediate predecessors of Pj.

Each process must wait to begin their task on

Test_and_inc(sj);

After performing their task, each process must decrease the semaphores of all the successors.

Example

Init(s1, 0); Init(s2, 1); Init(s3, 2),
Init(s4, 1), Init(s5, 2);
P1' : T&Inc(s1); P1, Dec(s2); Dec(s3); Dec(s4);
P2' : T&Inc(s2); P2, Dec(s3);
P3' : T&Inc(s3); P3, Dec(s5);
P4' : T&Inc(s4); P4; Dec(s5);
P5' : T&Inc(s1); P5;