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/semaphore.h"
6 
7 #if V8_OS_MACOSX
8 #include <mach/mach_init.h>
9 #include <mach/task.h>
10 #endif
11 
12 #include <errno.h>
13 
14 #include "src/base/logging.h"
15 #include "src/base/platform/elapsed-timer.h"
16 #include "src/base/platform/time.h"
17 
18 namespace v8 {
19 namespace base {
20 
21 #if V8_OS_MACOSX
22 
Semaphore(int count)23 Semaphore::Semaphore(int count) {
24   kern_return_t result = semaphore_create(
25       mach_task_self(), &native_handle_, SYNC_POLICY_FIFO, count);
26   DCHECK_EQ(KERN_SUCCESS, result);
27   USE(result);
28 }
29 
30 
~Semaphore()31 Semaphore::~Semaphore() {
32   kern_return_t result = semaphore_destroy(mach_task_self(), native_handle_);
33   DCHECK_EQ(KERN_SUCCESS, result);
34   USE(result);
35 }
36 
37 
Signal()38 void Semaphore::Signal() {
39   kern_return_t result = semaphore_signal(native_handle_);
40   DCHECK_EQ(KERN_SUCCESS, result);
41   USE(result);
42 }
43 
44 
Wait()45 void Semaphore::Wait() {
46   while (true) {
47     kern_return_t result = semaphore_wait(native_handle_);
48     if (result == KERN_SUCCESS) return;  // Semaphore was signalled.
49     DCHECK_EQ(KERN_ABORTED, result);
50   }
51 }
52 
53 
WaitFor(const TimeDelta & rel_time)54 bool Semaphore::WaitFor(const TimeDelta& rel_time) {
55   TimeTicks now = TimeTicks::Now();
56   TimeTicks end = now + rel_time;
57   while (true) {
58     mach_timespec_t ts;
59     if (now >= end) {
60       // Return immediately if semaphore was not signalled.
61       ts.tv_sec = 0;
62       ts.tv_nsec = 0;
63     } else {
64       ts = (end - now).ToMachTimespec();
65     }
66     kern_return_t result = semaphore_timedwait(native_handle_, ts);
67     if (result == KERN_SUCCESS) return true;  // Semaphore was signalled.
68     if (result == KERN_OPERATION_TIMED_OUT) return false;  // Timeout.
69     DCHECK_EQ(KERN_ABORTED, result);
70     now = TimeTicks::Now();
71   }
72 }
73 
74 #elif V8_OS_POSIX
75 
76 Semaphore::Semaphore(int count) {
77   DCHECK(count >= 0);
78 #if V8_LIBC_GLIBC
79   // sem_init in glibc prior to 2.1 does not zero out semaphores.
80   memset(&native_handle_, 0, sizeof(native_handle_));
81 #endif
82   int result = sem_init(&native_handle_, 0, count);
83   DCHECK_EQ(0, result);
84   USE(result);
85 }
86 
87 
88 Semaphore::~Semaphore() {
89   int result = sem_destroy(&native_handle_);
90   DCHECK_EQ(0, result);
91   USE(result);
92 }
93 
94 
95 void Semaphore::Signal() {
96   int result = sem_post(&native_handle_);
97   DCHECK_EQ(0, result);
98   USE(result);
99 }
100 
101 
102 void Semaphore::Wait() {
103   while (true) {
104     int result = sem_wait(&native_handle_);
105     if (result == 0) return;  // Semaphore was signalled.
106     // Signal caused spurious wakeup.
107     DCHECK_EQ(-1, result);
108     DCHECK_EQ(EINTR, errno);
109   }
110 }
111 
112 
113 bool Semaphore::WaitFor(const TimeDelta& rel_time) {
114 #if V8_OS_NACL
115   // PNaCL doesn't support sem_timedwait, do ugly busy waiting.
116   ElapsedTimer timer;
117   timer.Start();
118   do {
119     int result = sem_trywait(&native_handle_);
120     if (result == 0) return true;
121     DCHECK(errno == EAGAIN || errno == EINTR);
122   } while (!timer.HasExpired(rel_time));
123   return false;
124 #else
125   // Compute the time for end of timeout.
126   const Time time = Time::NowFromSystemTime() + rel_time;
127   const struct timespec ts = time.ToTimespec();
128 
129   // Wait for semaphore signalled or timeout.
130   while (true) {
131     int result = sem_timedwait(&native_handle_, &ts);
132     if (result == 0) return true;  // Semaphore was signalled.
133 #if V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4)
134     if (result > 0) {
135       // sem_timedwait in glibc prior to 2.3.4 returns the errno instead of -1.
136       errno = result;
137       result = -1;
138     }
139 #endif
140     if (result == -1 && errno == ETIMEDOUT) {
141       // Timed out while waiting for semaphore.
142       return false;
143     }
144     // Signal caused spurious wakeup.
145     DCHECK_EQ(-1, result);
146     DCHECK_EQ(EINTR, errno);
147   }
148 #endif
149 }
150 
151 #elif V8_OS_WIN
152 
153 Semaphore::Semaphore(int count) {
154   DCHECK(count >= 0);
155   native_handle_ = ::CreateSemaphoreA(NULL, count, 0x7fffffff, NULL);
156   DCHECK(native_handle_ != NULL);
157 }
158 
159 
160 Semaphore::~Semaphore() {
161   BOOL result = CloseHandle(native_handle_);
162   DCHECK(result);
163   USE(result);
164 }
165 
166 
167 void Semaphore::Signal() {
168   LONG dummy;
169   BOOL result = ReleaseSemaphore(native_handle_, 1, &dummy);
170   DCHECK(result);
171   USE(result);
172 }
173 
174 
175 void Semaphore::Wait() {
176   DWORD result = WaitForSingleObject(native_handle_, INFINITE);
177   DCHECK(result == WAIT_OBJECT_0);
178   USE(result);
179 }
180 
181 
182 bool Semaphore::WaitFor(const TimeDelta& rel_time) {
183   TimeTicks now = TimeTicks::Now();
184   TimeTicks end = now + rel_time;
185   while (true) {
186     int64_t msec = (end - now).InMilliseconds();
187     if (msec >= static_cast<int64_t>(INFINITE)) {
188       DWORD result = WaitForSingleObject(native_handle_, INFINITE - 1);
189       if (result == WAIT_OBJECT_0) {
190         return true;
191       }
192       DCHECK(result == WAIT_TIMEOUT);
193       now = TimeTicks::Now();
194     } else {
195       DWORD result = WaitForSingleObject(
196           native_handle_, (msec < 0) ? 0 : static_cast<DWORD>(msec));
197       if (result == WAIT_TIMEOUT) {
198         return false;
199       }
200       DCHECK(result == WAIT_OBJECT_0);
201       return true;
202     }
203   }
204 }
205 
206 #endif  // V8_OS_MACOSX
207 
208 }  // namespace base
209 }  // namespace v8
210