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