1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef LIB_SYNC_CONDITION_H_
6 #define LIB_SYNC_CONDITION_H_
7 
8 #include <lib/sync/mutex.h>
9 #include <zircon/compiler.h>
10 #include <zircon/types.h>
11 
12 __BEGIN_CDECLS
13 
14 // A condition variable that works with a sync_mutex_t
15 typedef struct sync_condition {
16     int lock;
17     void* head;
18     void* tail;
19 
20 #ifdef __cplusplus
sync_conditionsync_condition21     sync_condition()
22         : lock(0), head(nullptr), tail(nullptr) {}
23 #endif
24 } sync_condition_t;
25 
26 #if !defined(__cplusplus)
27 #define SYNC_CONDITION_INIT ((sync_condition_t){0})
28 #endif
29 
30 // Block until |condition| is signaled by sync_condition_signal()/sync_condition_broadcast(), or a spurious
31 // wake up occurs.
32 //
33 // |mutex| must be in a locked state, and will be atomically unlocked for the duration of the wait,
34 // then locked again before the function returns.
35 void sync_condition_wait(sync_condition_t* condition, sync_mutex_t* mutex);
36 
37 // Block until |condition| is signaled by sync_condition_signal()/sync_condition_broadcast(), or a spurious
38 // wake up or a timeout occurs.
39 //
40 // |mutex| must be in a locked state, and will be atomically unlocked for the duration of the wait,
41 // then locked again before the function returns.
42 //
43 // ZX_TIME_INFINITE can be used for |deadline| to wait for an unlimited amount of time.
44 //
45 // Return value:
46 //      ZX_OK if |condition| was signaled or a spurious wake up occurred.
47 //      ZX_ERR_TIMED_OUT if the wait timed out.
48 zx_status_t sync_condition_timedwait(sync_condition_t* condition, sync_mutex_t* mutex, zx_time_t deadline);
49 
50 // Wake up one thread waiting for |condition|.
51 //
52 // If the woken thread was waiting on sync_condition_timedwait(), then it is guaranteed
53 // to receive a ZX_OK return value even if a race with a timeout occurs. As an example
54 // where this makes a difference, consider the following implementation of a multi-producer,
55 // multi-consumer queue:
56 //
57 // Message* MessageQueue::DequeueTimeout(zx_time_t deadline) {
58 //    sync_mutex_lock(&mutex_);
59 //    for (;;) {
60 //        if (!list_.empty()) {
61 //            Message* msg = list_.front();
62 //            list_.pop_front();
63 //            sync_mutex_unlock(&mutex_);
64 //            return msg;
65 //        }
66 //        zx_status_t status = sync_condition_timedwait(&condition_, &mutex_, deadline);
67 //        if (status == ZX_ERR_TIMED_OUT) {
68 //            // Without the above guarantee, this would be a bug: a race between
69 //            // a timeout and a signal() would result in a missed wakeup.
70 //            // To fix that, we would need to recheck list_.empty() here, which
71 //            // is not obvious and would make the code more complex.
72 //            sync_mutex_unlock(&mutex_);
73 //            return nullptr;
74 //        }
75 //    }
76 // }
77 //
78 // void MessageQueue::Enqueue(Message* msg) {
79 //     sync_mutex_lock(&mutex_);
80 //     list_.push_back(msg);
81 //     // Signal just one waiter. Assumes that any possible waiter will dequeue the message.
82 //     sync_condition_signal(&condvar_);
83 //     sync_mutex_unlock(&mutex_);
84 // }
85 //
86 // Note that pthread does not seem to require this property, and in fact the current upstream
87 // implementation of pthread_cond_timedwait() in MUSL does not have it.
88 void sync_condition_signal(sync_condition_t* condition);
89 
90 // Wake up all threads that are currently waiting for |condition|.
91 void sync_condition_broadcast(sync_condition_t* condition);
92 
93 __END_CDECLS
94 
95 #endif // LIB_SYNC_CONDITION_H_
96