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 #if (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) || \ 42 (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || \ 43 (defined(__GNUC__) && (defined(__ppc__))) 44 #define _FAST_MUTEX_ASM_ 45 #else 46 #define _FAST_MUTEX_SYS_ 47 #endif 48 49 #if defined(_TTHREAD_WIN32_) 50 #ifndef WIN32_LEAN_AND_MEAN 51 #define WIN32_LEAN_AND_MEAN 52 #define __UNDEF_LEAN_AND_MEAN 53 #endif 54 #include <windows.h> 55 #ifdef __UNDEF_LEAN_AND_MEAN 56 #undef WIN32_LEAN_AND_MEAN 57 #undef __UNDEF_LEAN_AND_MEAN 58 #endif 59 #else 60 #ifdef _FAST_MUTEX_ASM_ 61 #include <sched.h> 62 #else 63 #include <pthread.h> 64 #endif 65 #endif 66 67 namespace tthread { 68 69 /// Fast mutex class. 70 /// This is a mutual exclusion object for synchronizing access to shared 71 /// memory areas for several threads. It is similar to the tthread::mutex class, 72 /// but instead of using system level functions, it is implemented as an atomic 73 /// spin lock with very low CPU overhead. 74 /// 75 /// The \c fast_mutex class is NOT compatible with the \c condition_variable 76 /// class (however, it IS compatible with the \c lock_guard class). It should 77 /// also be noted that the \c fast_mutex class typically does not provide 78 /// as accurate thread scheduling as a the standard \c mutex class does. 79 /// 80 /// Because of the limitations of the class, it should only be used in 81 /// situations where the mutex needs to be locked/unlocked very frequently. 82 /// 83 /// @note The "fast" version of this class relies on inline assembler language, 84 /// which is currently only supported for 32/64-bit Intel x86/AMD64 and 85 /// PowerPC architectures on a limited number of compilers (GNU g++ and MS 86 /// Visual C++). 87 /// For other architectures/compilers, system functions are used instead. 88 class fast_mutex { 89 public: 90 /// Constructor. 91 #if defined(_FAST_MUTEX_ASM_) fast_mutex()92 fast_mutex() : mLock(0) {} 93 #else 94 fast_mutex() 95 { 96 #if defined(_TTHREAD_WIN32_) 97 InitializeCriticalSection(&mHandle); 98 #elif defined(_TTHREAD_POSIX_) 99 pthread_mutex_init(&mHandle, NULL); 100 #endif 101 } 102 #endif 103 104 #if !defined(_FAST_MUTEX_ASM_) 105 /// Destructor. ~fast_mutex()106 ~fast_mutex() 107 { 108 #if defined(_TTHREAD_WIN32_) 109 DeleteCriticalSection(&mHandle); 110 #elif defined(_TTHREAD_POSIX_) 111 pthread_mutex_destroy(&mHandle); 112 #endif 113 } 114 #endif 115 116 /// Lock the mutex. 117 /// The method will block the calling thread until a lock on the mutex can 118 /// be obtained. The mutex remains locked until \c unlock() is called. 119 /// @see lock_guard lock()120 inline void lock() 121 { 122 #if defined(_FAST_MUTEX_ASM_) 123 bool gotLock; 124 do { 125 gotLock = try_lock(); 126 if(!gotLock) 127 { 128 #if defined(_TTHREAD_WIN32_) 129 Sleep(0); 130 #elif defined(_TTHREAD_POSIX_) 131 sched_yield(); 132 #endif 133 } 134 } while(!gotLock); 135 #else 136 #if defined(_TTHREAD_WIN32_) 137 EnterCriticalSection(&mHandle); 138 #elif defined(_TTHREAD_POSIX_) 139 pthread_mutex_lock(&mHandle); 140 #endif 141 #endif 142 } 143 144 /// Try to lock the mutex. 145 /// The method will try to lock the mutex. If it fails, the function will 146 /// return immediately (non-blocking). 147 /// @return \c true if the lock was acquired, or \c false if the lock could 148 /// not be acquired. try_lock()149 inline bool try_lock() 150 { 151 #if defined(_FAST_MUTEX_ASM_) 152 int oldLock; 153 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) 154 asm volatile ( 155 "movl $1,%%eax\n\t" 156 "xchg %%eax,%0\n\t" 157 "movl %%eax,%1\n\t" 158 : "=m" (mLock), "=m" (oldLock) 159 : 160 : "%eax", "memory" 161 ); 162 #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) 163 int *ptrLock = &mLock; 164 __asm { 165 mov eax,1 166 mov ecx,ptrLock 167 xchg eax,[ecx] 168 mov oldLock,eax 169 } 170 #elif defined(__GNUC__) && (defined(__ppc__)) 171 int newLock = 1; 172 asm volatile ( 173 "\n1:\n\t" 174 "lwarx %0,0,%1\n\t" 175 "cmpwi 0,%0,0\n\t" 176 "bne- 2f\n\t" 177 "stwcx. %2,0,%1\n\t" 178 "bne- 1b\n\t" 179 "isync\n" 180 "2:\n\t" 181 : "=&r" (oldLock) 182 : "r" (&mLock), "r" (newLock) 183 : "cr0", "memory" 184 ); 185 #endif 186 return (oldLock == 0); 187 #else 188 #if defined(_TTHREAD_WIN32_) 189 return TryEnterCriticalSection(&mHandle) ? true : false; 190 #elif defined(_TTHREAD_POSIX_) 191 return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; 192 #endif 193 #endif 194 } 195 196 /// Unlock the mutex. 197 /// If any threads are waiting for the lock on this mutex, one of them will 198 /// be unblocked. unlock()199 inline void unlock() 200 { 201 #if defined(_FAST_MUTEX_ASM_) 202 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) 203 asm volatile ( 204 "movl $0,%%eax\n\t" 205 "xchg %%eax,%0\n\t" 206 : "=m" (mLock) 207 : 208 : "%eax", "memory" 209 ); 210 #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) 211 int *ptrLock = &mLock; 212 __asm { 213 mov eax,0 214 mov ecx,ptrLock 215 xchg eax,[ecx] 216 } 217 #elif defined(__GNUC__) && (defined(__ppc__)) 218 asm volatile ( 219 "sync\n\t" // Replace with lwsync where possible? 220 : : : "memory" 221 ); 222 mLock = 0; 223 #endif 224 #else 225 #if defined(_TTHREAD_WIN32_) 226 LeaveCriticalSection(&mHandle); 227 #elif defined(_TTHREAD_POSIX_) 228 pthread_mutex_unlock(&mHandle); 229 #endif 230 #endif 231 } 232 233 private: 234 #if defined(_FAST_MUTEX_ASM_) 235 int mLock; 236 #else 237 #if defined(_TTHREAD_WIN32_) 238 CRITICAL_SECTION mHandle; 239 #elif defined(_TTHREAD_POSIX_) 240 pthread_mutex_t mHandle; 241 #endif 242 #endif 243 }; 244 245 } 246 247 #endif // _FAST_MUTEX_H_ 248 249