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 
Signal()37 void Semaphore::Signal() {
38   kern_return_t result = semaphore_signal(native_handle_);
39   DCHECK_EQ(KERN_SUCCESS, result);
40   USE(result);
41 }
42 
43 
Wait()44 void Semaphore::Wait() {
45   while (true) {
46     kern_return_t result = semaphore_wait(native_handle_);
47     if (result == KERN_SUCCESS) return;  // Semaphore was signalled.
48     DCHECK_EQ(KERN_ABORTED, result);
49   }
50 }
51 
52 
WaitFor(const TimeDelta & rel_time)53 bool Semaphore::WaitFor(const TimeDelta& rel_time) {
54   TimeTicks now = TimeTicks::Now();
55   TimeTicks end = now + rel_time;
56   while (true) {
57     mach_timespec_t ts;
58     if (now >= end) {
59       // Return immediately if semaphore was not signalled.
60       ts.tv_sec = 0;
61       ts.tv_nsec = 0;
62     } else {
63       ts = (end - now).ToMachTimespec();
64     }
65     kern_return_t result = semaphore_timedwait(native_handle_, ts);
66     if (result == KERN_SUCCESS) return true;  // Semaphore was signalled.
67     if (result == KERN_OPERATION_TIMED_OUT) return false;  // Timeout.
68     DCHECK_EQ(KERN_ABORTED, result);
69     now = TimeTicks::Now();
70   }
71 }
72 
73 #elif V8_OS_POSIX
74 
75 Semaphore::Semaphore(int count) {
76   DCHECK(count >= 0);
77   int result = sem_init(&native_handle_, 0, count);
78   DCHECK_EQ(0, result);
79   USE(result);
80 }
81 
82 
83 Semaphore::~Semaphore() {
84   int result = sem_destroy(&native_handle_);
85   DCHECK_EQ(0, result);
86   USE(result);
87 }
88 
89 void Semaphore::Signal() {
90   int result = sem_post(&native_handle_);
91   // This check may fail with <libc-2.21, which we use on the try bots, if the
92   // semaphore is destroyed while sem_post is still executed. A work around is
93   // to extend the lifetime of the semaphore.
94   CHECK_EQ(0, result);
95 }
96 
97 
98 void Semaphore::Wait() {
99   while (true) {
100     int result = sem_wait(&native_handle_);
101     if (result == 0) return;  // Semaphore was signalled.
102     // Signal caused spurious wakeup.
103     DCHECK_EQ(-1, result);
104     DCHECK_EQ(EINTR, errno);
105   }
106 }
107 
108 
109 bool Semaphore::WaitFor(const TimeDelta& rel_time) {
110   // Compute the time for end of timeout.
111   const Time time = Time::NowFromSystemTime() + rel_time;
112   const struct timespec ts = time.ToTimespec();
113 
114   // Wait for semaphore signalled or timeout.
115   while (true) {
116     int result = sem_timedwait(&native_handle_, &ts);
117     if (result == 0) return true;  // Semaphore was signalled.
118 #if V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4)
119     if (result > 0) {
120       // sem_timedwait in glibc prior to 2.3.4 returns the errno instead of -1.
121       errno = result;
122       result = -1;
123     }
124 #endif
125     if (result == -1 && errno == ETIMEDOUT) {
126       // Timed out while waiting for semaphore.
127       return false;
128     }
129     // Signal caused spurious wakeup.
130     DCHECK_EQ(-1, result);
131     DCHECK_EQ(EINTR, errno);
132   }
133 }
134 
135 #elif V8_OS_WIN
136 
137 Semaphore::Semaphore(int count) {
138   DCHECK(count >= 0);
139   native_handle_ = ::CreateSemaphoreA(NULL, count, 0x7fffffff, NULL);
140   DCHECK(native_handle_ != NULL);
141 }
142 
143 
144 Semaphore::~Semaphore() {
145   BOOL result = CloseHandle(native_handle_);
146   DCHECK(result);
147   USE(result);
148 }
149 
150 void Semaphore::Signal() {
151   LONG dummy;
152   BOOL result = ReleaseSemaphore(native_handle_, 1, &dummy);
153   DCHECK(result);
154   USE(result);
155 }
156 
157 
158 void Semaphore::Wait() {
159   DWORD result = WaitForSingleObject(native_handle_, INFINITE);
160   DCHECK(result == WAIT_OBJECT_0);
161   USE(result);
162 }
163 
164 
165 bool Semaphore::WaitFor(const TimeDelta& rel_time) {
166   TimeTicks now = TimeTicks::Now();
167   TimeTicks end = now + rel_time;
168   while (true) {
169     int64_t msec = (end - now).InMilliseconds();
170     if (msec >= static_cast<int64_t>(INFINITE)) {
171       DWORD result = WaitForSingleObject(native_handle_, INFINITE - 1);
172       if (result == WAIT_OBJECT_0) {
173         return true;
174       }
175       DCHECK(result == WAIT_TIMEOUT);
176       now = TimeTicks::Now();
177     } else {
178       DWORD result = WaitForSingleObject(
179           native_handle_, (msec < 0) ? 0 : static_cast<DWORD>(msec));
180       if (result == WAIT_TIMEOUT) {
181         return false;
182       }
183       DCHECK(result == WAIT_OBJECT_0);
184       return true;
185     }
186   }
187 }
188 
189 #endif  // V8_OS_MACOSX
190 
191 }  // namespace base
192 }  // namespace v8
193