FAST  3.2.0
Framework for Heterogeneous Medical Image Computing and Visualization
Semaphore.hpp
Go to the documentation of this file.
1 //---------------------------------------------------------
2 // For conditions of distribution and use, see
3 // https://github.com/preshing/cpp11-on-multicore/blob/master/LICENSE
4 //---------------------------------------------------------
5 /*
6 Copyright (c) 2015 Jeff Preshing
7 
8 This software is provided 'as-is', without any express or implied
9 warranty. In no event will the authors be held liable for any damages
10 arising from the use of this software.
11 
12 Permission is granted to anyone to use this software for any purpose,
13 including commercial applications, and to alter it and redistribute it
14 freely, subject to the following restrictions:
15 
16 1. The origin of this software must not be misrepresented; you must not
17  claim that you wrote the original software. If you use this software
18  in a product, an acknowledgement in the product documentation would be
19  appreciated but is not required.
20 2. Altered source versions must be plainly marked as such, and must not be
21  misrepresented as being the original software.
22 3. This notice may not be removed or altered from any source distribution.
23 */
24 
25 #ifndef __CPP11OM_SEMAPHORE_H__
26 #define __CPP11OM_SEMAPHORE_H__
27 
28 #include <atomic>
29 #include <cassert>
30 
31 
32 #if defined(_WIN32)
33 //---------------------------------------------------------
34 // Semaphore (Windows)
35 //---------------------------------------------------------
36 
37 #include <windows.h>
38 #undef min
39 #undef max
40 
41 class Semaphore
42 {
43 private:
44  HANDLE m_hSema;
45 
46  Semaphore(const Semaphore& other) = delete;
47  Semaphore& operator=(const Semaphore& other) = delete;
48 
49 public:
50  Semaphore(int initialCount = 0)
51  {
52  assert(initialCount >= 0);
53  m_hSema = CreateSemaphore(NULL, initialCount, MAXLONG, NULL);
54  }
55 
56  ~Semaphore()
57  {
58  CloseHandle(m_hSema);
59  }
60 
61  void wait()
62  {
63  WaitForSingleObject(m_hSema, INFINITE);
64  }
65 
66  void signal(int count = 1)
67  {
68  ReleaseSemaphore(m_hSema, count, NULL);
69  }
70 };
71 
72 
73 #elif defined(__MACH__)
74 //---------------------------------------------------------
75 // Semaphore (Apple iOS and OSX)
76 // Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
77 //---------------------------------------------------------
78 
79 #include <mach/mach.h>
80 
81 class Semaphore
82 {
83 private:
84  semaphore_t m_sema;
85 
86  Semaphore(const Semaphore& other) = delete;
87  Semaphore& operator=(const Semaphore& other) = delete;
88 
89 public:
90  Semaphore(int initialCount = 0)
91  {
92  assert(initialCount >= 0);
93  semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
94  }
95 
96  ~Semaphore()
97  {
98  semaphore_destroy(mach_task_self(), m_sema);
99  }
100 
101  void wait()
102  {
103  semaphore_wait(m_sema);
104  }
105 
106  void signal()
107  {
108  semaphore_signal(m_sema);
109  }
110 
111  void signal(int count)
112  {
113  while (count-- > 0)
114  {
115  semaphore_signal(m_sema);
116  }
117  }
118 };
119 
120 
121 #elif defined(__unix__)
122 //---------------------------------------------------------
123 // Semaphore (POSIX, Linux)
124 //---------------------------------------------------------
125 
126 #include <semaphore.h>
127 
128 class Semaphore
129 {
130 private:
131  sem_t m_sema;
132 
133  Semaphore(const Semaphore& other) = delete;
134  Semaphore& operator=(const Semaphore& other) = delete;
135 
136 public:
137  Semaphore(int initialCount = 0)
138  {
139  assert(initialCount >= 0);
140  sem_init(&m_sema, 0, initialCount);
141  }
142 
143  ~Semaphore()
144  {
145  sem_destroy(&m_sema);
146  }
147 
148  void wait()
149  {
150  // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
151  int rc;
152  do
153  {
154  rc = sem_wait(&m_sema);
155  }
156  while (rc == -1 && errno == EINTR);
157  }
158 
159  void signal()
160  {
161  sem_post(&m_sema);
162  }
163 
164  void signal(int count)
165  {
166  while (count-- > 0)
167  {
168  sem_post(&m_sema);
169  }
170  }
171 };
172 
173 
174 #else
175 
176 #error Unsupported platform!
177 
178 #endif
179 
180 
181 //---------------------------------------------------------
182 // LightweightSemaphore
183 //---------------------------------------------------------
185 {
186 private:
187  std::atomic<int> m_count;
188  Semaphore m_sema;
189 
190  void waitWithPartialSpinning()
191  {
192  int oldCount;
193  // Is there a better way to set the initial spin count?
194  // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
195  // as threads start hitting the kernel semaphore.
196  int spin = 10000;
197  while (spin--)
198  {
199  oldCount = m_count.load(std::memory_order_relaxed);
200  if ((oldCount > 0) && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire))
201  return;
202  std::atomic_signal_fence(std::memory_order_acquire); // Prevent the compiler from collapsing the loop.
203  }
204  oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
205  if (oldCount <= 0)
206  {
207  m_sema.wait();
208  }
209  }
210 
211 public:
212  LightweightSemaphore(int initialCount = 0) : m_count(initialCount)
213  {
214  assert(initialCount >= 0);
215  }
216 
217  bool tryWait()
218  {
219  int oldCount = m_count.load(std::memory_order_relaxed);
220  return (oldCount > 0 && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire));
221  }
222 
223  void wait()
224  {
225  if (!tryWait())
226  waitWithPartialSpinning();
227  }
228 
229  void signal(int count = 1)
230  {
231  int oldCount = m_count.fetch_add(count, std::memory_order_release);
232  int toRelease = -oldCount < count ? -oldCount : count;
233  if (toRelease > 0)
234  {
235  m_sema.signal(toRelease);
236  }
237  }
238 
239  int getCount() {
240  return m_count;
241  }
242 };
243 
244 
246 
247 
248 #endif // __CPP11OM_SEMAPHORE_H__
LightweightSemaphore::wait
void wait()
Definition: Semaphore.hpp:223
LightweightSemaphore::LightweightSemaphore
LightweightSemaphore(int initialCount=0)
Definition: Semaphore.hpp:212
LightweightSemaphore::signal
void signal(int count=1)
Definition: Semaphore.hpp:229
LightweightSemaphore::getCount
int getCount()
Definition: Semaphore.hpp:239
LightweightSemaphore
Definition: Semaphore.hpp:184
DefaultSemaphoreType
LightweightSemaphore DefaultSemaphoreType
Definition: Semaphore.hpp:245
LightweightSemaphore::tryWait
bool tryWait()
Definition: Semaphore.hpp:217