summaryrefslogtreecommitdiff
path: root/libpthread/nptl/DESIGN-condvar.txt
diff options
context:
space:
mode:
Diffstat (limited to 'libpthread/nptl/DESIGN-condvar.txt')
-rw-r--r--libpthread/nptl/DESIGN-condvar.txt134
1 files changed, 134 insertions, 0 deletions
diff --git a/libpthread/nptl/DESIGN-condvar.txt b/libpthread/nptl/DESIGN-condvar.txt
new file mode 100644
index 000000000..4845251c7
--- /dev/null
+++ b/libpthread/nptl/DESIGN-condvar.txt
@@ -0,0 +1,134 @@
+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);
+}