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 #if V8_OS_WIN
13 #include <windows.h>
14 #endif
15
16 namespace v8 {
17 namespace base {
18
19 #if V8_OS_POSIX
20
ConditionVariable()21 ConditionVariable::ConditionVariable() {
22 #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
23 (V8_OS_LINUX && V8_LIBC_GLIBC))
24 // On Free/Net/OpenBSD and Linux with glibc we can change the time
25 // source for pthread_cond_timedwait() to use the monotonic clock.
26 pthread_condattr_t attr;
27 int result = pthread_condattr_init(&attr);
28 DCHECK_EQ(0, result);
29 result = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
30 DCHECK_EQ(0, result);
31 result = pthread_cond_init(&native_handle_, &attr);
32 DCHECK_EQ(0, result);
33 result = pthread_condattr_destroy(&attr);
34 #else
35 int result = pthread_cond_init(&native_handle_, nullptr);
36 #endif
37 DCHECK_EQ(0, result);
38 USE(result);
39 }
40
41
~ConditionVariable()42 ConditionVariable::~ConditionVariable() {
43 #if defined(V8_OS_DARWIN)
44 // This hack is necessary to avoid a fatal pthreads subsystem bug in the
45 // Darwin kernel. http://crbug.com/517681.
46 {
47 Mutex lock;
48 MutexGuard l(&lock);
49 struct timespec ts;
50 ts.tv_sec = 0;
51 ts.tv_nsec = 1;
52 pthread_cond_timedwait_relative_np(&native_handle_, &lock.native_handle(),
53 &ts);
54 }
55 #endif
56 int result = pthread_cond_destroy(&native_handle_);
57 DCHECK_EQ(0, result);
58 USE(result);
59 }
60
61
NotifyOne()62 void ConditionVariable::NotifyOne() {
63 int result = pthread_cond_signal(&native_handle_);
64 DCHECK_EQ(0, result);
65 USE(result);
66 }
67
68
NotifyAll()69 void ConditionVariable::NotifyAll() {
70 int result = pthread_cond_broadcast(&native_handle_);
71 DCHECK_EQ(0, result);
72 USE(result);
73 }
74
75
Wait(Mutex * mutex)76 void ConditionVariable::Wait(Mutex* mutex) {
77 mutex->AssertHeldAndUnmark();
78 int result = pthread_cond_wait(&native_handle_, &mutex->native_handle());
79 DCHECK_EQ(0, result);
80 USE(result);
81 mutex->AssertUnheldAndMark();
82 }
83
84
WaitFor(Mutex * mutex,const TimeDelta & rel_time)85 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
86 struct timespec ts;
87 int result;
88 mutex->AssertHeldAndUnmark();
89 #if V8_OS_DARWIN
90 // Mac OS X provides pthread_cond_timedwait_relative_np(), which does
91 // not depend on the real time clock, which is what you really WANT here!
92 ts = rel_time.ToTimespec();
93 DCHECK_GE(ts.tv_sec, 0);
94 DCHECK_GE(ts.tv_nsec, 0);
95 result = pthread_cond_timedwait_relative_np(
96 &native_handle_, &mutex->native_handle(), &ts);
97 #else
98 #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
99 (V8_OS_LINUX && V8_LIBC_GLIBC))
100 // On Free/Net/OpenBSD and Linux with glibc we can change the time
101 // source for pthread_cond_timedwait() to use the monotonic clock.
102 result = clock_gettime(CLOCK_MONOTONIC, &ts);
103 DCHECK_EQ(0, result);
104 Time now = Time::FromTimespec(ts);
105 #else
106 // The timeout argument to pthread_cond_timedwait() is in absolute time.
107 Time now = Time::NowFromSystemTime();
108 #endif
109 Time end_time = now + rel_time;
110 DCHECK_GE(end_time, now);
111 ts = end_time.ToTimespec();
112 result = pthread_cond_timedwait(
113 &native_handle_, &mutex->native_handle(), &ts);
114 #endif // V8_OS_DARWIN
115 mutex->AssertUnheldAndMark();
116 if (result == ETIMEDOUT) {
117 return false;
118 }
119 DCHECK_EQ(0, result);
120 return true;
121 }
122
123 #elif V8_OS_WIN
124
125 ConditionVariable::ConditionVariable() {
126 InitializeConditionVariable(V8ToWindowsType(&native_handle_));
127 }
128
129
130 ConditionVariable::~ConditionVariable() {}
131
132 void ConditionVariable::NotifyOne() {
133 WakeConditionVariable(V8ToWindowsType(&native_handle_));
134 }
135
136 void ConditionVariable::NotifyAll() {
137 WakeAllConditionVariable(V8ToWindowsType(&native_handle_));
138 }
139
140
141 void ConditionVariable::Wait(Mutex* mutex) {
142 mutex->AssertHeldAndUnmark();
143 SleepConditionVariableSRW(V8ToWindowsType(&native_handle_),
144 V8ToWindowsType(&mutex->native_handle()), INFINITE,
145 0);
146 mutex->AssertUnheldAndMark();
147 }
148
149
150 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
151 int64_t msec = rel_time.InMilliseconds();
152 mutex->AssertHeldAndUnmark();
153 BOOL result = SleepConditionVariableSRW(
154 V8ToWindowsType(&native_handle_),
155 V8ToWindowsType(&mutex->native_handle()), static_cast<DWORD>(msec), 0);
156 #ifdef DEBUG
157 if (!result) {
158 // On failure, we only expect the CV to timeout. Any other error value means
159 // that we've unexpectedly woken up.
160 // Note that WAIT_TIMEOUT != ERROR_TIMEOUT. WAIT_TIMEOUT is used with the
161 // WaitFor* family of functions as a direct return value. ERROR_TIMEOUT is
162 // used with GetLastError().
163 DCHECK_EQ(static_cast<DWORD>(ERROR_TIMEOUT), GetLastError());
164 }
165 #endif
166 mutex->AssertUnheldAndMark();
167 return result != 0;
168 }
169
170 #elif V8_OS_STARBOARD
171
172 ConditionVariable::ConditionVariable() {
173 SbConditionVariableCreate(&native_handle_, nullptr);
174 }
175
176 ConditionVariable::~ConditionVariable() {
177 SbConditionVariableDestroy(&native_handle_);
178 }
179
180 void ConditionVariable::NotifyOne() {
181 SbConditionVariableSignal(&native_handle_);
182 }
183
184 void ConditionVariable::NotifyAll() {
185 SbConditionVariableBroadcast(&native_handle_);
186 }
187
188 void ConditionVariable::Wait(Mutex* mutex) {
189 SbConditionVariableWait(&native_handle_, &mutex->native_handle());
190 }
191
192 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
193 SbTime microseconds = static_cast<SbTime>(rel_time.InMicroseconds());
194 SbConditionVariableResult result = SbConditionVariableWaitTimed(
195 &native_handle_, &mutex->native_handle(), microseconds);
196 DCHECK(result != kSbConditionVariableFailed);
197 return result == kSbConditionVariableSignaled;
198 }
199
200 #endif // V8_OS_STARBOARD
201
202 } // namespace base
203 } // namespace v8
204