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
18namespace v8 {
19namespace base {
20
21#if V8_OS_MACOSX
22
23Semaphore::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
31Semaphore::~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
38void Semaphore::Signal() {
39  kern_return_t result = semaphore_signal(native_handle_);
40  DCHECK_EQ(KERN_SUCCESS, result);
41  USE(result);
42}
43
44
45void 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
54bool 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
76Semaphore::Semaphore(int count) {
77  DCHECK(count >= 0);
78  int result = sem_init(&native_handle_, 0, count);
79  DCHECK_EQ(0, result);
80  USE(result);
81}
82
83
84Semaphore::~Semaphore() {
85  int result = sem_destroy(&native_handle_);
86  DCHECK_EQ(0, result);
87  USE(result);
88}
89
90
91void Semaphore::Signal() {
92  int result = sem_post(&native_handle_);
93  DCHECK_EQ(0, result);
94  USE(result);
95}
96
97
98void 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
109bool Semaphore::WaitFor(const TimeDelta& rel_time) {
110#if V8_OS_NACL
111  // PNaCL doesn't support sem_timedwait, do ugly busy waiting.
112  ElapsedTimer timer;
113  timer.Start();
114  do {
115    int result = sem_trywait(&native_handle_);
116    if (result == 0) return true;
117    DCHECK(errno == EAGAIN || errno == EINTR);
118  } while (!timer.HasExpired(rel_time));
119  return false;
120#else
121  // Compute the time for end of timeout.
122  const Time time = Time::NowFromSystemTime() + rel_time;
123  const struct timespec ts = time.ToTimespec();
124
125  // Wait for semaphore signalled or timeout.
126  while (true) {
127    int result = sem_timedwait(&native_handle_, &ts);
128    if (result == 0) return true;  // Semaphore was signalled.
129#if V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4)
130    if (result > 0) {
131      // sem_timedwait in glibc prior to 2.3.4 returns the errno instead of -1.
132      errno = result;
133      result = -1;
134    }
135#endif
136    if (result == -1 && errno == ETIMEDOUT) {
137      // Timed out while waiting for semaphore.
138      return false;
139    }
140    // Signal caused spurious wakeup.
141    DCHECK_EQ(-1, result);
142    DCHECK_EQ(EINTR, errno);
143  }
144#endif
145}
146
147#elif V8_OS_WIN
148
149Semaphore::Semaphore(int count) {
150  DCHECK(count >= 0);
151  native_handle_ = ::CreateSemaphoreA(NULL, count, 0x7fffffff, NULL);
152  DCHECK(native_handle_ != NULL);
153}
154
155
156Semaphore::~Semaphore() {
157  BOOL result = CloseHandle(native_handle_);
158  DCHECK(result);
159  USE(result);
160}
161
162
163void Semaphore::Signal() {
164  LONG dummy;
165  BOOL result = ReleaseSemaphore(native_handle_, 1, &dummy);
166  DCHECK(result);
167  USE(result);
168}
169
170
171void Semaphore::Wait() {
172  DWORD result = WaitForSingleObject(native_handle_, INFINITE);
173  DCHECK(result == WAIT_OBJECT_0);
174  USE(result);
175}
176
177
178bool Semaphore::WaitFor(const TimeDelta& rel_time) {
179  TimeTicks now = TimeTicks::Now();
180  TimeTicks end = now + rel_time;
181  while (true) {
182    int64_t msec = (end - now).InMilliseconds();
183    if (msec >= static_cast<int64_t>(INFINITE)) {
184      DWORD result = WaitForSingleObject(native_handle_, INFINITE - 1);
185      if (result == WAIT_OBJECT_0) {
186        return true;
187      }
188      DCHECK(result == WAIT_TIMEOUT);
189      now = TimeTicks::Now();
190    } else {
191      DWORD result = WaitForSingleObject(
192          native_handle_, (msec < 0) ? 0 : static_cast<DWORD>(msec));
193      if (result == WAIT_TIMEOUT) {
194        return false;
195      }
196      DCHECK(result == WAIT_OBJECT_0);
197      return true;
198    }
199  }
200}
201
202#endif  // V8_OS_MACOSX
203
204} }  // namespace v8::base
205