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
15#ifndef dap_rwmutex_h
16#define dap_rwmutex_h
17
18#include <condition_variable>
19#include <mutex>
20
21namespace dap {
22
23////////////////////////////////////////////////////////////////////////////////
24// RWMutex
25////////////////////////////////////////////////////////////////////////////////
26
27// A RWMutex is a reader/writer mutual exclusion lock.
28// The lock can be held by an arbitrary number of readers or a single writer.
29// Also known as a shared mutex.
30class RWMutex {
31 public:
32 inline RWMutex() = default;
33
34 // lockReader() locks the mutex for reading.
35 // Multiple read locks can be held while there are no writer locks.
36 inline void lockReader();
37
38 // unlockReader() unlocks the mutex for reading.
39 inline void unlockReader();
40
41 // lockWriter() locks the mutex for writing.
42 // If the lock is already locked for reading or writing, lockWriter blocks
43 // until the lock is available.
44 inline void lockWriter();
45
46 // unlockWriter() unlocks the mutex for writing.
47 inline void unlockWriter();
48
49 private:
50 RWMutex(const RWMutex&) = delete;
51 RWMutex& operator=(const RWMutex&) = delete;
52
53 int readLocks = 0;
54 int pendingWriteLocks = 0;
55 std::mutex mutex;
56 std::condition_variable cv;
57};
58
59void RWMutex::lockReader() {
60 std::unique_lock<std::mutex> lock(mutex);
61 readLocks++;
62}
63
64void RWMutex::unlockReader() {
65 std::unique_lock<std::mutex> lock(mutex);
66 readLocks--;
67 if (readLocks == 0 && pendingWriteLocks > 0) {
68 cv.notify_one();
69 }
70}
71
72void RWMutex::lockWriter() {
73 std::unique_lock<std::mutex> lock(mutex);
74 if (readLocks > 0) {
75 pendingWriteLocks++;
76 cv.wait(lock, [&] { return readLocks == 0; });
77 pendingWriteLocks--;
78 }
79 lock.release(); // Keep lock held
80}
81
82void RWMutex::unlockWriter() {
83 if (pendingWriteLocks > 0) {
84 cv.notify_one();
85 }
86 mutex.unlock();
87}
88
89////////////////////////////////////////////////////////////////////////////////
90// RLock
91////////////////////////////////////////////////////////////////////////////////
92
93// RLock is a RAII read lock helper for a RWMutex.
94class RLock {
95 public:
96 inline RLock(RWMutex& mutex);
97 inline ~RLock();
98
99 inline RLock(RLock&&);
100 inline RLock& operator=(RLock&&);
101
102 private:
103 RLock(const RLock&) = delete;
104 RLock& operator=(const RLock&) = delete;
105
106 RWMutex* m;
107};
108
109RLock::RLock(RWMutex& mutex) : m(&mutex) {
110 m->lockReader();
111}
112
113RLock::~RLock() {
114 if (m != nullptr) {
115 m->unlockReader();
116 }
117}
118
119RLock::RLock(RLock&& other) {
120 m = other.m;
121 other.m = nullptr;
122}
123
124RLock& RLock::operator=(RLock&& other) {
125 m = other.m;
126 other.m = nullptr;
127 return *this;
128}
129
130////////////////////////////////////////////////////////////////////////////////
131// WLock
132////////////////////////////////////////////////////////////////////////////////
133
134// WLock is a RAII write lock helper for a RWMutex.
135class WLock {
136 public:
137 inline WLock(RWMutex& mutex);
138 inline ~WLock();
139
140 inline WLock(WLock&&);
141 inline WLock& operator=(WLock&&);
142
143 private:
144 WLock(const WLock&) = delete;
145 WLock& operator=(const WLock&) = delete;
146
147 RWMutex* m;
148};
149
150WLock::WLock(RWMutex& mutex) : m(&mutex) {
151 m->lockWriter();
152}
153
154WLock::~WLock() {
155 if (m != nullptr) {
156 m->unlockWriter();
157 }
158}
159
160WLock::WLock(WLock&& other) {
161 m = other.m;
162 other.m = nullptr;
163}
164
165WLock& WLock::operator=(WLock&& other) {
166 m = other.m;
167 other.m = nullptr;
168 return *this;
169}
170} // namespace dap
171
172#endif
173