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