Semaphores

Updated: April 19, 2023

Semaphores are a common form of synchronization that allows threads to “post” and “wait” on a semaphore to control when threads wake or sleep.

Semaphores differ from other synchronization primitives in that they are “async safe” and can be manipulated by signal handlers. If the desired effect is to have a signal handler wake a thread, semaphores are the right choice.

The QNX Neutrino implementation of semaphores supports both named and unnamed semaphores (see Named and unnamed semaphores below).

Semaphore or mutex?

A key advantage of semaphores over mutexes is that semaphores are defined to operate between processes.

Although our mutexes work between processes, the POSIX thread standard considers this an optional capability. Thus, if your code will rely on mutexes working between processes, to ensure portability you should probably use semaphores rather than mutexes.

However, depending on your needs you may wish to use mutexes rather than semaphores after considering the following:

Using semaphores

The post (sem_post()) operation increments the semaphore; the wait (sem_wait()) operation decrements it.

If you wait on a semaphore that is positive, you will not block. Waiting on a nonpositive semaphore will block until some other thread executes a post. It is valid to post one or more times before a wait. This use allows one or more threads to execute the wait without blocking.

Since semaphores, like condition variables, can legally return a nonzero value because of a false wake-up, correct usage requires a loop:

while (sem_wait(&s) && (errno == EINTR)) { do_nothing(); }
do_critical_region();   /* Semaphore was decremented */

For a complete list of functions you can use with semaphores, see the sem_*() functions in the C Library Reference.

Named and unnamed semaphores

Unnamed semaphores are created with sem_init(), and destroyed with sem_destroy(). They can be used by multiple threads within the same process. However, to use an unnamed semaphore between processes requires a shared memory object, which implies a large memory overhead.

Named semaphores are created with sem_open(), and closed with sem_close(). Like unnamed semaphores, they can be be used by multiple threads within the same process. Unlike unnamed semaphores, however, named semaphores can be easily shared between threads in different processes.

With the exception of anonymous named semaphores, named semaphores have a path and a name, so they can be found between processes without the use of shared memory objects and the overhead they imply. Named semaphores are therefore more useful for inter-process communication than are unnamed semaphores.

Additionally, named semaphores (including anonymous named semaphores) can be posted by a sigevent (e.g., returned from an ISR or delivered by a server), and can be shared between a parent and a child process across a fork() without requiring a shared mapping.

Anonymous named semaphores don't have a path and name like other named semaphores, but they share the other advantages noted above. For more information about anonymous named semaphores, see sem_open() in the C Library Reference.