Skip to main content
Engineering LibreTexts

11.3: Make your own semaphores

  • Page ID
    40643
  • Any problem that can be solved with semaphores can also be solved with condition variables and mutexes. We can prove that’s true by using condition variables and mutexes to implement a semaphore.

    Before you go on, you might want to try this as an exercise: write functions that implement the semaphore API in sem.h using using condition variables and mutexes. In the repository for this book, you’ll find my solution in mysem_soln.c and mysem_soln.h.

    If you have trouble getting started, you can use the following structure definition, from my solution, as a hint:

    typedef struct {
      int value, wakeups;
      Mutex *mutex;
      Cond *cond;
    } Semaphore;
    

    value is the value of the semaphore. wakeups counts the number of pending signals; that is, the number of threads that have been woken but have not yet resumed execution. The reason for wakeups is to make sure that our semaphores have Property 3, described in The Little Book of Semaphores.

    mutex provides exclusive access to value and wakeups; cond is the condition variable threads wait on if they wait on the semaphore.

    Here is the initialization code for this structure:

    Semaphore *make_semaphore(int value)
    {
      Semaphore *semaphore = check_malloc(sizeof(Semaphore));
      semaphore->value = value;
      semaphore->wakeups = 0;
      semaphore->mutex = make_mutex();
      semaphore->cond = make_cond();
      return semaphore;
    }

    Semaphore implementation

    Here is my implementation of semaphores using POSIX mutexes and condition variables:

    void semaphore_wait(Semaphore *semaphore)
    {
      mutex_lock(semaphore->mutex);
      semaphore->value--;
    
      if (semaphore->value < 0) {
        do {
          cond_wait(semaphore->cond, semaphore->mutex);
        } while (semaphore->wakeups < 1);
        semaphore->wakeups--;
      }
      mutex_unlock(semaphore->mutex);
    }
    

    When a thread waits on the semaphore, it has to lock the mutex before it decrements value. If the value of the semaphore becomes negative, the thread blocks until a “wakeup” is available. While it is blocked, the mutex is unlocked, so another thread can signal.

    Here is the code for semaphore_signal:

    void semaphore_signal(Semaphore *semaphore)
    {
      mutex_lock(semaphore->mutex);
      semaphore->value++;
    
      if (semaphore->value <= 0) {
        semaphore->wakeups++;
        cond_signal(semaphore->cond);
      }
      mutex_unlock(semaphore->mutex);
    }
    

    Again, a thread has to lock the mutex before it increments value. If the semaphore was negative, that means threads are waiting, so the signalling thread increments wakeups and signals the condition variable.

    At this point one of the waiting threads might wake up, but the mutex is still locked until the signalling thread unlocks it.

    At that point, one of the waiting threads returns from cond_wait and checks whether a wakeup is still available. If not, it loops and waits on the condition variable again. If so, it decrements wakeups, unlocks the mutex, and exits.

    One thing about this solution that might not be obvious is the use of a do...while loop. Can you figure out why it is not a more conventional while loop? What would go wrong?

    The problem is that with a while loop this implementation would not have Property 3. It would be possible for a thread to signal and then run around and catch its own signal.

    With the do...while loop, it is guaranteed1 that when a thread signals, one of the waiting threads will get the signal, even if the signaling thread runs around and gets the mutex before one of the waiting threads resumes.


    1. Well, almost. It turns out that a well-timed spurious wakeup (see http://en.Wikipedia.org/wiki/Spurious_wakeup) can violate this guarantee.
    • Was this article helpful?