lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 1 | Conditional Variable pseudocode. |
| 2 | ================================ |
| 3 | |
| 4 | int pthread_cond_timedwait (pthread_cond_t *cv, pthread_mutex_t *mutex); |
| 5 | int pthread_cond_signal (pthread_cond_t *cv); |
| 6 | int pthread_cond_broadcast (pthread_cond_t *cv); |
| 7 | |
| 8 | struct pthread_cond_t { |
| 9 | |
| 10 | unsigned int cond_lock; |
| 11 | |
| 12 | internal mutex |
| 13 | |
| 14 | uint64_t total_seq; |
| 15 | |
| 16 | Total number of threads using the conditional variable. |
| 17 | |
| 18 | uint64_t wakeup_seq; |
| 19 | |
| 20 | sequence number for next wakeup. |
| 21 | |
| 22 | uint64_t woken_seq; |
| 23 | |
| 24 | sequence number of last woken thread. |
| 25 | |
| 26 | uint32_t broadcast_seq; |
| 27 | |
| 28 | } |
| 29 | |
| 30 | |
| 31 | struct cv_data { |
| 32 | |
| 33 | pthread_cond_t *cv; |
| 34 | |
| 35 | uint32_t bc_seq |
| 36 | |
| 37 | } |
| 38 | |
| 39 | |
| 40 | |
| 41 | cleanup_handler(cv_data) |
| 42 | { |
| 43 | cv = cv_data->cv; |
| 44 | lll_lock(cv->lock); |
| 45 | |
| 46 | if (cv_data->bc_seq == cv->broadcast_seq) { |
| 47 | ++cv->wakeup_seq; |
| 48 | ++cv->woken_seq; |
| 49 | } |
| 50 | |
| 51 | /* make sure no signal gets lost. */ |
| 52 | FUTEX_WAKE(cv->wakeup_seq, ALL); |
| 53 | |
| 54 | lll_unlock(cv->lock); |
| 55 | } |
| 56 | |
| 57 | |
| 58 | cond_timedwait(cv, mutex, timeout): |
| 59 | { |
| 60 | lll_lock(cv->lock); |
| 61 | mutex_unlock(mutex); |
| 62 | |
| 63 | cleanup_push |
| 64 | |
| 65 | ++cv->total_seq; |
| 66 | val = seq = cv->wakeup_seq; |
| 67 | cv_data.bc = cv->broadcast_seq; |
| 68 | cv_data.cv = cv; |
| 69 | |
| 70 | while (1) { |
| 71 | |
| 72 | lll_unlock(cv->lock); |
| 73 | |
| 74 | enable_async(&cv_data); |
| 75 | |
| 76 | ret = FUTEX_WAIT(cv->wakeup_seq, val, timeout); |
| 77 | |
| 78 | restore_async |
| 79 | |
| 80 | lll_lock(cv->lock); |
| 81 | |
| 82 | if (bc != cv->broadcast_seq) |
| 83 | goto bc_out; |
| 84 | |
| 85 | val = cv->wakeup_seq; |
| 86 | |
| 87 | if (val != seq && cv->woken_seq != val) { |
| 88 | ret = 0; |
| 89 | break; |
| 90 | } |
| 91 | |
| 92 | if (ret == TIMEDOUT) { |
| 93 | ++cv->wakeup_seq; |
| 94 | break; |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | ++cv->woken_seq; |
| 99 | |
| 100 | bc_out: |
| 101 | lll_unlock(cv->lock); |
| 102 | |
| 103 | cleanup_pop |
| 104 | |
| 105 | mutex_lock(mutex); |
| 106 | |
| 107 | return ret; |
| 108 | } |
| 109 | |
| 110 | cond_signal(cv) |
| 111 | { |
| 112 | lll_lock(cv->lock); |
| 113 | |
| 114 | if (cv->total_seq > cv->wakeup_seq) { |
| 115 | ++cv->wakeup_seq; |
| 116 | FUTEX_WAKE(cv->wakeup_seq, 1); |
| 117 | } |
| 118 | |
| 119 | lll_unlock(cv->lock); |
| 120 | } |
| 121 | |
| 122 | cond_broadcast(cv) |
| 123 | { |
| 124 | lll_lock(cv->lock); |
| 125 | |
| 126 | if (cv->total_seq > cv->wakeup_seq) { |
| 127 | cv->wakeup_seq = cv->total_seq; |
| 128 | cv->woken_seq = cv->total_seq; |
| 129 | ++cv->broadcast_seq; |
| 130 | FUTEX_WAKE(cv->wakeup_seq, ALL); |
| 131 | } |
| 132 | |
| 133 | lll_unlock(cv->lock); |
| 134 | } |