/* This file is part of cpp-ethereum. cpp-ethereum is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. cpp-ethereum is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ /** @file Guards.h * @author Gav Wood * @date 2014 */ #pragma once #include #include #include namespace dev { using Mutex = std::mutex; using RecursiveMutex = std::recursive_mutex; using SharedMutex = boost::shared_mutex; using Guard = std::lock_guard; using RecursiveGuard = std::lock_guard; using ReadGuard = boost::shared_lock; using UpgradableGuard = boost::upgrade_lock; using UpgradeGuard = boost::upgrade_to_unique_lock; using WriteGuard = boost::unique_lock; template struct GenericGuardBool: GuardType { GenericGuardBool(MutexType& _m): GuardType(_m) {} bool b = true; }; template struct GenericUnguardBool { GenericUnguardBool(MutexType& _m): m(_m) { m.unlock(); } ~GenericUnguardBool() { m.lock(); } bool b = true; MutexType& m; }; template struct GenericUnguardSharedBool { GenericUnguardSharedBool(MutexType& _m): m(_m) { m.unlock_shared(); } ~GenericUnguardSharedBool() { m.lock_shared(); } bool b = true; MutexType& m; }; /** @brief Simple lock that waits for release without making context switch */ class SpinLock { public: SpinLock() { m_lock.clear(); } void lock() { while (m_lock.test_and_set(std::memory_order_acquire)) {} } void unlock() { m_lock.clear(std::memory_order_release); } private: std::atomic_flag m_lock; }; using SpinGuard = std::lock_guard; /** @brief Simple block guard. * The expression/block following is guarded though the given mutex. * Usage: * @code * Mutex m; * unsigned d; * ... * ETH_(m) d = 1; * ... * ETH_(m) { for (auto d = 10; d > 0; --d) foo(d); d = 0; } * @endcode * * There are several variants of this basic mechanism for different Mutex types and Guards. * * There is also the UNGUARD variant which allows an unguarded expression/block to exist within a * guarded expression. eg: * * @code * Mutex m; * int d; * ... * ETH_GUARDED(m) * { * for (auto d = 50; d > 25; --d) * foo(d); * ETH_UNGUARDED(m) * bar(); * for (; d > 0; --d) * foo(d); * } * @endcode */ #define DEV_GUARDED(MUTEX) \ for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) #define DEV_READ_GUARDED(MUTEX) \ for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) #define DEV_WRITE_GUARDED(MUTEX) \ for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) #define DEV_RECURSIVE_GUARDED(MUTEX) \ for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) #define DEV_UNGUARDED(MUTEX) \ for (GenericUnguardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) #define DEV_READ_UNGUARDED(MUTEX) \ for (GenericUnguardSharedBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) #define DEV_WRITE_UNGUARDED(MUTEX) \ for (GenericUnguardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) }