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