1// Copyright (c) 2011 The Chromium 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#include "base/synchronization/condition_variable.h"
6
7#include <errno.h>
8#include <stdint.h>
9#include <sys/time.h>
10
11#include "base/synchronization/lock.h"
12#include "base/threading/thread_restrictions.h"
13#include "base/time/time.h"
14#include "build/build_config.h"
15
16namespace base {
17
18ConditionVariable::ConditionVariable(Lock* user_lock)
19    : user_mutex_(user_lock->lock_.native_handle())
20#if DCHECK_IS_ON()
21    , user_lock_(user_lock)
22#endif
23{
24  int rv = 0;
25  // http://crbug.com/293736
26  // NaCl doesn't support monotonic clock based absolute deadlines.
27  // On older Android platform versions, it's supported through the
28  // non-standard pthread_cond_timedwait_monotonic_np. Newer platform
29  // versions have pthread_condattr_setclock.
30  // Mac can use relative time deadlines.
31#if !defined(OS_MACOSX) && !defined(OS_NACL) && \
32      !(defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
33  pthread_condattr_t attrs;
34  rv = pthread_condattr_init(&attrs);
35  DCHECK_EQ(0, rv);
36  pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
37  rv = pthread_cond_init(&condition_, &attrs);
38  pthread_condattr_destroy(&attrs);
39#else
40  rv = pthread_cond_init(&condition_, NULL);
41#endif
42  DCHECK_EQ(0, rv);
43}
44
45ConditionVariable::~ConditionVariable() {
46#if defined(OS_MACOSX)
47  // This hack is necessary to avoid a fatal pthreads subsystem bug in the
48  // Darwin kernel. http://crbug.com/517681.
49  {
50    base::Lock lock;
51    base::AutoLock l(lock);
52    struct timespec ts;
53    ts.tv_sec = 0;
54    ts.tv_nsec = 1;
55    pthread_cond_timedwait_relative_np(&condition_, lock.lock_.native_handle(),
56                                       &ts);
57  }
58#endif
59
60  int rv = pthread_cond_destroy(&condition_);
61  DCHECK_EQ(0, rv);
62}
63
64void ConditionVariable::Wait() {
65  base::ThreadRestrictions::AssertWaitAllowed();
66#if DCHECK_IS_ON()
67  user_lock_->CheckHeldAndUnmark();
68#endif
69  int rv = pthread_cond_wait(&condition_, user_mutex_);
70  DCHECK_EQ(0, rv);
71#if DCHECK_IS_ON()
72  user_lock_->CheckUnheldAndMark();
73#endif
74}
75
76void ConditionVariable::TimedWait(const TimeDelta& max_time) {
77  base::ThreadRestrictions::AssertWaitAllowed();
78  int64_t usecs = max_time.InMicroseconds();
79  struct timespec relative_time;
80  relative_time.tv_sec = usecs / Time::kMicrosecondsPerSecond;
81  relative_time.tv_nsec =
82      (usecs % Time::kMicrosecondsPerSecond) * Time::kNanosecondsPerMicrosecond;
83
84#if DCHECK_IS_ON()
85  user_lock_->CheckHeldAndUnmark();
86#endif
87
88#if defined(OS_MACOSX)
89  int rv = pthread_cond_timedwait_relative_np(
90      &condition_, user_mutex_, &relative_time);
91#else
92  // The timeout argument to pthread_cond_timedwait is in absolute time.
93  struct timespec absolute_time;
94#if defined(OS_NACL)
95  // See comment in constructor for why this is different in NaCl.
96  struct timeval now;
97  gettimeofday(&now, NULL);
98  absolute_time.tv_sec = now.tv_sec;
99  absolute_time.tv_nsec = now.tv_usec * Time::kNanosecondsPerMicrosecond;
100#else
101  struct timespec now;
102  clock_gettime(CLOCK_MONOTONIC, &now);
103  absolute_time.tv_sec = now.tv_sec;
104  absolute_time.tv_nsec = now.tv_nsec;
105#endif
106
107  absolute_time.tv_sec += relative_time.tv_sec;
108  absolute_time.tv_nsec += relative_time.tv_nsec;
109  absolute_time.tv_sec += absolute_time.tv_nsec / Time::kNanosecondsPerSecond;
110  absolute_time.tv_nsec %= Time::kNanosecondsPerSecond;
111  DCHECK_GE(absolute_time.tv_sec, now.tv_sec);  // Overflow paranoia
112
113#if defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
114  int rv = pthread_cond_timedwait_monotonic_np(
115      &condition_, user_mutex_, &absolute_time);
116#else
117  int rv = pthread_cond_timedwait(&condition_, user_mutex_, &absolute_time);
118#endif  // OS_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
119#endif  // OS_MACOSX
120
121  DCHECK(rv == 0 || rv == ETIMEDOUT);
122#if DCHECK_IS_ON()
123  user_lock_->CheckUnheldAndMark();
124#endif
125}
126
127void ConditionVariable::Broadcast() {
128  int rv = pthread_cond_broadcast(&condition_);
129  DCHECK_EQ(0, rv);
130}
131
132void ConditionVariable::Signal() {
133  int rv = pthread_cond_signal(&condition_);
134  DCHECK_EQ(0, rv);
135}
136
137}  // namespace base
138