// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- // vim:tabstop=4:shiftwidth=4:expandtab: /* * Copyright (C) 2004-2015 Wu Yongwei * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any * damages arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute * it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. If you use this * software in a product, an acknowledgement in the product * documentation would be appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * 3. This notice may not be removed or altered from any source * distribution. * * This file is part of Stones of Nvwa: * http://sourceforge.net/projects/nvwa * */ /** * @file fast_mutex.h * * A fast mutex implementation for POSIX, Win32, and modern C++. * * @date 2015-05-19 */ #ifndef NVWA_FAST_MUTEX_H #define NVWA_FAST_MUTEX_H #include "debug/nvwa/_nvwa.h" // NVWA_NAMESPACE_* #include "debug/nvwa/c++11.h" // HAVE_CXX11_MUTEX # if !defined(_NOTHREADS) # if !defined(NVWA_USE_CXX11_MUTEX) && HAVE_CXX11_MUTEX != 0 && \ !defined(_WIN32THREADS) && !defined(NVWA_WIN32THREADS) && \ !defined(NVWA_PTHREADS) && !defined(NVWA_NOTHREADS) && \ defined(_WIN32) && defined(_MT) && \ (!defined(_MSC_VER) || defined(_DLL)) // Prefer using std::mutex on Windows to avoid the namespace // pollution caused by . However, MSVC has a re-entry // issue with its std::mutex implementation, and std::mutex should // not be used unless /MD or /MDd is used. For more information, // check out: // // https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag // http://stackoverflow.com/questions/14319344/stdmutex-lock-hangs-when-overriding-the-new-operator // # define NVWA_USE_CXX11_MUTEX 1 # endif # if !defined(_WIN32THREADS) && \ (defined(_WIN32) && defined(_MT)) // Automatically use _WIN32THREADS when specifying -MT/-MD in MSVC, // or -mthreads in MinGW GCC. # define _WIN32THREADS # elif !defined(_PTHREADS) && \ defined(_REENTRANT) // Automatically use _PTHREADS when specifying -pthread in GCC or Clang. # define _PTHREADS # endif # endif # ifndef NVWA_USE_CXX11_MUTEX # if HAVE_CXX11_MUTEX != 0 && \ !defined(_NOTHREADS) && !defined(NVWA_NOTHREADS) && \ !defined(_PTHREADS) && !defined(NVWA_PTHREADS) && \ !defined(_WIN32THREADS) && !defined(NVWA_WIN32THREADS) # define NVWA_USE_CXX11_MUTEX 1 # else # define NVWA_USE_CXX11_MUTEX 0 # endif # endif # if !defined(_PTHREADS) && !defined(_WIN32THREADS) && \ !defined(_NOTHREADS) && NVWA_USE_CXX11_MUTEX == 0 # define _NOTHREADS # endif # if defined(_NOTHREADS) # if defined(_PTHREADS) || defined(_WIN32THREADS) || \ NVWA_USE_CXX11_MUTEX != 0 # undef _NOTHREADS # error "Cannot define multi-threaded mode with -D_NOTHREADS" # endif # endif # if defined(__MINGW32__) && defined(_WIN32THREADS) && !defined(_MT) # error "Be sure to specify -mthreads with -D_WIN32THREADS" # endif // With all the heuristics above, things may still go wrong, maybe even // due to a specific inclusion order. So they may be overridden by // manually defining the NVWA_* macros below. # if NVWA_USE_CXX11_MUTEX == 0 && \ !defined(NVWA_WIN32THREADS) && \ !defined(NVWA_PTHREADS) && \ !defined(NVWA_NOTHREADS) // _WIN32THREADS takes precedence, as some C++ libraries have _PTHREADS // defined even on Win32 platforms. # if defined(_WIN32THREADS) # define NVWA_WIN32THREADS # elif defined(_PTHREADS) # define NVWA_PTHREADS # else # define NVWA_NOTHREADS # endif # endif # ifndef _FAST_MUTEX_CHECK_INITIALIZATION /** * Macro to control whether to check for initialization status for each * lock/unlock operation. Defining it to a non-zero value will enable * the check, so that the construction/destruction of a static object * using a static fast_mutex not yet constructed or already destroyed * will work (with lock/unlock operations ignored). Defining it to zero * will disable to check. */ # define _FAST_MUTEX_CHECK_INITIALIZATION 1 # endif # ifdef _DEBUG # include # include /** Macro for fast_mutex assertions. Real version (for debug mode). */ # define _FAST_MUTEX_ASSERT(_Expr, _Msg) \ if (!(_Expr)) { \ fprintf(stderr, "fast_mutex::%s\n", _Msg); \ abort(); \ } # else /** Macro for fast_mutex assertions. Fake version (for release mode). */ # define _FAST_MUTEX_ASSERT(_Expr, _Msg) \ ((void)0) # endif # if NVWA_USE_CXX11_MUTEX != 0 # include NVWA_NAMESPACE_BEGIN /** * Macro alias to `volatile' semantics. Here it is truly volatile since * it is in a multi-threaded (C++11) environment. */ # define __VOLATILE volatile /** * Class for non-reentrant fast mutexes. This is the implementation * using the C++11 mutex. */ class fast_mutex { std::mutex _M_mtx_impl; # if _FAST_MUTEX_CHECK_INITIALIZATION bool _M_initialized; # endif # ifdef _DEBUG bool _M_locked; # endif public: fast_mutex() # ifdef _DEBUG : _M_locked(false) # endif { # if _FAST_MUTEX_CHECK_INITIALIZATION _M_initialized = true; # endif } ~fast_mutex() { _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); # if _FAST_MUTEX_CHECK_INITIALIZATION _M_initialized = false; # endif } void lock() { # if _FAST_MUTEX_CHECK_INITIALIZATION if (!_M_initialized) return; # endif _M_mtx_impl.lock(); # ifdef _DEBUG _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); _M_locked = true; # endif } void unlock() { # if _FAST_MUTEX_CHECK_INITIALIZATION if (!_M_initialized) return; # endif # ifdef _DEBUG _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); _M_locked = false; # endif _M_mtx_impl.unlock(); } private: fast_mutex(const fast_mutex&); fast_mutex& operator=(const fast_mutex&); }; NVWA_NAMESPACE_END # elif defined(NVWA_PTHREADS) # include NVWA_NAMESPACE_BEGIN /** * Macro alias to `volatile' semantics. Here it is truly volatile since * it is in a multi-threaded (POSIX threads) environment. */ # define __VOLATILE volatile /** * Class for non-reentrant fast mutexes. This is the implementation * for POSIX threads. */ class fast_mutex { pthread_mutex_t _M_mtx_impl; # if _FAST_MUTEX_CHECK_INITIALIZATION bool _M_initialized; # endif # ifdef _DEBUG bool _M_locked; # endif public: fast_mutex() # ifdef _DEBUG : _M_locked(false) # endif { ::pthread_mutex_init(&_M_mtx_impl, _NULLPTR); # if _FAST_MUTEX_CHECK_INITIALIZATION _M_initialized = true; # endif } ~fast_mutex() { _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); # if _FAST_MUTEX_CHECK_INITIALIZATION _M_initialized = false; # endif ::pthread_mutex_destroy(&_M_mtx_impl); } void lock() { # if _FAST_MUTEX_CHECK_INITIALIZATION if (!_M_initialized) return; # endif ::pthread_mutex_lock(&_M_mtx_impl); # ifdef _DEBUG // The following assertion should _always_ be true for a // real `fast' pthread_mutex. However, this assertion can // help sometimes, when people forget to use `-lpthread' and // glibc provides an empty implementation. Having this // assertion is also more consistent. _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); _M_locked = true; # endif } void unlock() { # if _FAST_MUTEX_CHECK_INITIALIZATION if (!_M_initialized) return; # endif # ifdef _DEBUG _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); _M_locked = false; # endif ::pthread_mutex_unlock(&_M_mtx_impl); } private: fast_mutex(const fast_mutex&); fast_mutex& operator=(const fast_mutex&); }; NVWA_NAMESPACE_END # elif defined(NVWA_WIN32THREADS) # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif /* WIN32_LEAN_AND_MEAN */ # include NVWA_NAMESPACE_BEGIN /** * Macro alias to `volatile' semantics. Here it is truly volatile since * it is in a multi-threaded (Win32 threads) environment. */ # define __VOLATILE volatile /** * Class for non-reentrant fast mutexes. This is the implementation * for Win32 threads. */ class fast_mutex { CRITICAL_SECTION _M_mtx_impl; # if _FAST_MUTEX_CHECK_INITIALIZATION bool _M_initialized; # endif # ifdef _DEBUG bool _M_locked; # endif public: fast_mutex() # ifdef _DEBUG : _M_locked(false) # endif { ::InitializeCriticalSection(&_M_mtx_impl); # if _FAST_MUTEX_CHECK_INITIALIZATION _M_initialized = true; # endif } ~fast_mutex() { _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); # if _FAST_MUTEX_CHECK_INITIALIZATION _M_initialized = false; # endif ::DeleteCriticalSection(&_M_mtx_impl); } void lock() { # if _FAST_MUTEX_CHECK_INITIALIZATION if (!_M_initialized) return; # endif ::EnterCriticalSection(&_M_mtx_impl); # ifdef _DEBUG _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); _M_locked = true; # endif } void unlock() { # if _FAST_MUTEX_CHECK_INITIALIZATION if (!_M_initialized) return; # endif # ifdef _DEBUG _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); _M_locked = false; # endif ::LeaveCriticalSection(&_M_mtx_impl); } private: fast_mutex(const fast_mutex&); fast_mutex& operator=(const fast_mutex&); }; NVWA_NAMESPACE_END # elif defined(NVWA_NOTHREADS) NVWA_NAMESPACE_BEGIN /** * Macro alias to `volatile' semantics. Here it is not truly volatile * since it is in a single-threaded environment. */ # define __VOLATILE /** * Class for non-reentrant fast mutexes. This is the null * implementation for single-threaded environments. */ class fast_mutex { # ifdef _DEBUG bool _M_locked; # endif public: fast_mutex() # ifdef _DEBUG : _M_locked(false) # endif { } ~fast_mutex() { _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); } void lock() { # ifdef _DEBUG _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); _M_locked = true; # endif } void unlock() { # ifdef _DEBUG _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); _M_locked = false; # endif } private: fast_mutex(const fast_mutex&); fast_mutex& operator=(const fast_mutex&); }; NVWA_NAMESPACE_END # endif // Definition of class fast_mutex NVWA_NAMESPACE_BEGIN /** RAII lock class for fast_mutex. */ class fast_mutex_autolock { fast_mutex& _M_mtx; public: explicit fast_mutex_autolock(fast_mutex& mtx) : _M_mtx(mtx) { _M_mtx.lock(); } ~fast_mutex_autolock() { _M_mtx.unlock(); } private: fast_mutex_autolock(const fast_mutex_autolock&); fast_mutex_autolock& operator=(const fast_mutex_autolock&); }; NVWA_NAMESPACE_END #endif // NVWA_FAST_MUTEX_H