rwmutex.h 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Copyright 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef dap_rwmutex_h
  15. #define dap_rwmutex_h
  16. #include <condition_variable>
  17. #include <mutex>
  18. namespace dap {
  19. ////////////////////////////////////////////////////////////////////////////////
  20. // RWMutex
  21. ////////////////////////////////////////////////////////////////////////////////
  22. // A RWMutex is a reader/writer mutual exclusion lock.
  23. // The lock can be held by an arbitrary number of readers or a single writer.
  24. // Also known as a shared mutex.
  25. class RWMutex {
  26. public:
  27. inline RWMutex() = default;
  28. // lockReader() locks the mutex for reading.
  29. // Multiple read locks can be held while there are no writer locks.
  30. inline void lockReader();
  31. // unlockReader() unlocks the mutex for reading.
  32. inline void unlockReader();
  33. // lockWriter() locks the mutex for writing.
  34. // If the lock is already locked for reading or writing, lockWriter blocks
  35. // until the lock is available.
  36. inline void lockWriter();
  37. // unlockWriter() unlocks the mutex for writing.
  38. inline void unlockWriter();
  39. private:
  40. RWMutex(const RWMutex&) = delete;
  41. RWMutex& operator=(const RWMutex&) = delete;
  42. int readLocks = 0;
  43. int pendingWriteLocks = 0;
  44. std::mutex mutex;
  45. std::condition_variable cv;
  46. };
  47. void RWMutex::lockReader() {
  48. std::unique_lock<std::mutex> lock(mutex);
  49. readLocks++;
  50. }
  51. void RWMutex::unlockReader() {
  52. std::unique_lock<std::mutex> lock(mutex);
  53. readLocks--;
  54. if (readLocks == 0 && pendingWriteLocks > 0) {
  55. cv.notify_one();
  56. }
  57. }
  58. void RWMutex::lockWriter() {
  59. std::unique_lock<std::mutex> lock(mutex);
  60. if (readLocks > 0) {
  61. pendingWriteLocks++;
  62. cv.wait(lock, [&] { return readLocks == 0; });
  63. pendingWriteLocks--;
  64. }
  65. lock.release(); // Keep lock held
  66. }
  67. void RWMutex::unlockWriter() {
  68. if (pendingWriteLocks > 0) {
  69. cv.notify_one();
  70. }
  71. mutex.unlock();
  72. }
  73. ////////////////////////////////////////////////////////////////////////////////
  74. // RLock
  75. ////////////////////////////////////////////////////////////////////////////////
  76. // RLock is a RAII read lock helper for a RWMutex.
  77. class RLock {
  78. public:
  79. inline RLock(RWMutex& mutex);
  80. inline ~RLock();
  81. inline RLock(RLock&&);
  82. inline RLock& operator=(RLock&&);
  83. private:
  84. RLock(const RLock&) = delete;
  85. RLock& operator=(const RLock&) = delete;
  86. RWMutex* m;
  87. };
  88. RLock::RLock(RWMutex& mutex) : m(&mutex) {
  89. m->lockReader();
  90. }
  91. RLock::~RLock() {
  92. if (m != nullptr) {
  93. m->unlockReader();
  94. }
  95. }
  96. RLock::RLock(RLock&& other) {
  97. m = other.m;
  98. other.m = nullptr;
  99. }
  100. RLock& RLock::operator=(RLock&& other) {
  101. m = other.m;
  102. other.m = nullptr;
  103. return *this;
  104. }
  105. ////////////////////////////////////////////////////////////////////////////////
  106. // WLock
  107. ////////////////////////////////////////////////////////////////////////////////
  108. // WLock is a RAII write lock helper for a RWMutex.
  109. class WLock {
  110. public:
  111. inline WLock(RWMutex& mutex);
  112. inline ~WLock();
  113. inline WLock(WLock&&);
  114. inline WLock& operator=(WLock&&);
  115. private:
  116. WLock(const WLock&) = delete;
  117. WLock& operator=(const WLock&) = delete;
  118. RWMutex* m;
  119. };
  120. WLock::WLock(RWMutex& mutex) : m(&mutex) {
  121. m->lockWriter();
  122. }
  123. WLock::~WLock() {
  124. if (m != nullptr) {
  125. m->unlockWriter();
  126. }
  127. }
  128. WLock::WLock(WLock&& other) {
  129. m = other.m;
  130. other.m = nullptr;
  131. }
  132. WLock& WLock::operator=(WLock&& other) {
  133. m = other.m;
  134. other.m = nullptr;
  135. return *this;
  136. }
  137. } // namespace dap
  138. #endif