blob: b675bf0a862f8cabcee7b6eea6431c0f0caf20f8 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/edk/util/cond_var.h"
#include <errno.h>
#include <string.h>
#include <time.h>
#include <limits>
#include "build/build_config.h"
#include "mojo/edk/util/logging_internal.h"
#include "mojo/edk/util/mutex.h"
namespace mojo {
namespace util {
namespace {
// Helper for |CondVar::WaitWithTimeout()|. Returns true on (definite) time-out.
bool RelativeTimedWait(const struct timespec& timeout_rel,
pthread_cond_t* posix_cond_var,
pthread_mutex_t* posix_mutex) {
// Mac has a function to do a relative timed wait directly.
#if defined(OS_MACOSX)
int error = pthread_cond_timedwait_relative_np(posix_cond_var, posix_mutex,
&timeout_rel);
INTERNAL_DCHECK_WITH_ERRNO(error == 0 || error == ETIMEDOUT || error == EINTR,
"pthread_cond_timedwait_relative_np", error);
return error == ETIMEDOUT;
#else
static const long kNanosecondsPerSecond = 1000000000L;
// NaCl's |pthread_condattr_setclock()| only supports |CLOCK_REALTIME| (which is
// the default, which is why we don't bother setting it in |CondVar|'s
// constructor).
#if defined(OS_NACL)
static const clockid_t kClockType = CLOCK_REALTIME;
#else
static const clockid_t kClockType = CLOCK_MONOTONIC;
#endif // defined(OS_NACL)
struct timespec timeout_abs;
int error = clock_gettime(kClockType, &timeout_abs);
// Note: The return value of |clock_gettime()| is *not* an error code, unlike
// the pthreads functions (however, it sets errno).
INTERNAL_DCHECK_WITH_ERRNO(!error, "clock_gettime", errno);
timeout_abs.tv_sec += timeout_rel.tv_sec;
timeout_abs.tv_nsec += timeout_rel.tv_nsec;
if (timeout_abs.tv_nsec >= kNanosecondsPerSecond) {
timeout_abs.tv_sec++;
timeout_abs.tv_nsec -= kNanosecondsPerSecond;
INTERNAL_DCHECK(timeout_abs.tv_nsec < kNanosecondsPerSecond);
}
// Older Android doesn't have |pthread_condattr_setclock()|, but they have
// |pthread_cond_timedwait_monotonic_np()|.
#if defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
error = pthread_cond_timedwait_monotonic_np(posix_cond_var, posix_mutex,
&timeout_abs);
INTERNAL_DCHECK_WITH_ERRNO(error == 0 || error == ETIMEDOUT || error == EINTR,
"pthread_cond_timedwait_monotonic_np", error);
#else
error = pthread_cond_timedwait(posix_cond_var, posix_mutex, &timeout_abs);
INTERNAL_DCHECK_WITH_ERRNO(error == 0 || error == ETIMEDOUT || error == EINTR,
"pthread_cond_timedwait", error);
#endif // defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
return error == ETIMEDOUT;
#endif // defined(OS_MACOSX)
}
} // namespace
CondVar::CondVar() {
// Mac and older Android don't have |pthread_condattr_setclock()| (but they have
// other timed wait functions we can use) and NaCl doesn't have a useful one.
#if !defined(OS_MACOSX) && !defined(OS_NACL) && \
!(defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
pthread_condattr_t attr;
int error = pthread_condattr_init(&attr);
INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_condattr_init", error);
error = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_condattr_setclock", error);
error = pthread_cond_init(&impl_, &attr);
INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_init", error);
error = pthread_condattr_destroy(&attr);
INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_condattr_destroy", error);
#else
int error = pthread_cond_init(&impl_, nullptr);
INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_init", error);
#endif // !defined(OS_MACOSX) && !defined(OS_NACL) && !(defined(OS_ANDROID)...)
}
CondVar::~CondVar() {
int error = pthread_cond_destroy(&impl_);
INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_destroy", error);
}
void CondVar::Wait(Mutex* mutex) {
INTERNAL_DCHECK(mutex);
mutex->AssertHeld();
int error = pthread_cond_wait(&impl_, &mutex->impl_);
INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_wait", error);
}
bool CondVar::WaitWithTimeout(Mutex* mutex, uint64_t timeout_microseconds) {
static const uint64_t kMicrosecondsPerSecond = 1000000ULL;
static const uint64_t kNanosecondsPerMicrosecond = 1000ULL;
// Turn very long waits into "forever". This isn't a huge concern if |time_t|
// is 64-bit, but overflowing |time_t| is a real risk if it's only 32-bit.
// (2^31 / 16 seconds = ~4.25 years, so we won't risk overflowing until 2033.)
constexpr uint64_t kForeverThresholdSeconds =
std::numeric_limits<time_t>::max() / 16;
uint64_t timeout_seconds = timeout_microseconds / kMicrosecondsPerSecond;
if (timeout_seconds >= kForeverThresholdSeconds) {
Wait(mutex);
return false; // Did *not* time out.
}
INTERNAL_DCHECK(mutex);
mutex->AssertHeld();
struct timespec timeout_rel = {};
timeout_rel.tv_sec = static_cast<time_t>(timeout_seconds);
timeout_rel.tv_nsec =
static_cast<long>((timeout_microseconds % kMicrosecondsPerSecond) *
kNanosecondsPerMicrosecond);
return RelativeTimedWait(timeout_rel, &impl_, &mutex->impl_);
}
void CondVar::Signal() {
int error = pthread_cond_signal(&impl_);
INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_signal", error);
}
void CondVar::SignalAll() {
int error = pthread_cond_broadcast(&impl_);
INTERNAL_DCHECK_WITH_ERRNO(!error, "pthread_cond_broadcast", error);
}
} // namespace util
} // namespace mojo