/* 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 #pragma warning(push) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #include #pragma warning(pop) #pragma GCC diagnostic pop namespace dev { using Mutex = std::mutex; using RecursiveMutex = std::recursive_mutex; using SharedMutex = boost::shared_mutex; using Guard = std::lock_guard; using UniqueGuard = std::unique_lock; 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; template class Notified { public: Notified() {} Notified(N const& _v): m_value(_v) {} Notified(Notified const&) = delete; Notified& operator=(N const& _v) { UniqueGuard l(m_mutex); m_value = _v; m_cv.notify_all(); return *this; } operator N() const { UniqueGuard l(m_mutex); return m_value; } void wait() const { N old; { UniqueGuard l(m_mutex); old = m_value; } waitNot(old); } void wait(N const& _v) const { UniqueGuard l(m_mutex); m_cv.wait(l, [&](){return m_value == _v;}); } void waitNot(N const& _v) const { UniqueGuard l(m_mutex); m_cv.wait(l, [&](){return m_value != _v;}); } template void wait(F const& _f) const { UniqueGuard l(m_mutex); m_cv.wait(l, _f); } template void wait(std::chrono::duration _d) const { N old; { UniqueGuard l(m_mutex); old = m_value; } waitNot(_d, old); } template void wait(std::chrono::duration _d, N const& _v) const { UniqueGuard l(m_mutex); m_cv.wait_for(l, _d, [&](){return m_value == _v;}); } template void waitNot(std::chrono::duration _d, N const& _v) const { UniqueGuard l(m_mutex); m_cv.wait_for(l, _d, [&](){return m_value != _v;}); } template void wait(std::chrono::duration _d, F const& _f) const { UniqueGuard l(m_mutex); m_cv.wait_for(l, _d, _f); } private: mutable Mutex m_mutex; mutable std::condition_variable m_cv; N m_value; }; /** @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) }