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