Guards.h 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /*
  2. This file is part of cpp-ethereum.
  3. cpp-ethereum is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. cpp-ethereum is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. /** @file Guards.h
  15. * @author Gav Wood <i@gavwood.com>
  16. * @date 2014
  17. */
  18. #pragma once
  19. #include <mutex>
  20. #include <condition_variable>
  21. #include <atomic>
  22. #pragma warning(push)
  23. #pragma GCC diagnostic push
  24. #pragma GCC diagnostic ignored "-Wunused-parameter"
  25. #include <boost/thread.hpp>
  26. #pragma warning(pop)
  27. #pragma GCC diagnostic pop
  28. namespace dev
  29. {
  30. using Mutex = std::mutex;
  31. using RecursiveMutex = std::recursive_mutex;
  32. using SharedMutex = boost::shared_mutex;
  33. using Guard = std::lock_guard<std::mutex>;
  34. using UniqueGuard = std::unique_lock<std::mutex>;
  35. using RecursiveGuard = std::lock_guard<std::recursive_mutex>;
  36. using ReadGuard = boost::shared_lock<boost::shared_mutex>;
  37. using UpgradableGuard = boost::upgrade_lock<boost::shared_mutex>;
  38. using UpgradeGuard = boost::upgrade_to_unique_lock<boost::shared_mutex>;
  39. using WriteGuard = boost::unique_lock<boost::shared_mutex>;
  40. template <class GuardType, class MutexType>
  41. struct GenericGuardBool: GuardType
  42. {
  43. GenericGuardBool(MutexType& _m): GuardType(_m) {}
  44. bool b = true;
  45. };
  46. template <class MutexType>
  47. struct GenericUnguardBool
  48. {
  49. GenericUnguardBool(MutexType& _m): m(_m) { m.unlock(); }
  50. ~GenericUnguardBool() { m.lock(); }
  51. bool b = true;
  52. MutexType& m;
  53. };
  54. template <class MutexType>
  55. struct GenericUnguardSharedBool
  56. {
  57. GenericUnguardSharedBool(MutexType& _m): m(_m) { m.unlock_shared(); }
  58. ~GenericUnguardSharedBool() { m.lock_shared(); }
  59. bool b = true;
  60. MutexType& m;
  61. };
  62. /** @brief Simple lock that waits for release without making context switch */
  63. class SpinLock
  64. {
  65. public:
  66. SpinLock() { m_lock.clear(); }
  67. void lock() { while (m_lock.test_and_set(std::memory_order_acquire)) {} }
  68. void unlock() { m_lock.clear(std::memory_order_release); }
  69. private:
  70. std::atomic_flag m_lock;
  71. };
  72. using SpinGuard = std::lock_guard<SpinLock>;
  73. template <class N>
  74. class Notified
  75. {
  76. public:
  77. Notified() {}
  78. Notified(N const& _v): m_value(_v) {}
  79. Notified(Notified const&) = delete;
  80. Notified& operator=(N const& _v) { UniqueGuard l(m_mutex); m_value = _v; m_cv.notify_all(); return *this; }
  81. operator N() const { UniqueGuard l(m_mutex); return m_value; }
  82. void wait() const { N old; { UniqueGuard l(m_mutex); old = m_value; } waitNot(old); }
  83. void wait(N const& _v) const { UniqueGuard l(m_mutex); m_cv.wait(l, [&](){return m_value == _v;}); }
  84. void waitNot(N const& _v) const { UniqueGuard l(m_mutex); m_cv.wait(l, [&](){return m_value != _v;}); }
  85. template <class F> void wait(F const& _f) const { UniqueGuard l(m_mutex); m_cv.wait(l, _f); }
  86. template <class R, class P> void wait(std::chrono::duration<R, P> _d) const { N old; { UniqueGuard l(m_mutex); old = m_value; } waitNot(_d, old); }
  87. template <class R, class P> void wait(std::chrono::duration<R, P> _d, N const& _v) const { UniqueGuard l(m_mutex); m_cv.wait_for(l, _d, [&](){return m_value == _v;}); }
  88. template <class R, class P> void waitNot(std::chrono::duration<R, P> _d, N const& _v) const { UniqueGuard l(m_mutex); m_cv.wait_for(l, _d, [&](){return m_value != _v;}); }
  89. template <class R, class P, class F> void wait(std::chrono::duration<R, P> _d, F const& _f) const { UniqueGuard l(m_mutex); m_cv.wait_for(l, _d, _f); }
  90. private:
  91. mutable Mutex m_mutex;
  92. mutable std::condition_variable m_cv;
  93. N m_value;
  94. };
  95. /** @brief Simple block guard.
  96. * The expression/block following is guarded though the given mutex.
  97. * Usage:
  98. * @code
  99. * Mutex m;
  100. * unsigned d;
  101. * ...
  102. * ETH_(m) d = 1;
  103. * ...
  104. * ETH_(m) { for (auto d = 10; d > 0; --d) foo(d); d = 0; }
  105. * @endcode
  106. *
  107. * There are several variants of this basic mechanism for different Mutex types and Guards.
  108. *
  109. * There is also the UNGUARD variant which allows an unguarded expression/block to exist within a
  110. * guarded expression. eg:
  111. *
  112. * @code
  113. * Mutex m;
  114. * int d;
  115. * ...
  116. * ETH_GUARDED(m)
  117. * {
  118. * for (auto d = 50; d > 25; --d)
  119. * foo(d);
  120. * ETH_UNGUARDED(m)
  121. * bar();
  122. * for (; d > 0; --d)
  123. * foo(d);
  124. * }
  125. * @endcode
  126. */
  127. #define DEV_GUARDED(MUTEX) \
  128. for (GenericGuardBool<Guard, Mutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
  129. #define DEV_READ_GUARDED(MUTEX) \
  130. for (GenericGuardBool<ReadGuard, SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
  131. #define DEV_WRITE_GUARDED(MUTEX) \
  132. for (GenericGuardBool<WriteGuard, SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
  133. #define DEV_RECURSIVE_GUARDED(MUTEX) \
  134. for (GenericGuardBool<RecursiveGuard, RecursiveMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
  135. #define DEV_UNGUARDED(MUTEX) \
  136. for (GenericUnguardBool<Mutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
  137. #define DEV_READ_UNGUARDED(MUTEX) \
  138. for (GenericUnguardSharedBool<SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
  139. #define DEV_WRITE_UNGUARDED(MUTEX) \
  140. for (GenericUnguardBool<SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
  141. }