1// Copyright 2013 the V8 project 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 "src/base/platform/condition-variable.h"
6
7#include <errno.h>
8#include <time.h>
9
10#include "src/base/platform/time.h"
11
12namespace v8 {
13namespace base {
14
15#if V8_OS_POSIX
16
17ConditionVariable::ConditionVariable() {
18  // TODO(bmeurer): The test for V8_LIBRT_NOT_AVAILABLE is a temporary
19  // hack to support cross-compiling Chrome for Android in AOSP. Remove
20  // this once AOSP is fixed.
21#if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
22     (V8_OS_LINUX && V8_LIBC_GLIBC)) && !V8_LIBRT_NOT_AVAILABLE
23  // On Free/Net/OpenBSD and Linux with glibc we can change the time
24  // source for pthread_cond_timedwait() to use the monotonic clock.
25  pthread_condattr_t attr;
26  int result = pthread_condattr_init(&attr);
27  DCHECK_EQ(0, result);
28  result = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
29  DCHECK_EQ(0, result);
30  result = pthread_cond_init(&native_handle_, &attr);
31  DCHECK_EQ(0, result);
32  result = pthread_condattr_destroy(&attr);
33#else
34  int result = pthread_cond_init(&native_handle_, NULL);
35#endif
36  DCHECK_EQ(0, result);
37  USE(result);
38}
39
40
41ConditionVariable::~ConditionVariable() {
42  int result = pthread_cond_destroy(&native_handle_);
43  DCHECK_EQ(0, result);
44  USE(result);
45}
46
47
48void ConditionVariable::NotifyOne() {
49  int result = pthread_cond_signal(&native_handle_);
50  DCHECK_EQ(0, result);
51  USE(result);
52}
53
54
55void ConditionVariable::NotifyAll() {
56  int result = pthread_cond_broadcast(&native_handle_);
57  DCHECK_EQ(0, result);
58  USE(result);
59}
60
61
62void ConditionVariable::Wait(Mutex* mutex) {
63  mutex->AssertHeldAndUnmark();
64  int result = pthread_cond_wait(&native_handle_, &mutex->native_handle());
65  DCHECK_EQ(0, result);
66  USE(result);
67  mutex->AssertUnheldAndMark();
68}
69
70
71bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
72  struct timespec ts;
73  int result;
74  mutex->AssertHeldAndUnmark();
75#if V8_OS_MACOSX
76  // Mac OS X provides pthread_cond_timedwait_relative_np(), which does
77  // not depend on the real time clock, which is what you really WANT here!
78  ts = rel_time.ToTimespec();
79  DCHECK_GE(ts.tv_sec, 0);
80  DCHECK_GE(ts.tv_nsec, 0);
81  result = pthread_cond_timedwait_relative_np(
82      &native_handle_, &mutex->native_handle(), &ts);
83#else
84  // TODO(bmeurer): The test for V8_LIBRT_NOT_AVAILABLE is a temporary
85  // hack to support cross-compiling Chrome for Android in AOSP. Remove
86  // this once AOSP is fixed.
87#if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
88     (V8_OS_LINUX && V8_LIBC_GLIBC)) && !V8_LIBRT_NOT_AVAILABLE
89  // On Free/Net/OpenBSD and Linux with glibc we can change the time
90  // source for pthread_cond_timedwait() to use the monotonic clock.
91  result = clock_gettime(CLOCK_MONOTONIC, &ts);
92  DCHECK_EQ(0, result);
93  Time now = Time::FromTimespec(ts);
94#else
95  // The timeout argument to pthread_cond_timedwait() is in absolute time.
96  Time now = Time::NowFromSystemTime();
97#endif
98  Time end_time = now + rel_time;
99  DCHECK_GE(end_time, now);
100  ts = end_time.ToTimespec();
101  result = pthread_cond_timedwait(
102      &native_handle_, &mutex->native_handle(), &ts);
103#endif  // V8_OS_MACOSX
104  mutex->AssertUnheldAndMark();
105  if (result == ETIMEDOUT) {
106    return false;
107  }
108  DCHECK_EQ(0, result);
109  return true;
110}
111
112#elif V8_OS_WIN
113
114struct ConditionVariable::Event {
115  Event() : handle_(::CreateEventA(NULL, true, false, NULL)) {
116    DCHECK(handle_ != NULL);
117  }
118
119  ~Event() {
120    BOOL ok = ::CloseHandle(handle_);
121    DCHECK(ok);
122    USE(ok);
123  }
124
125  bool WaitFor(DWORD timeout_ms) {
126    DWORD result = ::WaitForSingleObject(handle_, timeout_ms);
127    if (result == WAIT_OBJECT_0) {
128      return true;
129    }
130    DCHECK(result == WAIT_TIMEOUT);
131    return false;
132  }
133
134  HANDLE handle_;
135  Event* next_;
136  HANDLE thread_;
137  volatile bool notified_;
138};
139
140
141ConditionVariable::NativeHandle::~NativeHandle() {
142  DCHECK(waitlist_ == NULL);
143
144  while (freelist_ != NULL) {
145    Event* event = freelist_;
146    freelist_ = event->next_;
147    delete event;
148  }
149}
150
151
152ConditionVariable::Event* ConditionVariable::NativeHandle::Pre() {
153  LockGuard<Mutex> lock_guard(&mutex_);
154
155  // Grab an event from the free list or create a new one.
156  Event* event = freelist_;
157  if (event != NULL) {
158    freelist_ = event->next_;
159  } else {
160    event = new Event;
161  }
162  event->thread_ = GetCurrentThread();
163  event->notified_ = false;
164
165#ifdef DEBUG
166  // The event must not be on the wait list.
167  for (Event* we = waitlist_; we != NULL; we = we->next_) {
168    DCHECK_NE(event, we);
169  }
170#endif
171
172  // Prepend the event to the wait list.
173  event->next_ = waitlist_;
174  waitlist_ = event;
175
176  return event;
177}
178
179
180void ConditionVariable::NativeHandle::Post(Event* event, bool result) {
181  LockGuard<Mutex> lock_guard(&mutex_);
182
183  // Remove the event from the wait list.
184  for (Event** wep = &waitlist_;; wep = &(*wep)->next_) {
185    DCHECK_NE(NULL, *wep);
186    if (*wep == event) {
187      *wep = event->next_;
188      break;
189    }
190  }
191
192#ifdef DEBUG
193  // The event must not be on the free list.
194  for (Event* fe = freelist_; fe != NULL; fe = fe->next_) {
195    DCHECK_NE(event, fe);
196  }
197#endif
198
199  // Reset the event.
200  BOOL ok = ::ResetEvent(event->handle_);
201  DCHECK(ok);
202  USE(ok);
203
204  // Insert the event into the free list.
205  event->next_ = freelist_;
206  freelist_ = event;
207
208  // Forward signals delivered after the timeout to the next waiting event.
209  if (!result && event->notified_ && waitlist_ != NULL) {
210    ok = ::SetEvent(waitlist_->handle_);
211    DCHECK(ok);
212    USE(ok);
213    waitlist_->notified_ = true;
214  }
215}
216
217
218ConditionVariable::ConditionVariable() {}
219
220
221ConditionVariable::~ConditionVariable() {}
222
223
224void ConditionVariable::NotifyOne() {
225  // Notify the thread with the highest priority in the waitlist
226  // that was not already signalled.
227  LockGuard<Mutex> lock_guard(native_handle_.mutex());
228  Event* highest_event = NULL;
229  int highest_priority = std::numeric_limits<int>::min();
230  for (Event* event = native_handle().waitlist();
231       event != NULL;
232       event = event->next_) {
233    if (event->notified_) {
234      continue;
235    }
236    int priority = GetThreadPriority(event->thread_);
237    DCHECK_NE(THREAD_PRIORITY_ERROR_RETURN, priority);
238    if (priority >= highest_priority) {
239      highest_priority = priority;
240      highest_event = event;
241    }
242  }
243  if (highest_event != NULL) {
244    DCHECK(!highest_event->notified_);
245    ::SetEvent(highest_event->handle_);
246    highest_event->notified_ = true;
247  }
248}
249
250
251void ConditionVariable::NotifyAll() {
252  // Notify all threads on the waitlist.
253  LockGuard<Mutex> lock_guard(native_handle_.mutex());
254  for (Event* event = native_handle().waitlist();
255       event != NULL;
256       event = event->next_) {
257    if (!event->notified_) {
258      ::SetEvent(event->handle_);
259      event->notified_ = true;
260    }
261  }
262}
263
264
265void ConditionVariable::Wait(Mutex* mutex) {
266  // Create and setup the wait event.
267  Event* event = native_handle_.Pre();
268
269  // Release the user mutex.
270  mutex->Unlock();
271
272  // Wait on the wait event.
273  while (!event->WaitFor(INFINITE))
274    ;
275
276  // Reaquire the user mutex.
277  mutex->Lock();
278
279  // Release the wait event (we must have been notified).
280  DCHECK(event->notified_);
281  native_handle_.Post(event, true);
282}
283
284
285bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
286  // Create and setup the wait event.
287  Event* event = native_handle_.Pre();
288
289  // Release the user mutex.
290  mutex->Unlock();
291
292  // Wait on the wait event.
293  TimeTicks now = TimeTicks::Now();
294  TimeTicks end = now + rel_time;
295  bool result = false;
296  while (true) {
297    int64_t msec = (end - now).InMilliseconds();
298    if (msec >= static_cast<int64_t>(INFINITE)) {
299      result = event->WaitFor(INFINITE - 1);
300      if (result) {
301        break;
302      }
303      now = TimeTicks::Now();
304    } else {
305      result = event->WaitFor((msec < 0) ? 0 : static_cast<DWORD>(msec));
306      break;
307    }
308  }
309
310  // Reaquire the user mutex.
311  mutex->Lock();
312
313  // Release the wait event.
314  DCHECK(!result || event->notified_);
315  native_handle_.Post(event, result);
316
317  return result;
318}
319
320#endif  // V8_OS_POSIX
321
322} }  // namespace v8::base
323