| Conditional Variable pseudocode. |
| ================================ |
| |
| int pthread_cond_timedwait (pthread_cond_t *cv, pthread_mutex_t *mutex); |
| int pthread_cond_signal (pthread_cond_t *cv); |
| int pthread_cond_broadcast (pthread_cond_t *cv); |
| |
| struct pthread_cond_t { |
| |
| unsigned int cond_lock; |
| |
| internal mutex |
| |
| uint64_t total_seq; |
| |
| Total number of threads using the conditional variable. |
| |
| uint64_t wakeup_seq; |
| |
| sequence number for next wakeup. |
| |
| uint64_t woken_seq; |
| |
| sequence number of last woken thread. |
| |
| uint32_t broadcast_seq; |
| |
| } |
| |
| |
| struct cv_data { |
| |
| pthread_cond_t *cv; |
| |
| uint32_t bc_seq |
| |
| } |
| |
| |
| |
| cleanup_handler(cv_data) |
| { |
| cv = cv_data->cv; |
| lll_lock(cv->lock); |
| |
| if (cv_data->bc_seq == cv->broadcast_seq) { |
| ++cv->wakeup_seq; |
| ++cv->woken_seq; |
| } |
| |
| /* make sure no signal gets lost. */ |
| FUTEX_WAKE(cv->wakeup_seq, ALL); |
| |
| lll_unlock(cv->lock); |
| } |
| |
| |
| cond_timedwait(cv, mutex, timeout): |
| { |
| lll_lock(cv->lock); |
| mutex_unlock(mutex); |
| |
| cleanup_push |
| |
| ++cv->total_seq; |
| val = seq = cv->wakeup_seq; |
| cv_data.bc = cv->broadcast_seq; |
| cv_data.cv = cv; |
| |
| while (1) { |
| |
| lll_unlock(cv->lock); |
| |
| enable_async(&cv_data); |
| |
| ret = FUTEX_WAIT(cv->wakeup_seq, val, timeout); |
| |
| restore_async |
| |
| lll_lock(cv->lock); |
| |
| if (bc != cv->broadcast_seq) |
| goto bc_out; |
| |
| val = cv->wakeup_seq; |
| |
| if (val != seq && cv->woken_seq != val) { |
| ret = 0; |
| break; |
| } |
| |
| if (ret == TIMEDOUT) { |
| ++cv->wakeup_seq; |
| break; |
| } |
| } |
| |
| ++cv->woken_seq; |
| |
| bc_out: |
| lll_unlock(cv->lock); |
| |
| cleanup_pop |
| |
| mutex_lock(mutex); |
| |
| return ret; |
| } |
| |
| cond_signal(cv) |
| { |
| lll_lock(cv->lock); |
| |
| if (cv->total_seq > cv->wakeup_seq) { |
| ++cv->wakeup_seq; |
| FUTEX_WAKE(cv->wakeup_seq, 1); |
| } |
| |
| lll_unlock(cv->lock); |
| } |
| |
| cond_broadcast(cv) |
| { |
| lll_lock(cv->lock); |
| |
| if (cv->total_seq > cv->wakeup_seq) { |
| cv->wakeup_seq = cv->total_seq; |
| cv->woken_seq = cv->total_seq; |
| ++cv->broadcast_seq; |
| FUTEX_WAKE(cv->wakeup_seq, ALL); |
| } |
| |
| lll_unlock(cv->lock); |
| } |