#pragma once
#include <memory>
#include <mutex>
#include <utility>
#include <glog/logging.h>
#include <folly/Portability.h>
#include <folly/experimental/coro/Coroutine.h>
#if FOLLY_HAS_COROUTINES
namespace folly {
namespace coro {
template <typename Mutex>
class FOLLY_NODISCARD SharedLock {
public:
SharedLock() noexcept : mutex_(nullptr), locked_(false) {}
explicit SharedLock(Mutex& mutex, std::defer_lock_t) noexcept
: mutex_(std::addressof(mutex)), locked_(false) {}
explicit SharedLock(Mutex& mutex, std::adopt_lock_t) noexcept
: mutex_(std::addressof(mutex)), locked_(true) {}
explicit SharedLock(Mutex& mutex, std::try_to_lock_t) noexcept(
noexcept(mutex.try_lock_shared()))
: mutex_(std::addressof(mutex)), locked_(mutex.try_lock_shared()) {}
SharedLock(SharedLock&& other) noexcept
: mutex_(std::exchange(other.mutex_, nullptr)),
locked_(std::exchange(other.locked_, false)) {}
SharedLock(const SharedLock&) = delete;
SharedLock& operator=(const SharedLock&) = delete;
~SharedLock() {
if (locked_) {
mutex_->unlock_shared();
}
}
SharedLock& operator=(SharedLock&& other) noexcept {
SharedLock temp(std::move(other));
swap(temp);
return *this;
}
Mutex* mutex() const noexcept { return mutex_; }
Mutex* release() noexcept {
locked_ = false;
return std::exchange(mutex_, nullptr);
}
bool owns_lock() const noexcept { return locked_; }
explicit operator bool() const noexcept { return owns_lock(); }
bool try_lock() noexcept(noexcept(mutex_->try_lock_shared())) {
DCHECK(!locked_);
DCHECK(mutex_ != nullptr);
locked_ = mutex_->try_lock_shared();
return locked_;
}
void unlock() noexcept(noexcept(mutex_->unlock_shared())) {
DCHECK(locked_);
locked_ = false;
mutex_->unlock_shared();
}
void swap(SharedLock& other) noexcept {
std::swap(mutex_, other.mutex_);
std::swap(locked_, other.locked_);
}
private:
Mutex* mutex_;
bool locked_;
};
}
}
#endif