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 
12 namespace v8 {
13 namespace base {
14 
15 #if V8_OS_POSIX
16 
ConditionVariable()17 ConditionVariable::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_, nullptr);
32 #endif
33   DCHECK_EQ(0, result);
34   USE(result);
35 }
36 
37 
~ConditionVariable()38 ConditionVariable::~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 
NotifyOne()58 void ConditionVariable::NotifyOne() {
59   int result = pthread_cond_signal(&native_handle_);
60   DCHECK_EQ(0, result);
61   USE(result);
62 }
63 
64 
NotifyAll()65 void ConditionVariable::NotifyAll() {
66   int result = pthread_cond_broadcast(&native_handle_);
67   DCHECK_EQ(0, result);
68   USE(result);
69 }
70 
71 
Wait(Mutex * mutex)72 void 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 
WaitFor(Mutex * mutex,const TimeDelta & rel_time)81 bool 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 
121 ConditionVariable::ConditionVariable() {
122   InitializeConditionVariable(&native_handle_);
123 }
124 
125 
126 ConditionVariable::~ConditionVariable() {}
127 
128 void ConditionVariable::NotifyOne() { WakeConditionVariable(&native_handle_); }
129 
130 void ConditionVariable::NotifyAll() {
131   WakeAllConditionVariable(&native_handle_);
132 }
133 
134 
135 void ConditionVariable::Wait(Mutex* mutex) {
136   mutex->AssertHeldAndUnmark();
137   SleepConditionVariableSRW(&native_handle_, &mutex->native_handle(), INFINITE,
138                             0);
139   mutex->AssertUnheldAndMark();
140 }
141 
142 
143 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
144   int64_t msec = rel_time.InMilliseconds();
145   mutex->AssertHeldAndUnmark();
146   BOOL result = SleepConditionVariableSRW(
147       &native_handle_, &mutex->native_handle(), static_cast<DWORD>(msec), 0);
148 #ifdef DEBUG
149   if (!result) {
150     // On failure, we only expect the CV to timeout. Any other error value means
151     // that we've unexpectedly woken up.
152     // Note that WAIT_TIMEOUT != ERROR_TIMEOUT. WAIT_TIMEOUT is used with the
153     // WaitFor* family of functions as a direct return value. ERROR_TIMEOUT is
154     // used with GetLastError().
155     DCHECK_EQ(static_cast<DWORD>(ERROR_TIMEOUT), GetLastError());
156   }
157 #endif
158   mutex->AssertUnheldAndMark();
159   return result != 0;
160 }
161 
162 #endif  // V8_OS_POSIX
163 
164 }  // namespace base
165 }  // namespace v8
166