FIX 4.0 Demo 1.0
Loading...
Searching...
No Matches
lightweightsemaphore.h
Go to the documentation of this file.
1// Provides an efficient implementation of a semaphore (LightweightSemaphore).
2// This is an extension of Jeff Preshing's sempahore implementation (licensed
3// under the terms of its separate zlib license) that has been adapted and
4// extended by Cameron Desrochers.
5
6#pragma once
7
8#include <cstddef> // For std::size_t
9#include <atomic>
10#include <type_traits> // For std::make_signed<T>
11
12#if defined(_WIN32)
13// Avoid including windows.h in a header; we only need a handful of
14// items, so we'll redeclare them here (this is relatively safe since
15// the API generally has to remain stable between Windows versions).
16// I know this is an ugly hack but it still beats polluting the global
17// namespace with thousands of generic names or adding a .cpp for nothing.
18extern "C" {
19 struct _SECURITY_ATTRIBUTES;
20 __declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName);
21 __declspec(dllimport) int __stdcall CloseHandle(void* hObject);
22 __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds);
23 __declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount);
24}
25#elif defined(__MACH__)
26#include <mach/mach.h>
27#elif defined(__MVS__)
28#include <zos-semaphore.h>
29#elif defined(__unix__)
30#include <semaphore.h>
31
32#if defined(__GLIBC_PREREQ) && defined(_GNU_SOURCE)
33#if __GLIBC_PREREQ(2,30)
34#define MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
35#endif
36#endif
37#endif
38
39namespace moodycamel
40{
41namespace details
42{
43
44// Code in the mpmc_sema namespace below is an adaptation of Jeff Preshing's
45// portable + lightweight semaphore implementations, originally from
46// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
47// LICENSE:
48// Copyright (c) 2015 Jeff Preshing
49//
50// This software is provided 'as-is', without any express or implied
51// warranty. In no event will the authors be held liable for any damages
52// arising from the use of this software.
53//
54// Permission is granted to anyone to use this software for any purpose,
55// including commercial applications, and to alter it and redistribute it
56// freely, subject to the following restrictions:
57//
58// 1. The origin of this software must not be misrepresented; you must not
59// claim that you wrote the original software. If you use this software
60// in a product, an acknowledgement in the product documentation would be
61// appreciated but is not required.
62// 2. Altered source versions must be plainly marked as such, and must not be
63// misrepresented as being the original software.
64// 3. This notice may not be removed or altered from any source distribution.
65#if defined(_WIN32)
66class Semaphore
67{
68private:
69 void* m_hSema;
70
71 Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
72 Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
73
74public:
75 Semaphore(int initialCount = 0)
76 {
77 assert(initialCount >= 0);
78 const long maxLong = 0x7fffffff;
79 m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
80 assert(m_hSema);
81 }
82
83 ~Semaphore()
84 {
85 CloseHandle(m_hSema);
86 }
87
88 bool wait()
89 {
90 const unsigned long infinite = 0xffffffff;
91 return WaitForSingleObject(m_hSema, infinite) == 0;
92 }
93
94 bool try_wait()
95 {
96 return WaitForSingleObject(m_hSema, 0) == 0;
97 }
98
99 bool timed_wait(std::uint64_t usecs)
100 {
101 return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0;
102 }
103
104 void signal(int count = 1)
105 {
106 while (!ReleaseSemaphore(m_hSema, count, nullptr));
107 }
108};
109#elif defined(__MACH__)
110//---------------------------------------------------------
111// Semaphore (Apple iOS and OSX)
112// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
113//---------------------------------------------------------
114class Semaphore
115{
116private:
117 semaphore_t m_sema;
118
119 Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
120 Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
121
122public:
123 Semaphore(int initialCount = 0)
124 {
125 assert(initialCount >= 0);
126 kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
127 assert(rc == KERN_SUCCESS);
128 (void)rc;
129 }
130
131 ~Semaphore()
132 {
133 semaphore_destroy(mach_task_self(), m_sema);
134 }
135
136 bool wait()
137 {
138 return semaphore_wait(m_sema) == KERN_SUCCESS;
139 }
140
141 bool try_wait()
142 {
143 return timed_wait(0);
144 }
145
146 bool timed_wait(std::uint64_t timeout_usecs)
147 {
148 mach_timespec_t ts;
149 ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000);
150 ts.tv_nsec = static_cast<int>((timeout_usecs % 1000000) * 1000);
151
152 // added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
153 kern_return_t rc = semaphore_timedwait(m_sema, ts);
154 return rc == KERN_SUCCESS;
155 }
156
157 void signal()
158 {
159 while (semaphore_signal(m_sema) != KERN_SUCCESS);
160 }
161
162 void signal(int count)
163 {
164 while (count-- > 0)
165 {
166 while (semaphore_signal(m_sema) != KERN_SUCCESS);
167 }
168 }
169};
170#elif defined(__unix__) || defined(__MVS__)
171//---------------------------------------------------------
172// Semaphore (POSIX, Linux, zOS)
173//---------------------------------------------------------
174class Semaphore
175{
176private:
177 sem_t m_sema;
178
179 Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
180 Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
181
182public:
183 Semaphore(int initialCount = 0)
184 {
185 assert(initialCount >= 0);
186 int rc = sem_init(&m_sema, 0, static_cast<unsigned int>(initialCount));
187 assert(rc == 0);
188 (void)rc;
189 }
190
191 ~Semaphore()
192 {
193 sem_destroy(&m_sema);
194 }
195
196 bool wait()
197 {
198 // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
199 int rc;
200 do {
201 rc = sem_wait(&m_sema);
202 } while (rc == -1 && errno == EINTR);
203 return rc == 0;
204 }
205
206 bool try_wait()
207 {
208 int rc;
209 do {
210 rc = sem_trywait(&m_sema);
211 } while (rc == -1 && errno == EINTR);
212 return rc == 0;
213 }
214
215 bool timed_wait(std::uint64_t usecs)
216 {
217 struct timespec ts;
218 const int usecs_in_1_sec = 1000000;
219 const int nsecs_in_1_sec = 1000000000;
220#ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
221 clock_gettime(CLOCK_MONOTONIC, &ts);
222#else
223 clock_gettime(CLOCK_REALTIME, &ts);
224#endif
225 ts.tv_sec += (time_t)(usecs / usecs_in_1_sec);
226 ts.tv_nsec += (long)(usecs % usecs_in_1_sec) * 1000;
227 // sem_timedwait bombs if you have more than 1e9 in tv_nsec
228 // so we have to clean things up before passing it in
229 if (ts.tv_nsec >= nsecs_in_1_sec) {
230 ts.tv_nsec -= nsecs_in_1_sec;
231 ++ts.tv_sec;
232 }
233
234 int rc;
235 do {
236#ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC
237 rc = sem_clockwait(&m_sema, CLOCK_MONOTONIC, &ts);
238#else
239 rc = sem_timedwait(&m_sema, &ts);
240#endif
241 } while (rc == -1 && errno == EINTR);
242 return rc == 0;
243 }
244
245 void signal()
246 {
247 while (sem_post(&m_sema) == -1);
248 }
249
250 void signal(int count)
251 {
252 while (count-- > 0)
253 {
254 while (sem_post(&m_sema) == -1);
255 }
256 }
257};
258#else
259#error Unsupported platform! (No semaphore wrapper available)
260#endif
261
262} // end namespace details
263
264
265//---------------------------------------------------------
266// LightweightSemaphore
267//---------------------------------------------------------
269{
270public:
271 typedef std::make_signed<std::size_t>::type ssize_t;
272
273private:
274 std::atomic<ssize_t> m_count;
275 details::Semaphore m_sema;
276 int m_maxSpins;
277
278 bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1)
279 {
281 int spin = m_maxSpins;
282 while (--spin >= 0)
283 {
284 oldCount = m_count.load(std::memory_order_relaxed);
285 if ((oldCount > 0) && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
286 return true;
287 std::atomic_signal_fence(std::memory_order_acquire); // Prevent the compiler from collapsing the loop.
288 }
289 oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
290 if (oldCount > 0)
291 return true;
292 if (timeout_usecs < 0)
293 {
294 if (m_sema.wait())
295 return true;
296 }
297 if (timeout_usecs > 0 && m_sema.timed_wait((std::uint64_t)timeout_usecs))
298 return true;
299 // At this point, we've timed out waiting for the semaphore, but the
300 // count is still decremented indicating we may still be waiting on
301 // it. So we have to re-adjust the count, but only if the semaphore
302 // wasn't signaled enough times for us too since then. If it was, we
303 // need to release the semaphore too.
304 while (true)
305 {
306 oldCount = m_count.load(std::memory_order_acquire);
307 if (oldCount >= 0 && m_sema.try_wait())
308 return true;
309 if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
310 return false;
311 }
312 }
313
314 ssize_t waitManyWithPartialSpinning(ssize_t max, std::int64_t timeout_usecs = -1)
315 {
316 assert(max > 0);
318 int spin = m_maxSpins;
319 while (--spin >= 0)
320 {
321 oldCount = m_count.load(std::memory_order_relaxed);
322 if (oldCount > 0)
323 {
325 if (m_count.compare_exchange_strong(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
326 return oldCount - newCount;
327 }
328 std::atomic_signal_fence(std::memory_order_acquire);
329 }
330 oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
331 if (oldCount <= 0)
332 {
333 if ((timeout_usecs == 0) || (timeout_usecs < 0 && !m_sema.wait()) || (timeout_usecs > 0 && !m_sema.timed_wait((std::uint64_t)timeout_usecs)))
334 {
335 while (true)
336 {
337 oldCount = m_count.load(std::memory_order_acquire);
338 if (oldCount >= 0 && m_sema.try_wait())
339 break;
340 if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
341 return 0;
342 }
343 }
344 }
345 if (max > 1)
346 return 1 + tryWaitMany(max - 1);
347 return 1;
348 }
349
350public:
351 LightweightSemaphore(ssize_t initialCount = 0, int maxSpins = 10000) : m_count(initialCount), m_maxSpins(maxSpins)
352 {
353 assert(initialCount >= 0);
354 assert(maxSpins >= 0);
355 }
356
357 bool tryWait()
358 {
359 ssize_t oldCount = m_count.load(std::memory_order_relaxed);
360 while (oldCount > 0)
361 {
362 if (m_count.compare_exchange_weak(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
363 return true;
364 }
365 return false;
366 }
367
368 bool wait()
369 {
370 return tryWait() || waitWithPartialSpinning();
371 }
372
373 bool wait(std::int64_t timeout_usecs)
374 {
375 return tryWait() || waitWithPartialSpinning(timeout_usecs);
376 }
377
378 // Acquires between 0 and (greedily) max, inclusive
380 {
381 assert(max >= 0);
382 ssize_t oldCount = m_count.load(std::memory_order_relaxed);
383 while (oldCount > 0)
384 {
386 if (m_count.compare_exchange_weak(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
387 return oldCount - newCount;
388 }
389 return 0;
390 }
391
392 // Acquires at least one, and (greedily) at most max
394 {
395 assert(max >= 0);
397 if (result == 0 && max > 0)
398 result = waitManyWithPartialSpinning(max, timeout_usecs);
399 return result;
400 }
401
403 {
404 ssize_t result = waitMany(max, -1);
405 assert(result > 0);
406 return result;
407 }
408
410 {
411 assert(count >= 0);
412 ssize_t oldCount = m_count.fetch_add(count, std::memory_order_release);
414 if (toRelease > 0)
415 {
416 m_sema.signal((int)toRelease);
417 }
418 }
419
420 std::size_t availableApprox() const
421 {
422 ssize_t count = m_count.load(std::memory_order_relaxed);
423 return count > 0 ? static_cast<std::size_t>(count) : 0;
424 }
425};
426
427} // end namespace moodycamel
Definition concurrentqueue.h:768
Definition lightweightsemaphore.h:269
bool wait(std::int64_t timeout_usecs)
Definition lightweightsemaphore.h:373
bool tryWait()
Definition lightweightsemaphore.h:357
LightweightSemaphore(ssize_t initialCount=0, int maxSpins=10000)
Definition lightweightsemaphore.h:351
bool wait()
Definition lightweightsemaphore.h:368
ssize_t waitMany(ssize_t max)
Definition lightweightsemaphore.h:402
void signal(ssize_t count=1)
Definition lightweightsemaphore.h:409
std::size_t availableApprox() const
Definition lightweightsemaphore.h:420
ssize_t waitMany(ssize_t max, std::int64_t timeout_usecs)
Definition lightweightsemaphore.h:393
ssize_t tryWaitMany(ssize_t max)
Definition lightweightsemaphore.h:379
std::make_signed< std::size_t >::type ssize_t
Definition lightweightsemaphore.h:271
#define MOODYCAMEL_DELETE_FUNCTION
Definition concurrentqueue.h:233
Definition blockingconcurrentqueue.h:20