// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). #include "utilities/transactions/transaction_db_mutex_impl.h" #include #include #include #include #include "rocksdb/utilities/transaction_db_mutex.h" namespace ROCKSDB_NAMESPACE { class TransactionDBMutexImpl : public TransactionDBMutex { public: TransactionDBMutexImpl() {} ~TransactionDBMutexImpl() override {} Status Lock() override; Status TryLockFor(int64_t timeout_time) override; void UnLock() override { mutex_.unlock(); } friend class TransactionDBCondVarImpl; private: std::mutex mutex_; }; class TransactionDBCondVarImpl : public TransactionDBCondVar { public: TransactionDBCondVarImpl() {} ~TransactionDBCondVarImpl() override {} Status Wait(std::shared_ptr mutex) override; Status WaitFor(std::shared_ptr mutex, int64_t timeout_time) override; void Notify() override { cv_.notify_one(); } void NotifyAll() override { cv_.notify_all(); } private: std::condition_variable cv_; }; std::shared_ptr TransactionDBMutexFactoryImpl::AllocateMutex() { return std::shared_ptr(new TransactionDBMutexImpl()); } std::shared_ptr TransactionDBMutexFactoryImpl::AllocateCondVar() { return std::shared_ptr(new TransactionDBCondVarImpl()); } Status TransactionDBMutexImpl::Lock() { mutex_.lock(); return Status::OK(); } Status TransactionDBMutexImpl::TryLockFor(int64_t timeout_time) { bool locked = true; if (timeout_time == 0) { locked = mutex_.try_lock(); } else { // Previously, this code used a std::timed_mutex. However, this was changed // due to known bugs in gcc versions < 4.9. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54562 // // Since this mutex isn't held for long and only a single mutex is ever // held at a time, it is reasonable to ignore the lock timeout_time here // and only check it when waiting on the condition_variable. mutex_.lock(); } if (!locked) { // timeout acquiring mutex return Status::TimedOut(Status::SubCode::kMutexTimeout); } return Status::OK(); } Status TransactionDBCondVarImpl::Wait( std::shared_ptr mutex) { auto mutex_impl = static_cast(mutex.get()); std::unique_lock lock(mutex_impl->mutex_, std::adopt_lock); cv_.wait(lock); // Make sure unique_lock doesn't unlock mutex when it destructs lock.release(); return Status::OK(); } Status TransactionDBCondVarImpl::WaitFor( std::shared_ptr mutex, int64_t timeout_time) { Status s; auto mutex_impl = static_cast(mutex.get()); std::unique_lock lock(mutex_impl->mutex_, std::adopt_lock); if (timeout_time < 0) { // If timeout is negative, do not use a timeout cv_.wait(lock); } else { auto duration = std::chrono::microseconds(timeout_time); auto cv_status = cv_.wait_for(lock, duration); // Check if the wait stopped due to timing out. if (cv_status == std::cv_status::timeout) { s = Status::TimedOut(Status::SubCode::kMutexTimeout); } } // Make sure unique_lock doesn't unlock mutex when it destructs lock.release(); // CV was signaled, or we spuriously woke up (but didn't time out) return s; } } // namespace ROCKSDB_NAMESPACE