SpinLockRW.hpp 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. ///
  2. /// \file Util/SpinLockRW.hpp
  3. ///
  4. /// A read/write spinlock implementation
  5. ///
  6. /// \copyright
  7. /// Copyright (c) 2017-2017 Josh Blum
  8. /// SPDX-License-Identifier: BSL-1.0
  9. ///
  10. #pragma once
  11. #include <Pothos/Config.hpp>
  12. #include <thread>
  13. #include <atomic>
  14. namespace Pothos {
  15. namespace Util {
  16. /*!
  17. * Implementation of SharedLock
  18. * Switch to std::shared_lock for C++14
  19. */
  20. template <typename T>
  21. class SharedLock
  22. {
  23. public:
  24. SharedLock(T &mutex):
  25. _mutex(mutex)
  26. {
  27. _mutex.lock_shared();
  28. }
  29. ~SharedLock(void)
  30. {
  31. _mutex.unlock_shared();
  32. }
  33. private:
  34. T &_mutex;
  35. };
  36. /*!
  37. * A spin lock that supports multiple readers + single writer.
  38. * This lock is optimized for infrequent writes and frequent reading.
  39. * Its primarily used in the core library for certain plugin hooks
  40. * which require write locks during the plugin's loader hooks but
  41. * then requires almost exclusively reads during runtime operation.
  42. *
  43. * - For writers, use with std::lock_guard<Pothos::Util::SpinLock>
  44. * - For readers, use with Pothos::Util::SpinLockRW::SharedLock
  45. */
  46. class POTHOS_API SpinLockRW
  47. {
  48. public:
  49. //! Convenient typedef for shared lock type
  50. typedef Pothos::Util::SharedLock<SpinLockRW> SharedLock;
  51. //! Create a new unlocked spin lock
  52. SpinLockRW(void);
  53. //! Try to lock shared, return true for lock
  54. bool try_lock_shared(void) noexcept;
  55. //! Lock for multiple reader access
  56. void lock_shared(void) noexcept;
  57. //! Unlock from multiple reader access
  58. void unlock_shared(void) noexcept;
  59. //! Try to lock, return true for lock
  60. bool try_lock(void) noexcept;
  61. //! Lock for single writer access
  62. void lock(void) noexcept;
  63. //! Unlock single writer access
  64. void unlock(void) noexcept;
  65. private:
  66. enum : unsigned {WRITER_LOCK = unsigned(~0), UNLOCKED = 0};
  67. std::atomic<unsigned> _lock;
  68. };
  69. } //namespace Util
  70. } //namespace Pothos
  71. inline Pothos::Util::SpinLockRW::SpinLockRW(void)
  72. {
  73. this->unlock();
  74. }
  75. inline bool Pothos::Util::SpinLockRW::try_lock_shared(void) noexcept
  76. {
  77. //true when the expected condition is not write lock
  78. //and swap in the value of expected +1 (additional reader)
  79. unsigned expected = _lock.load(std::memory_order_acquire);
  80. return expected != WRITER_LOCK and _lock.compare_exchange_weak(expected, expected+1, std::memory_order_acq_rel);
  81. }
  82. inline void Pothos::Util::SpinLockRW::lock_shared(void) noexcept
  83. {
  84. size_t count(0);
  85. while (not this->try_lock_shared())
  86. {
  87. if (++count > 1024) std::this_thread::yield();
  88. }
  89. }
  90. inline void Pothos::Util::SpinLockRW::unlock_shared(void) noexcept
  91. {
  92. //decrement the reader count by 1
  93. _lock.fetch_sub(1, std::memory_order_release);
  94. }
  95. inline bool Pothos::Util::SpinLockRW::try_lock(void) noexcept
  96. {
  97. //true when the expected condition is unlocked (no readers)
  98. //and swap in the value of write lock (one writer)
  99. unsigned expected = UNLOCKED;
  100. return _lock.compare_exchange_weak(expected, WRITER_LOCK, std::memory_order_acq_rel);
  101. }
  102. inline void Pothos::Util::SpinLockRW::lock(void) noexcept
  103. {
  104. size_t count(0);
  105. while (not this->try_lock())
  106. {
  107. if (++count > 1024) std::this_thread::yield();
  108. }
  109. }
  110. inline void Pothos::Util::SpinLockRW::unlock(void) noexcept
  111. {
  112. //restore to unlocked (no writer, no readers)
  113. _lock.store(UNLOCKED, std::memory_order_release);
  114. }