1 // -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 // vim:tabstop=4:shiftwidth=4:expandtab:
3 
4 /*
5  * Copyright (C) 2004-2015 Wu Yongwei <adah at users dot sourceforge dot net>
6  *
7  * This software is provided 'as-is', without any express or implied
8  * warranty.  In no event will the authors be held liable for any
9  * damages arising from the use of this software.
10  *
11  * Permission is granted to anyone to use this software for any purpose,
12  * including commercial applications, and to alter it and redistribute
13  * it freely, subject to the following restrictions:
14  *
15  * 1. The origin of this software must not be misrepresented; you must
16  *    not claim that you wrote the original software.  If you use this
17  *    software in a product, an acknowledgement in the product
18  *    documentation would be appreciated but is not required.
19  * 2. Altered source versions must be plainly marked as such, and must
20  *    not be misrepresented as being the original software.
21  * 3. This notice may not be removed or altered from any source
22  *    distribution.
23  *
24  * This file is part of Stones of Nvwa:
25  *      http://sourceforge.net/projects/nvwa
26  *
27  */
28 
29 /**
30  * @file  fast_mutex.h
31  *
32  * A fast mutex implementation for POSIX, Win32, and modern C++.
33  *
34  * @date  2015-05-19
35  */
36 
37 #ifndef NVWA_FAST_MUTEX_H
38 #define NVWA_FAST_MUTEX_H
39 
40 #include "debug/nvwa/_nvwa.h"              // NVWA_NAMESPACE_*
41 #include "debug/nvwa/c++11.h"              // HAVE_CXX11_MUTEX
42 
43 # if !defined(_NOTHREADS)
44 #   if !defined(NVWA_USE_CXX11_MUTEX) && HAVE_CXX11_MUTEX != 0 && \
45             !defined(_WIN32THREADS) && !defined(NVWA_WIN32THREADS) && \
46             !defined(NVWA_PTHREADS) && !defined(NVWA_NOTHREADS) && \
47             defined(_WIN32) && defined(_MT) && \
48             (!defined(_MSC_VER) || defined(_DLL))
49 //      Prefer using std::mutex on Windows to avoid the namespace
50 //      pollution caused by <windows.h>.  However, MSVC has a re-entry
51 //      issue with its std::mutex implementation, and std::mutex should
52 //      not be used unless /MD or /MDd is used.  For more information,
53 //      check out:
54 //
55 //        https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag
56 //        http://stackoverflow.com/questions/14319344/stdmutex-lock-hangs-when-overriding-the-new-operator
57 //
58 #       define NVWA_USE_CXX11_MUTEX 1
59 #   endif
60 
61 #   if !defined(_WIN32THREADS) && \
62             (defined(_WIN32) && defined(_MT))
63 //      Automatically use _WIN32THREADS when specifying -MT/-MD in MSVC,
64 //      or -mthreads in MinGW GCC.
65 #       define _WIN32THREADS
66 #   elif !defined(_PTHREADS) && \
67             defined(_REENTRANT)
68 //      Automatically use _PTHREADS when specifying -pthread in GCC or Clang.
69 #       define _PTHREADS
70 #   endif
71 # endif
72 
73 # ifndef NVWA_USE_CXX11_MUTEX
74 #   if HAVE_CXX11_MUTEX != 0 && \
75             !defined(_NOTHREADS)    && !defined(NVWA_NOTHREADS) && \
76             !defined(_PTHREADS)     && !defined(NVWA_PTHREADS) && \
77             !defined(_WIN32THREADS) && !defined(NVWA_WIN32THREADS)
78 #       define NVWA_USE_CXX11_MUTEX 1
79 #   else
80 #       define NVWA_USE_CXX11_MUTEX 0
81 #   endif
82 # endif
83 
84 # if !defined(_PTHREADS) && !defined(_WIN32THREADS) && \
85         !defined(_NOTHREADS) && NVWA_USE_CXX11_MUTEX == 0
86 #   define _NOTHREADS
87 # endif
88 
89 # if defined(_NOTHREADS)
90 #   if defined(_PTHREADS) || defined(_WIN32THREADS) || \
91             NVWA_USE_CXX11_MUTEX != 0
92 #       undef _NOTHREADS
93 #       error "Cannot define multi-threaded mode with -D_NOTHREADS"
94 #   endif
95 # endif
96 
97 # if defined(__MINGW32__) && defined(_WIN32THREADS) && !defined(_MT)
98 #   error "Be sure to specify -mthreads with -D_WIN32THREADS"
99 # endif
100 
101 // With all the heuristics above, things may still go wrong, maybe even
102 // due to a specific inclusion order.  So they may be overridden by
103 // manually defining the NVWA_* macros below.
104 # if NVWA_USE_CXX11_MUTEX == 0 && \
105         !defined(NVWA_WIN32THREADS) && \
106         !defined(NVWA_PTHREADS) && \
107         !defined(NVWA_NOTHREADS)
108 //  _WIN32THREADS takes precedence, as some C++ libraries have _PTHREADS
109 //  defined even on Win32 platforms.
110 #   if defined(_WIN32THREADS)
111 #       define NVWA_WIN32THREADS
112 #   elif defined(_PTHREADS)
113 #       define NVWA_PTHREADS
114 #   else
115 #       define NVWA_NOTHREADS
116 #   endif
117 # endif
118 
119 # ifndef _FAST_MUTEX_CHECK_INITIALIZATION
120 /**
121  * Macro to control whether to check for initialization status for each
122  * lock/unlock operation.  Defining it to a non-zero value will enable
123  * the check, so that the construction/destruction of a static object
124  * using a static fast_mutex not yet constructed or already destroyed
125  * will work (with lock/unlock operations ignored).  Defining it to zero
126  * will disable to check.
127  */
128 #   define _FAST_MUTEX_CHECK_INITIALIZATION 1
129 # endif
130 
131 # ifdef _DEBUG
132 #   include <stdio.h>
133 #   include <stdlib.h>
134 /** Macro for fast_mutex assertions.  Real version (for debug mode). */
135 #   define _FAST_MUTEX_ASSERT(_Expr, _Msg) \
136         if (!(_Expr)) { \
137             fprintf(stderr, "fast_mutex::%s\n", _Msg); \
138             abort(); \
139         }
140 # else
141 /** Macro for fast_mutex assertions.  Fake version (for release mode). */
142 #   define _FAST_MUTEX_ASSERT(_Expr, _Msg) \
143         ((void)0)
144 # endif
145 
146 # if NVWA_USE_CXX11_MUTEX != 0
147 #   include <mutex>
148 NVWA_NAMESPACE_BEGIN
149 /**
150  * Macro alias to `volatile' semantics.  Here it is truly volatile since
151  * it is in a multi-threaded (C++11) environment.
152  */
153 #   define __VOLATILE volatile
154     /**
155      * Class for non-reentrant fast mutexes.  This is the implementation
156      * using the C++11 mutex.
157      */
158     class fast_mutex
159     {
160         std::mutex _M_mtx_impl;
161 #       if _FAST_MUTEX_CHECK_INITIALIZATION
162         bool _M_initialized;
163 #       endif
164 #       ifdef _DEBUG
165         bool _M_locked;
166 #       endif
167     public:
fast_mutex()168         fast_mutex()
169 #       ifdef _DEBUG
170             : _M_locked(false)
171 #       endif
172         {
173 #       if _FAST_MUTEX_CHECK_INITIALIZATION
174             _M_initialized = true;
175 #       endif
176         }
~fast_mutex()177         ~fast_mutex()
178         {
179             _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
180 #       if _FAST_MUTEX_CHECK_INITIALIZATION
181             _M_initialized = false;
182 #       endif
183         }
lock()184         void lock()
185         {
186 #       if _FAST_MUTEX_CHECK_INITIALIZATION
187             if (!_M_initialized)
188                 return;
189 #       endif
190             _M_mtx_impl.lock();
191 #       ifdef _DEBUG
192             _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
193             _M_locked = true;
194 #       endif
195         }
unlock()196         void unlock()
197         {
198 #       if _FAST_MUTEX_CHECK_INITIALIZATION
199             if (!_M_initialized)
200                 return;
201 #       endif
202 #       ifdef _DEBUG
203             _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
204             _M_locked = false;
205 #       endif
206             _M_mtx_impl.unlock();
207         }
208     private:
209         fast_mutex(const fast_mutex&);
210         fast_mutex& operator=(const fast_mutex&);
211     };
212 NVWA_NAMESPACE_END
213 # elif defined(NVWA_PTHREADS)
214 #   include <pthread.h>
215 NVWA_NAMESPACE_BEGIN
216 /**
217  * Macro alias to `volatile' semantics.  Here it is truly volatile since
218  * it is in a multi-threaded (POSIX threads) environment.
219  */
220 #   define __VOLATILE volatile
221     /**
222      * Class for non-reentrant fast mutexes.  This is the implementation
223      * for POSIX threads.
224      */
225     class fast_mutex
226     {
227         pthread_mutex_t _M_mtx_impl;
228 #       if _FAST_MUTEX_CHECK_INITIALIZATION
229         bool _M_initialized;
230 #       endif
231 #       ifdef _DEBUG
232         bool _M_locked;
233 #       endif
234     public:
235         fast_mutex()
236 #       ifdef _DEBUG
237             : _M_locked(false)
238 #       endif
239         {
240             ::pthread_mutex_init(&_M_mtx_impl, _NULLPTR);
241 #       if _FAST_MUTEX_CHECK_INITIALIZATION
242             _M_initialized = true;
243 #       endif
244         }
245         ~fast_mutex()
246         {
247             _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
248 #       if _FAST_MUTEX_CHECK_INITIALIZATION
249             _M_initialized = false;
250 #       endif
251             ::pthread_mutex_destroy(&_M_mtx_impl);
252         }
253         void lock()
254         {
255 #       if _FAST_MUTEX_CHECK_INITIALIZATION
256             if (!_M_initialized)
257                 return;
258 #       endif
259             ::pthread_mutex_lock(&_M_mtx_impl);
260 #       ifdef _DEBUG
261             // The following assertion should _always_ be true for a
262             // real `fast' pthread_mutex.  However, this assertion can
263             // help sometimes, when people forget to use `-lpthread' and
264             // glibc provides an empty implementation.  Having this
265             // assertion is also more consistent.
266             _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
267             _M_locked = true;
268 #       endif
269         }
270         void unlock()
271         {
272 #       if _FAST_MUTEX_CHECK_INITIALIZATION
273             if (!_M_initialized)
274                 return;
275 #       endif
276 #       ifdef _DEBUG
277             _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
278             _M_locked = false;
279 #       endif
280             ::pthread_mutex_unlock(&_M_mtx_impl);
281         }
282     private:
283         fast_mutex(const fast_mutex&);
284         fast_mutex& operator=(const fast_mutex&);
285     };
286 NVWA_NAMESPACE_END
287 # elif defined(NVWA_WIN32THREADS)
288 #   ifndef WIN32_LEAN_AND_MEAN
289 #     define WIN32_LEAN_AND_MEAN
290 #   endif /* WIN32_LEAN_AND_MEAN */
291 #   include <windows.h>
292 NVWA_NAMESPACE_BEGIN
293 /**
294  * Macro alias to `volatile' semantics.  Here it is truly volatile since
295  * it is in a multi-threaded (Win32 threads) environment.
296  */
297 #   define __VOLATILE volatile
298     /**
299      * Class for non-reentrant fast mutexes.  This is the implementation
300      * for Win32 threads.
301      */
302     class fast_mutex
303     {
304         CRITICAL_SECTION _M_mtx_impl;
305 #       if _FAST_MUTEX_CHECK_INITIALIZATION
306         bool _M_initialized;
307 #       endif
308 #       ifdef _DEBUG
309         bool _M_locked;
310 #       endif
311     public:
312         fast_mutex()
313 #       ifdef _DEBUG
314             : _M_locked(false)
315 #       endif
316         {
317             ::InitializeCriticalSection(&_M_mtx_impl);
318 #       if _FAST_MUTEX_CHECK_INITIALIZATION
319             _M_initialized = true;
320 #       endif
321         }
322         ~fast_mutex()
323         {
324             _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
325 #       if _FAST_MUTEX_CHECK_INITIALIZATION
326             _M_initialized = false;
327 #       endif
328             ::DeleteCriticalSection(&_M_mtx_impl);
329         }
330         void lock()
331         {
332 #       if _FAST_MUTEX_CHECK_INITIALIZATION
333             if (!_M_initialized)
334                 return;
335 #       endif
336             ::EnterCriticalSection(&_M_mtx_impl);
337 #       ifdef _DEBUG
338             _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
339             _M_locked = true;
340 #       endif
341         }
342         void unlock()
343         {
344 #       if _FAST_MUTEX_CHECK_INITIALIZATION
345             if (!_M_initialized)
346                 return;
347 #       endif
348 #       ifdef _DEBUG
349             _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
350             _M_locked = false;
351 #       endif
352             ::LeaveCriticalSection(&_M_mtx_impl);
353         }
354     private:
355         fast_mutex(const fast_mutex&);
356         fast_mutex& operator=(const fast_mutex&);
357     };
358 NVWA_NAMESPACE_END
359 # elif defined(NVWA_NOTHREADS)
360 NVWA_NAMESPACE_BEGIN
361 /**
362  * Macro alias to `volatile' semantics.  Here it is not truly volatile
363  * since it is in a single-threaded environment.
364  */
365 #   define __VOLATILE
366     /**
367      * Class for non-reentrant fast mutexes.  This is the null
368      * implementation for single-threaded environments.
369      */
370     class fast_mutex
371     {
372 #       ifdef _DEBUG
373         bool _M_locked;
374 #       endif
375     public:
376         fast_mutex()
377 #       ifdef _DEBUG
378             : _M_locked(false)
379 #       endif
380         {
381         }
382         ~fast_mutex()
383         {
384             _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
385         }
386         void lock()
387         {
388 #       ifdef _DEBUG
389             _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
390             _M_locked = true;
391 #       endif
392         }
393         void unlock()
394         {
395 #       ifdef _DEBUG
396             _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
397             _M_locked = false;
398 #       endif
399         }
400     private:
401         fast_mutex(const fast_mutex&);
402         fast_mutex& operator=(const fast_mutex&);
403     };
404 NVWA_NAMESPACE_END
405 # endif // Definition of class fast_mutex
406 
407 NVWA_NAMESPACE_BEGIN
408 /** RAII lock class for fast_mutex. */
409 class fast_mutex_autolock
410 {
411     fast_mutex& _M_mtx;
412 public:
fast_mutex_autolock(fast_mutex & mtx)413     explicit fast_mutex_autolock(fast_mutex& mtx) : _M_mtx(mtx)
414     {
415         _M_mtx.lock();
416     }
~fast_mutex_autolock()417     ~fast_mutex_autolock()
418     {
419         _M_mtx.unlock();
420     }
421 private:
422     fast_mutex_autolock(const fast_mutex_autolock&);
423     fast_mutex_autolock& operator=(const fast_mutex_autolock&);
424 };
425 NVWA_NAMESPACE_END
426 
427 #endif // NVWA_FAST_MUTEX_H
428