19struct _SECURITY_ATTRIBUTES;
20__declspec(dllimport)
void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes,
23 const wchar_t* lpName);
24__declspec(dllimport)
int __stdcall CloseHandle(
void* hObject);
25__declspec(dllimport)
unsigned long __stdcall WaitForSingleObject(
void* hHandle,
unsigned long dwMilliseconds);
26__declspec(dllimport)
int __stdcall ReleaseSemaphore(
void* hSemaphore,
long lReleaseCount,
long* lpPreviousCount);
28#elif defined(__MACH__)
30#elif defined(__unix__) || defined(__wasm__)
33#if defined(__GLIBC_PREREQ) && defined(_GNU_SOURCE)
34#if __GLIBC_PREREQ(2, 30)
35#define MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
69 Semaphore(
const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
70 Semaphore& operator=(
const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
73 Semaphore(
int initialCount = 0)
75 assert(initialCount >= 0);
76 const long maxLong = 0x7fffffff;
77 m_hSema = CreateSemaphoreW(
nullptr, initialCount, maxLong,
nullptr);
81 ~Semaphore() { CloseHandle(m_hSema); }
85 const unsigned long infinite = 0xffffffff;
86 return WaitForSingleObject(m_hSema, infinite) == 0;
89 bool try_wait() {
return WaitForSingleObject(m_hSema, 0) == 0; }
91 bool timed_wait(std::uint64_t usecs) {
return WaitForSingleObject(m_hSema, (
unsigned long)(usecs / 1000)) == 0; }
93 void signal(
int count = 1)
95 while (!ReleaseSemaphore(m_hSema, count,
nullptr))
99#elif defined(__MACH__)
108 Semaphore(
const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
109 Semaphore& operator=(
const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
112 Semaphore(
int initialCount = 0)
114 assert(initialCount >= 0);
115 kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
116 assert(rc == KERN_SUCCESS);
120 ~Semaphore() { semaphore_destroy(mach_task_self(), m_sema); }
122 bool wait() {
return semaphore_wait(m_sema) == KERN_SUCCESS; }
124 bool try_wait() {
return timed_wait(0); }
126 bool timed_wait(std::uint64_t timeout_usecs)
129 ts.tv_sec =
static_cast<unsigned int>(timeout_usecs / 1000000);
130 ts.tv_nsec =
static_cast<int>((timeout_usecs % 1000000) * 1000);
134 kern_return_t rc = semaphore_timedwait(m_sema, ts);
135 return rc == KERN_SUCCESS;
140 while (semaphore_signal(m_sema) != KERN_SUCCESS)
144 void signal(
int count)
146 while (count-- > 0) {
147 while (semaphore_signal(m_sema) != KERN_SUCCESS)
152#elif defined(__unix__) || defined(__wasm__)
160 Semaphore(
const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
161 Semaphore& operator=(
const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
164 Semaphore(
int initialCount = 0)
166 assert(initialCount >= 0);
167 int rc = sem_init(&m_sema, 0,
static_cast<unsigned int>(initialCount));
172 ~Semaphore() { sem_destroy(&m_sema); }
179 rc = sem_wait(&m_sema);
180 }
while (rc == -1 && errno == EINTR);
188 rc = sem_trywait(&m_sema);
189 }
while (rc == -1 && errno == EINTR);
193 bool timed_wait(std::uint64_t usecs)
196 const int usecs_in_1_sec = 1000000;
197 const int nsecs_in_1_sec = 1000000000;
198#ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
199 clock_gettime(CLOCK_MONOTONIC, &ts);
201 clock_gettime(CLOCK_REALTIME, &ts);
203 ts.tv_sec += (time_t)(usecs / usecs_in_1_sec);
204 ts.tv_nsec += (long)(usecs % usecs_in_1_sec) * 1000;
207 if (ts.tv_nsec >= nsecs_in_1_sec) {
208 ts.tv_nsec -= nsecs_in_1_sec;
214#ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
215 rc = sem_clockwait(&m_sema, CLOCK_MONOTONIC, &ts);
217 rc = sem_timedwait(&m_sema, &ts);
219 }
while (rc == -1 && errno == EINTR);
225 while (sem_post(&m_sema) == -1)
229 void signal(
int count)
231 while (count-- > 0) {
232 while (sem_post(&m_sema) == -1)
238#error Unsupported platform! (No semaphore wrapper available)
248 typedef std::make_signed<std::size_t>::type ssize_t;
251 std::atomic<ssize_t> m_count;
252 details::Semaphore m_sema;
255 bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1)
258 int spin = m_maxSpins;
259 while (--spin >= 0) {
260 oldCount = m_count.load(std::memory_order_relaxed);
261 if ((oldCount > 0) && m_count.compare_exchange_strong(
262 oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
264 std::atomic_signal_fence(std::memory_order_acquire);
266 oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
269 if (timeout_usecs < 0) {
273 if (timeout_usecs > 0 && m_sema.timed_wait((std::uint64_t)timeout_usecs))
281 oldCount = m_count.load(std::memory_order_acquire);
282 if (oldCount >= 0 && m_sema.try_wait())
284 if (oldCount < 0 && m_count.compare_exchange_strong(
285 oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
290 ssize_t waitManyWithPartialSpinning(ssize_t max, std::int64_t timeout_usecs = -1)
294 int spin = m_maxSpins;
295 while (--spin >= 0) {
296 oldCount = m_count.load(std::memory_order_relaxed);
298 ssize_t newCount = oldCount > max ? oldCount - max : 0;
299 if (m_count.compare_exchange_strong(
300 oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
301 return oldCount - newCount;
303 std::atomic_signal_fence(std::memory_order_acquire);
305 oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
307 if ((timeout_usecs == 0) || (timeout_usecs < 0 && !m_sema.wait()) ||
308 (timeout_usecs > 0 && !m_sema.timed_wait((std::uint64_t)timeout_usecs))) {
310 oldCount = m_count.load(std::memory_order_acquire);
311 if (oldCount >= 0 && m_sema.try_wait())
314 m_count.compare_exchange_strong(
315 oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
321 return 1 + tryWaitMany(max - 1);
327 : m_count(initialCount)
328 , m_maxSpins(maxSpins)
330 assert(initialCount >= 0);
331 assert(maxSpins >= 0);
336 ssize_t oldCount = m_count.load(std::memory_order_relaxed);
337 while (oldCount > 0) {
338 if (m_count.compare_exchange_weak(
339 oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
345 bool wait() {
return tryWait() || waitWithPartialSpinning(); }
347 bool wait(std::int64_t timeout_usecs) {
return tryWait() || waitWithPartialSpinning(timeout_usecs); }
350 ssize_t tryWaitMany(ssize_t max)
353 ssize_t oldCount = m_count.load(std::memory_order_relaxed);
354 while (oldCount > 0) {
355 ssize_t newCount = oldCount > max ? oldCount - max : 0;
356 if (m_count.compare_exchange_weak(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
357 return oldCount - newCount;
363 ssize_t waitMany(ssize_t max, std::int64_t timeout_usecs)
366 ssize_t result = tryWaitMany(max);
367 if (result == 0 && max > 0)
368 result = waitManyWithPartialSpinning(max, timeout_usecs);
372 ssize_t waitMany(ssize_t max)
374 ssize_t result = waitMany(max, -1);
379 void signal(ssize_t count = 1)
382 ssize_t oldCount = m_count.fetch_add(count, std::memory_order_release);
383 ssize_t toRelease = -oldCount < count ? -oldCount : count;
385 m_sema.signal((
int)toRelease);
389 std::size_t availableApprox()
const
391 ssize_t count = m_count.load(std::memory_order_relaxed);
392 return count > 0 ?
static_cast<std::size_t
>(count) : 0;
Definition: lightweightsemaphore.h:246