blob: 27e545721c289777d8bfb911e316be37b0d89bc0 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_SYNC_CONDITION_H_
#define LIB_SYNC_CONDITION_H_
#include <assert.h>
#include <lib/sync/mutex.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
__BEGIN_CDECLS
// A condition variable that works with a sync_mutex_t
typedef struct sync_condition {
int lock;
void* head;
void* tail;
#ifdef __cplusplus
sync_condition() : lock(0), head(nullptr), tail(nullptr) {}
#endif
} sync_condition_t;
static_assert(sizeof(((sync_condition_t*)0)->lock) == sizeof(sync_mutex_t),
"sync_condition lock storage must be the same size as sync_mutex_t");
#if !defined(__cplusplus)
#define SYNC_CONDITION_INIT ((sync_condition_t){0})
#endif
// Block until |condition| is signaled by sync_condition_signal()/sync_condition_broadcast(), or a
// spurious wake up occurs.
//
// |mutex| must be in a locked state, and will be atomically unlocked for the duration of the wait,
// then locked again before the function returns.
void sync_condition_wait(sync_condition_t* condition, sync_mutex_t* mutex);
// Block until |condition| is signaled by sync_condition_signal()/sync_condition_broadcast(), or a
// spurious wake up or a timeout occurs.
//
// |mutex| must be in a locked state, and will be atomically unlocked for the duration of the wait,
// then locked again before the function returns.
//
// ZX_TIME_INFINITE can be used for |deadline| to wait for an unlimited amount of time.
//
// Return value:
// ZX_OK if |condition| was signaled or a spurious wake up occurred.
// ZX_ERR_TIMED_OUT if the wait timed out.
zx_status_t sync_condition_timedwait(sync_condition_t* condition, sync_mutex_t* mutex,
zx_time_t deadline);
// Wake up one thread waiting for |condition|.
//
// If the woken thread was waiting on sync_condition_timedwait(), then it is guaranteed
// to receive a ZX_OK return value even if a race with a timeout occurs. As an example
// where this makes a difference, consider the following implementation of a multi-producer,
// multi-consumer queue:
//
// Message* MessageQueue::DequeueTimeout(zx_time_t deadline) {
// sync_mutex_lock(&mutex_);
// for (;;) {
// if (!list_.empty()) {
// Message* msg = list_.front();
// list_.pop_front();
// sync_mutex_unlock(&mutex_);
// return msg;
// }
// zx_status_t status = sync_condition_timedwait(&condition_, &mutex_, deadline);
// if (status == ZX_ERR_TIMED_OUT) {
// // Without the above guarantee, this would be a bug: a race between
// // a timeout and a signal() would result in a missed wakeup.
// // To fix that, we would need to recheck list_.empty() here, which
// // is not obvious and would make the code more complex.
// sync_mutex_unlock(&mutex_);
// return nullptr;
// }
// }
// }
//
// void MessageQueue::Enqueue(Message* msg) {
// sync_mutex_lock(&mutex_);
// list_.push_back(msg);
// // Signal just one waiter. Assumes that any possible waiter will dequeue the message.
// sync_condition_signal(&condvar_);
// sync_mutex_unlock(&mutex_);
// }
//
// Note that pthread does not seem to require this property, and in fact the current upstream
// implementation of pthread_cond_timedwait() in MUSL does not have it.
void sync_condition_signal(sync_condition_t* condition);
// Wake up all threads that are currently waiting for |condition|.
void sync_condition_broadcast(sync_condition_t* condition);
__END_CDECLS
#endif // LIB_SYNC_CONDITION_H_