1 #ifndef GENESIS_UTILS_THREADING_LIGHTWEIGHT_SEMAPHORE_H_
2 #define GENESIS_UTILS_THREADING_LIGHTWEIGHT_SEMAPHORE_H_
67 #include <type_traits>
76 struct _SECURITY_ATTRIBUTES;
77 __declspec(dllimport)
void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes,
long lInitialCount,
long lMaximumCount,
const wchar_t* lpName);
78 __declspec(dllimport)
int __stdcall CloseHandle(
void* hObject);
79 __declspec(dllimport)
unsigned long __stdcall WaitForSingleObject(
void* hHandle,
unsigned long dwMilliseconds);
80 __declspec(dllimport)
int __stdcall ReleaseSemaphore(
void* hSemaphore,
long lReleaseCount,
long* lpPreviousCount);
82 #elif defined(__MACH__)
83 #include <mach/mach.h>
84 #elif defined(__MVS__)
85 #include <zos-semaphore.h>
86 #elif defined(__unix__)
87 #include <semaphore.h>
89 #if defined(__GLIBC_PREREQ) && defined(_GNU_SOURCE)
90 #if __GLIBC_PREREQ(2, 30)
91 #define MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
130 Semaphore(
int initialCount = 0)
132 assert(initialCount >= 0);
133 const long maxLong = 0x7fffffff;
134 m_hSema = CreateSemaphoreW(
nullptr, initialCount, maxLong,
nullptr);
140 CloseHandle(m_hSema);
145 const unsigned long infinite = 0xffffffff;
146 return WaitForSingleObject(m_hSema, infinite) == 0;
151 return WaitForSingleObject(m_hSema, 0) == 0;
154 bool timed_wait(std::uint64_t usecs)
156 return WaitForSingleObject(m_hSema, (
unsigned long)(usecs / 1000)) == 0;
159 void signal(
int count = 1)
161 while (!ReleaseSemaphore(m_hSema, count,
nullptr))
165 #elif defined(__MACH__)
178 Semaphore(
int initialCount = 0)
180 assert(initialCount >= 0);
181 kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
182 assert(rc == KERN_SUCCESS);
188 semaphore_destroy(mach_task_self(), m_sema);
193 return semaphore_wait(m_sema) == KERN_SUCCESS;
198 return timed_wait(0);
201 bool timed_wait(std::uint64_t timeout_usecs)
204 ts.tv_sec =
static_cast<unsigned int>(timeout_usecs / 1000000);
205 ts.tv_nsec =
static_cast<int>((timeout_usecs % 1000000) * 1000);
208 kern_return_t rc = semaphore_timedwait(m_sema, ts);
209 return rc == KERN_SUCCESS;
214 while (semaphore_signal(m_sema) != KERN_SUCCESS)
218 void signal(
int count)
220 while (count-- > 0) {
221 while (semaphore_signal(m_sema) != KERN_SUCCESS)
226 #elif defined(__unix__) || defined(__MVS__)
238 Semaphore(
int initialCount = 0)
240 assert(initialCount >= 0);
241 int rc = sem_init(&m_sema, 0,
static_cast<unsigned int>(initialCount));
248 sem_destroy(&m_sema);
256 rc = sem_wait(&m_sema);
257 }
while (rc == -1 && errno == EINTR);
265 rc = sem_trywait(&m_sema);
266 }
while (rc == -1 && errno == EINTR);
270 bool timed_wait(std::uint64_t usecs)
273 const int usecs_in_1_sec = 1000000;
274 const int nsecs_in_1_sec = 1000000000;
275 #ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
276 clock_gettime(CLOCK_MONOTONIC, &ts);
278 clock_gettime(CLOCK_REALTIME, &ts);
280 ts.tv_sec += (time_t)(usecs / usecs_in_1_sec);
281 ts.tv_nsec += (long)(usecs % usecs_in_1_sec) * 1000;
284 if (ts.tv_nsec >= nsecs_in_1_sec) {
285 ts.tv_nsec -= nsecs_in_1_sec;
291 #ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
292 rc = sem_clockwait(&m_sema, CLOCK_MONOTONIC, &ts);
294 rc = sem_timedwait(&m_sema, &ts);
296 }
while (rc == -1 && errno == EINTR);
302 while (sem_post(&m_sema) == -1)
306 void signal(
int count)
308 while (count-- > 0) {
309 while (sem_post(&m_sema) == -1)
315 #error Unsupported platform! (No semaphore wrapper available)
325 typedef std::make_signed<std::size_t>::type
ssize_t;
328 std::atomic<ssize_t> m_count;
329 details::Semaphore m_sema;
332 bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1)
335 int spin = m_maxSpins;
336 while (--spin >= 0) {
337 oldCount = m_count.load(std::memory_order_relaxed);
338 if ((oldCount > 0) && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
340 std::atomic_signal_fence(std::memory_order_acquire);
342 oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
345 if (timeout_usecs < 0) {
349 if (timeout_usecs > 0 && m_sema.timed_wait((std::uint64_t)timeout_usecs))
357 oldCount = m_count.load(std::memory_order_acquire);
358 if (oldCount >= 0 && m_sema.try_wait())
360 if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
365 ssize_t waitManyWithPartialSpinning(
ssize_t max, std::int64_t timeout_usecs = -1)
369 int spin = m_maxSpins;
370 while (--spin >= 0) {
371 oldCount = m_count.load(std::memory_order_relaxed);
373 ssize_t newCount = oldCount > max ? oldCount - max : 0;
374 if (m_count.compare_exchange_strong(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
375 return oldCount - newCount;
377 std::atomic_signal_fence(std::memory_order_acquire);
379 oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
381 if ((timeout_usecs == 0) || (timeout_usecs < 0 && !m_sema.wait()) || (timeout_usecs > 0 && !m_sema.timed_wait((std::uint64_t)timeout_usecs))) {
383 oldCount = m_count.load(std::memory_order_acquire);
384 if (oldCount >= 0 && m_sema.try_wait())
386 if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
398 : m_count(initialCount)
399 , m_maxSpins(maxSpins)
401 assert(initialCount >= 0);
402 assert(maxSpins >= 0);
407 ssize_t oldCount = m_count.load(std::memory_order_relaxed);
408 while (oldCount > 0) {
409 if (m_count.compare_exchange_weak(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
417 return tryWait() || waitWithPartialSpinning();
420 bool wait(std::int64_t timeout_usecs)
422 return tryWait() || waitWithPartialSpinning(timeout_usecs);
429 ssize_t oldCount = m_count.load(std::memory_order_relaxed);
430 while (oldCount > 0) {
431 ssize_t newCount = oldCount > max ? oldCount - max : 0;
432 if (m_count.compare_exchange_weak(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
433 return oldCount - newCount;
443 if (result == 0 && max > 0)
444 result = waitManyWithPartialSpinning(max, timeout_usecs);
458 ssize_t oldCount = m_count.fetch_add(count, std::memory_order_release);
459 ssize_t toRelease = -oldCount < count ? -oldCount : count;
461 m_sema.signal((
int)toRelease);
467 ssize_t count = m_count.load(std::memory_order_relaxed);
468 return count > 0 ?
static_cast<std::size_t
>(count) : 0;
475 #endif // include guard