Semaphores (semLib.h)


Introduction

This article provides information on how to create, take, give and delete semaphores of three different types of semaphores: counting semaphores, binary semaphores, and mutexes. It is assumed that you are already familiar with the general operating principles of semaphores.

Semaphore creation

The only point at which the use of the three different kinds of semaphores differ is in their creation. Examples of the creation of each kind of semaphore is given below:

Counting semaphore creation

Counting semaphores are created using semCCreate(), as illustrated in the below example:

void myMain()
{
 SEM_ID cid; // Holds the semaphore's id (SEM_ID is defined in semLib)
 
 // Create a semaphore that
 // - has prioritized access for queued threads (SEM_Q_PRIORITY)
 // - has an initial count of 2
 cid = semCCreate(SEM_Q_PRIORITY, 2);
 ...
 }

Prioritized access (SEM_Q_PRIORITY) means that the highest-priority waiting thread will be allowed to run first when the semaphore is given. The alternative is SEM_Q_FIFO. The initial count of the semaphore is basically how many times semTake() (see below) can be called before the caller is blocked.

Binary semaphore creation

Binary semaphores are created using semBCreate(), as illustrated in the below example:
 void mýMain()
 {
 SEM_ID bid;
 
 // Create a binary semaphore that
 // - has FIFO access
 // - is initially empty
 bid = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
 ...
 }
SEM_EMPTY means that the semaphore is empty on creation, i.e. that a call to semTake() will block the caller. If SEM_FULL was specified instead, semTake() could be called once before a caller is blocked.
Binary semaphores are generally used for iter-task communication and synchronization purposes.

Mutex creation

Mutexes are designed for mutual exclusion (hence the name), i.e., to enforce atomic access to a ressource. Therefore, mutexes are usually created with a set of properties as shown below (note that mutexes are initially full by default):
 void myMain()
 {
 SEM_ID mid;
 
 // Create a binary semaphore that
 // - has prioritized access (SEM_Q_PRIORITY),
 // - ensures that the holding thread cannot be deleted while holding
 //   the mutex (SEM_DELETE_SAFE),
 // - protects against priority inversion (SEM_INVERSION_SAFE)
 mid = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
 ...
 }
Note that while binary and counting semaphores are created with two arguments, access rights and options, mutexes are created with only one argument, options, in which access rights are specified. The reason for this is unknown.

To take and give a semaphore

Semaphores, regardless of type, are taken/waited with a call to semTake() and given/signalled with a call to semGive() as shown below:
 void myMain()
 {
 SEM_ID mid = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
 ...
//Attempt to take the semaphore - wait forever for success
 semTake(mid, WAIT_FOREVER);
 ...
 
// Give the semaphore
 semGive(mid);
 }