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