1 /* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- 2 Copyright (c) 2010-2012 Marcus Geelnard 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely, subject to the following restrictions: 11 12 1. The origin of this software must not be misrepresented; you must not 13 claim that you wrote the original software. If you use this software 14 in a product, an acknowledgment in the product documentation would be 15 appreciated but is not required. 16 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 20 3. This notice may not be removed or altered from any source 21 distribution. 22 */ 23 24 #ifndef _FAST_MUTEX_H_ 25 #define _FAST_MUTEX_H_ 26 27 /// @file 28 29 // Which platform are we on? 30 #if !defined(_TTHREAD_PLATFORM_DEFINED_) 31 #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) 32 #define _TTHREAD_WIN32_ 33 #else 34 #define _TTHREAD_POSIX_ 35 #endif 36 #define _TTHREAD_PLATFORM_DEFINED_ 37 #endif 38 39 // Check if we can support the assembly language level implementation (otherwise 40 // revert to the system API) 41 // Note: on MSVC, inline assembly is not supported on the ARM and x64 processors 42 // (see https://docs.microsoft.com/en-us/cpp/assembler/inline/inline-assembler) 43 #if ((defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) || \ 44 (defined(_MSC_VER) && (defined(_M_IX86))) || \ 45 (defined(__GNUC__) && (defined(__ppc__)))) && !defined(TTHREAD_DISABLE_ASM) 46 #define _FAST_MUTEX_ASM_ 47 #else 48 #define _FAST_MUTEX_SYS_ 49 #endif 50 51 #if defined(_TTHREAD_WIN32_) 52 #ifndef WIN32_LEAN_AND_MEAN 53 #define WIN32_LEAN_AND_MEAN 54 #define __UNDEF_LEAN_AND_MEAN 55 #endif 56 #include <windows.h> 57 #ifdef __UNDEF_LEAN_AND_MEAN 58 #undef WIN32_LEAN_AND_MEAN 59 #undef __UNDEF_LEAN_AND_MEAN 60 #endif 61 #else 62 #ifdef _FAST_MUTEX_ASM_ 63 #include <sched.h> 64 #else 65 #include <pthread.h> 66 #endif 67 #endif 68 69 namespace tthread { 70 71 /// Fast mutex class. 72 /// This is a mutual exclusion object for synchronizing access to shared 73 /// memory areas for several threads. It is similar to the tthread::mutex class, 74 /// but instead of using system level functions, it is implemented as an atomic 75 /// spin lock with very low CPU overhead. 76 /// 77 /// The \c fast_mutex class is NOT compatible with the \c condition_variable 78 /// class (however, it IS compatible with the \c lock_guard class). It should 79 /// also be noted that the \c fast_mutex class typically does not provide 80 /// as accurate thread scheduling as a the standard \c mutex class does. 81 /// 82 /// Because of the limitations of the class, it should only be used in 83 /// situations where the mutex needs to be locked/unlocked very frequently. 84 /// 85 /// @note The "fast" version of this class relies on inline assembler language, 86 /// which is currently only supported for 32/64-bit Intel x86/AMD64 and 87 /// PowerPC architectures on a limited number of compilers (GNU g++ and MS 88 /// Visual C++). 89 /// For other architectures/compilers, system functions are used instead. 90 class fast_mutex { 91 public: 92 /// Constructor. 93 #if defined(_FAST_MUTEX_ASM_) fast_mutex()94 fast_mutex() : mLock(0) {} 95 #else 96 fast_mutex() 97 { 98 #if defined(_TTHREAD_WIN32_) 99 InitializeCriticalSection(&mHandle); 100 #elif defined(_TTHREAD_POSIX_) 101 pthread_mutex_init(&mHandle, NULL); 102 #endif 103 } 104 #endif 105 106 #if !defined(_FAST_MUTEX_ASM_) 107 /// Destructor. ~fast_mutex()108 ~fast_mutex() 109 { 110 #if defined(_TTHREAD_WIN32_) 111 DeleteCriticalSection(&mHandle); 112 #elif defined(_TTHREAD_POSIX_) 113 pthread_mutex_destroy(&mHandle); 114 #endif 115 } 116 #endif 117 118 /// Lock the mutex. 119 /// The method will block the calling thread until a lock on the mutex can 120 /// be obtained. The mutex remains locked until \c unlock() is called. 121 /// @see lock_guard lock()122 inline void lock() 123 { 124 #if defined(_FAST_MUTEX_ASM_) 125 bool gotLock; 126 do { 127 gotLock = try_lock(); 128 if(!gotLock) 129 { 130 #if defined(_TTHREAD_WIN32_) 131 Sleep(0); 132 #elif defined(_TTHREAD_POSIX_) 133 sched_yield(); 134 #endif 135 } 136 } while(!gotLock); 137 #else 138 #if defined(_TTHREAD_WIN32_) 139 EnterCriticalSection(&mHandle); 140 #elif defined(_TTHREAD_POSIX_) 141 pthread_mutex_lock(&mHandle); 142 #endif 143 #endif 144 } 145 146 /// Try to lock the mutex. 147 /// The method will try to lock the mutex. If it fails, the function will 148 /// return immediately (non-blocking). 149 /// @return \c true if the lock was acquired, or \c false if the lock could 150 /// not be acquired. try_lock()151 inline bool try_lock() 152 { 153 #if defined(_FAST_MUTEX_ASM_) 154 int oldLock; 155 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) 156 asm volatile ( 157 "movl $1,%%eax\n\t" // move 1 to eax 158 "xchg %%eax,%0\n\t" // try to set the lock bit 159 "movl %%eax,%1\n\t" // export our result to a test var 160 : "=m" (mLock), "=r" (oldLock) // use a register for output [used to be "=m" (oldLock)] 161 : "m" (mLock) // mLock is both input and output [used to be empty] 162 : "%eax", "memory" 163 ); 164 #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) 165 int *ptrLock = &mLock; 166 __asm { 167 mov eax,1 168 mov ecx,ptrLock 169 xchg eax,[ecx] 170 mov oldLock,eax 171 } 172 #elif defined(__GNUC__) && (defined(__ppc__)) 173 int newLock = 1; 174 asm volatile ( 175 "\n1:\n\t" 176 "lwarx %0,0,%1\n\t" 177 "cmpwi 0,%0,0\n\t" 178 "bne- 2f\n\t" 179 "stwcx. %2,0,%1\n\t" 180 "bne- 1b\n\t" 181 "isync\n" 182 "2:\n\t" 183 : "=&r" (oldLock) 184 : "r" (&mLock), "r" (newLock) 185 : "cr0", "memory" 186 ); 187 #endif 188 return (oldLock == 0); 189 #else 190 #if defined(_TTHREAD_WIN32_) 191 return TryEnterCriticalSection(&mHandle) ? true : false; 192 #elif defined(_TTHREAD_POSIX_) 193 return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; 194 #endif 195 #endif 196 } 197 198 /// Unlock the mutex. 199 /// If any threads are waiting for the lock on this mutex, one of them will 200 /// be unblocked. unlock()201 inline void unlock() 202 { 203 #if defined(_FAST_MUTEX_ASM_) 204 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) 205 asm volatile ( 206 "movl $0,%%eax\n\t" // move 1 to eax 207 "xchg %%eax,%0\n\t" // unset the lock bit 208 : "=m" (mLock) 209 : 210 : "%eax", "memory" 211 ); 212 #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) 213 int *ptrLock = &mLock; 214 __asm { 215 mov eax,0 216 mov ecx,ptrLock 217 xchg eax,[ecx] 218 } 219 #elif defined(__GNUC__) && (defined(__ppc__)) 220 asm volatile ( 221 "sync\n\t" // Replace with lwsync where possible? 222 : : : "memory" 223 ); 224 mLock = 0; 225 #endif 226 #else 227 #if defined(_TTHREAD_WIN32_) 228 LeaveCriticalSection(&mHandle); 229 #elif defined(_TTHREAD_POSIX_) 230 pthread_mutex_unlock(&mHandle); 231 #endif 232 #endif 233 } 234 235 private: 236 #if defined(_FAST_MUTEX_ASM_) 237 int mLock; 238 #else 239 #if defined(_TTHREAD_WIN32_) 240 CRITICAL_SECTION mHandle; 241 #elif defined(_TTHREAD_POSIX_) 242 pthread_mutex_t mHandle; 243 #endif 244 #endif 245 }; 246 247 } 248 249 #endif // _FAST_MUTEX_H_ 250 251