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