Table of Contents
- Syscalls, memory, and your first therad
- The pointer to self and thread-local storage
- Futexes, mutexes, and memory sychronization
- Joining threads and dynamic initialization
- Cancellation
- Scheduling and task priority
- RW Locks
- Condition variables
- Final thoughts
Condition Variables
Condition variables are a mechanism used for signaling that a certain predicate
has become true. The POSIX mechanism for handling them boils down to three
functions: cond_wait
, cond_signal
and cond_broadcast
. The first one causes
the thread that calls it to wait. The second wakes a thread up so that it can
verify whether the condition is true. The third wakes all the waiters up.
The waiter
1tb_futex_lock(&cond->lock);
2...
3++cond->waiters;
4int bseq = cond->broadcast_seq;
5int futex = cond->futex;
6tb_futex_unlock(&cond->lock);
7
8while(1) {
9 st = SYSCALL3(__NR_futex, &cond->futex, FUTEX_WAIT, futex);
10 if(st == -EINTR)
11 continue;
12
13 tb_futex_lock(&cond->lock);
14 if(cond->signal_num) {
15 --cond->signal_num;
16 goto exit;
17 }
18
19 if(bseq != cond->broadcast_seq)
20 goto exit;
21 tb_futex_unlock(&cond->lock);
22}
The algorithm is as follows:
- We lock the internal lock.
- We remember the value of the broadcast sequence and the futex.
- In a loop, we wait for the futex.
- We go back to sleeping if the
FUTEX_WAIT
syscall was interrupted by a signal. - We consume a signal, if we can, and exit.
- If there was a broadcast, we exit too.
- Otherwise, we go back to sleep.
Signal
We wake one of the threads up. We bump the value of the futex to prevent a deadlock. Then we bump the number of signals and wake the futex.
1int tbthread_cond_signal(tbthread_cond_t *cond)
2{
3 tb_futex_lock(&cond->lock);
4 if(cond->waiters == cond->signal_num)
5 goto exit;
6 ++cond->futex;
7 ++cond->signal_num;
8 SYSCALL3(__NR_futex, &cond->futex, FUTEX_WAKE, 1);
9exit:
10 tb_futex_unlock(&cond->lock);
11 return 0;
12}
Broadcast
We wake all the waiters. The algorithm is essentially the same as for signal, except that, we bump the broadcast sequence number and wake all the threads instead of just one.
1int tbthread_cond_broadcast(tbthread_cond_t *cond)
2{
3 tb_futex_lock(&cond->lock);
4 if(!cond->waiters)
5 goto exit;
6 ++cond->futex;
7 ++cond->broadcast_seq;
8 SYSCALL3(__NR_futex, &cond->futex, FUTEX_WAKE, INT_MAX);
9exit:
10 tb_futex_unlock(&cond->lock);
11 return 0;
12}
See the full patch at GitHub.
If you like this kind of content, you can subscribe to my newsletter, follow me on Twitter, or subscribe to my RSS channel.