|
|
+++
|
|
|
title = "Semaphore"
|
|
|
+++
|
|
|
|
|
|
# Semaphore
|
|
|
|
|
|
A semaphore is another synchronization primitive. It is initialized to some value. Threads can either `sem_wait` or `sem_post` which lowers or increases the value. If the value reaches zero and a wait is called, the thread will be blocked until a post is called.
|
|
|
|
|
|
Using a semaphore is as easy as using a mutex. First, decide if on the initial value, for example the number of remaining spaces in an array. Unlike pthread mutex there are no shortcuts to creating a semaphore - use `sem_init`.
|
|
|
|
|
|
```c
|
|
|
#include <semaphore.h>
|
|
|
|
|
|
sem_t s;
|
|
|
int main() {
|
|
|
sem_init(&s, 0, 10); // returns -1 (=FAILED) on OS X
|
|
|
sem_wait(&s); // Could do this 10 times without blocking
|
|
|
sem_post(&s); // Announce that we've finished (and one more resource item is available; increment count)
|
|
|
sem_destroy(&s); // release resources of the semaphore
|
|
|
}
|
|
|
```
|
|
|
|
|
|
When using a semaphore, wait and post can be called from different threads! Unlike a mutex, the increment and decrement can be from different threads.
|
|
|
|
|
|
This becomes especially useful if you want to use a semaphore to implement a mutex. A mutex is a semaphore that always waits before it posts. Some textbooks will refer to a mutex as a binary semaphore. You do have to be careful to never add more than one to a semaphore or otherwise your mutex abstraction breaks. That is usually why a mutex is used to implement a semaphore and vice versa.
|
|
|
|
|
|
- Initialize the semaphore with a count of one.
|
|
|
|
|
|
- Replace `pthread_mutex_lock` with `sem_wait`
|
|
|
|
|
|
- Replace `pthread_mutex_unlock` with `sem_post`
|
|
|
|
|
|
```c
|
|
|
sem_t s;
|
|
|
sem_init(&s, 0, 1);
|
|
|
|
|
|
sem_wait(&s);
|
|
|
// Critical Section
|
|
|
sem_post(&s);
|
|
|
```
|
|
|
|
|
|
But be warned, it isn’t the same! A mutex can handle what we call lock inversion well. Meaning the following code breaks with a traditional mutex, but produces a race condition with threads.
|
|
|
|
|
|
```c
|
|
|
// Thread 1
|
|
|
sem_wait(&s);
|
|
|
// Critical Section
|
|
|
sem_post(&s);
|
|
|
|
|
|
// Thread 2
|
|
|
// Some threads want to see the world burn
|
|
|
sem_post(&s);
|
|
|
|
|
|
// Thread 3
|
|
|
sem_wait(&s);
|
|
|
// Not thread-safe!
|
|
|
sem_post(&s);
|
|
|
```
|
|
|
|
|
|
If we replace it with mutex lock, it won’t work now.
|
|
|
|
|
|
```c
|
|
|
// Thread 1
|
|
|
mutex_lock(&s);
|
|
|
// Critical Section
|
|
|
mutex_unlock(&s);
|
|
|
|
|
|
// Thread 2
|
|
|
// Foiled!
|
|
|
mutex_unlock(&s);
|
|
|
|
|
|
// Thread 3
|
|
|
mutex_lock(&s);
|
|
|
// Now it's thread-safe
|
|
|
mutex_unlock(&s);
|
|
|
```
|
|
|
|
|
|
Also, binary semaphores are different than mutexes because one thread can unlock a mutex from a different thread.
|
|
|
|
|
|
### Signal Safety
|
|
|
|
|
|
Also, `sem_post` is one of a handful of functions that can be correctly used inside a signal handler `pthread_mutex_unlock` is not. We can release a waiting thread that can now make all of the calls that we disallowed to call inside the signal handler itself e.g. `printf`. Here is some code that utilizes this;
|
|
|
|
|
|
```c
|
|
|
#include <stdio.h>
|
|
|
#include <pthread.h>
|
|
|
#include <signal.h>
|
|
|
#include <semaphore.h>
|
|
|
#include <unistd.h>
|
|
|
|
|
|
sem_t s;
|
|
|
|
|
|
void handler(int signal) {
|
|
|
sem_post(&s); /* Release the Kraken! */
|
|
|
}
|
|
|
|
|
|
void *singsong(void *param) {
|
|
|
sem_wait(&s);
|
|
|
printf("Waiting until a signal releases...\n");
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
int ok = sem_init(&s, 0, 0 /* Initial value of zero*/);
|
|
|
if (ok == -1) {
|
|
|
perror("Could not create unnamed semaphore");
|
|
|
return 1;
|
|
|
}
|
|
|
signal(SIGINT, handler); // Too simple! See Signals chapter
|
|
|
|
|
|
pthread_t tid;
|
|
|
pthread_create(&tid, NULL, singsong, NULL);
|
|
|
pthread_exit(NULL); /* Process will exit when there are no more threads */
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Other uses for semaphores are keeping track of empty spaces in arrays. We will discuss these in the thread-safe data structures section.
|