// Copyright 2020 Google LLC1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// https://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314#ifndef dap_rwmutex_h15#define dap_rwmutex_h1617#include <condition_variable>18#include <mutex>1920namespace dap {2122////////////////////////////////////////////////////////////////////////////////23// RWMutex24////////////////////////////////////////////////////////////////////////////////2526// A RWMutex is a reader/writer mutual exclusion lock.27// The lock can be held by an arbitrary number of readers or a single writer.28// Also known as a shared mutex.29class RWMutex {30public:31inline RWMutex() = default;3233// lockReader() locks the mutex for reading.34// Multiple read locks can be held while there are no writer locks.35inline void lockReader();3637// unlockReader() unlocks the mutex for reading.38inline void unlockReader();3940// lockWriter() locks the mutex for writing.41// If the lock is already locked for reading or writing, lockWriter blocks42// until the lock is available.43inline void lockWriter();4445// unlockWriter() unlocks the mutex for writing.46inline void unlockWriter();4748private:49RWMutex(const RWMutex&) = delete;50RWMutex& operator=(const RWMutex&) = delete;5152int readLocks = 0;53int pendingWriteLocks = 0;54std::mutex mutex;55std::condition_variable cv;56};5758void RWMutex::lockReader() {59std::unique_lock<std::mutex> lock(mutex);60readLocks++;61}6263void RWMutex::unlockReader() {64std::unique_lock<std::mutex> lock(mutex);65readLocks--;66if (readLocks == 0 && pendingWriteLocks > 0) {67cv.notify_one();68}69}7071void RWMutex::lockWriter() {72std::unique_lock<std::mutex> lock(mutex);73if (readLocks > 0) {74pendingWriteLocks++;75cv.wait(lock, [&] { return readLocks == 0; });76pendingWriteLocks--;77}78lock.release(); // Keep lock held79}8081void RWMutex::unlockWriter() {82if (pendingWriteLocks > 0) {83cv.notify_one();84}85mutex.unlock();86}8788////////////////////////////////////////////////////////////////////////////////89// RLock90////////////////////////////////////////////////////////////////////////////////9192// RLock is a RAII read lock helper for a RWMutex.93class RLock {94public:95inline RLock(RWMutex& mutex);96inline ~RLock();9798inline RLock(RLock&&);99inline RLock& operator=(RLock&&);100101private:102RLock(const RLock&) = delete;103RLock& operator=(const RLock&) = delete;104105RWMutex* m;106};107108RLock::RLock(RWMutex& mutex) : m(&mutex) {109m->lockReader();110}111112RLock::~RLock() {113if (m != nullptr) {114m->unlockReader();115}116}117118RLock::RLock(RLock&& other) {119m = other.m;120other.m = nullptr;121}122123RLock& RLock::operator=(RLock&& other) {124m = other.m;125other.m = nullptr;126return *this;127}128129////////////////////////////////////////////////////////////////////////////////130// WLock131////////////////////////////////////////////////////////////////////////////////132133// WLock is a RAII write lock helper for a RWMutex.134class WLock {135public:136inline WLock(RWMutex& mutex);137inline ~WLock();138139inline WLock(WLock&&);140inline WLock& operator=(WLock&&);141142private:143WLock(const WLock&) = delete;144WLock& operator=(const WLock&) = delete;145146RWMutex* m;147};148149WLock::WLock(RWMutex& mutex) : m(&mutex) {150m->lockWriter();151}152153WLock::~WLock() {154if (m != nullptr) {155m->unlockWriter();156}157}158159WLock::WLock(WLock&& other) {160m = other.m;161other.m = nullptr;162}163164WLock& WLock::operator=(WLock&& other) {165m = other.m;166other.m = nullptr;167return *this;168}169} // namespace dap170171#endif172173174