// Copyright (c) 2015, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the // LICENSE file in the root directory of this source tree. An additional grant // of patent rights can be found in the PATENTS file in the same directory. #ifndef ROCKSDB_LITE #include "utilities/transactions/transaction_db_mutex_impl.h" #include #include #include #include #include "rocksdb/utilities/transaction_db_mutex.h" namespace rocksdb { class TransactionDBMutexImpl : public TransactionDBMutex { public: TransactionDBMutexImpl() : lock_(mutex_, std::defer_lock) {} ~TransactionDBMutexImpl() {} Status Lock() override; Status TryLockFor(int64_t timeout_time) override; void UnLock() override { lock_.unlock(); } friend class TransactionDBCondVarImpl; private: std::mutex mutex_; // Do not acquire mutex_ directly. Use lock_. std::unique_lock lock_; }; class TransactionDBCondVarImpl : public TransactionDBCondVar { public: TransactionDBCondVarImpl() {} ~TransactionDBCondVarImpl() {} 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() { lock_.lock(); return Status::OK(); } Status TransactionDBMutexImpl::TryLockFor(int64_t timeout_time) { bool locked = true; if (timeout_time == 0) { locked = lock_.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. lock_.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 = reinterpret_cast(mutex.get()); cv_.wait(mutex_impl->lock_); return Status::OK(); } Status TransactionDBCondVarImpl::WaitFor( std::shared_ptr mutex, int64_t timeout_time) { auto mutex_impl = reinterpret_cast(mutex.get()); if (timeout_time < 0) { // If timeout is negative, do not use a timeout cv_.wait(mutex_impl->lock_); } else { auto duration = std::chrono::microseconds(timeout_time); auto cv_status = cv_.wait_for(mutex_impl->lock_, duration); // Check if the wait stopped due to timing out. if (cv_status == std::cv_status::timeout) { return Status::TimedOut(Status::SubCode::kMutexTimeout); } } // CV was signaled, or we spuriously woke up (but didn't time out) return Status::OK(); } } // namespace rocksdb #endif // ROCKSDB_LITE