1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. 2 // This source code is licensed under both the GPLv2 (found in the 3 // COPYING file in the root directory) and Apache 2.0 License 4 // (found in the LICENSE.Apache file in the root directory). 5 // 6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 7 // Use of this source code is governed by a BSD-style license that can be 8 // found in the LICENSE file. See the AUTHORS file for names of contributors. 9 10 #pragma once 11 #include <assert.h> 12 #include <atomic> 13 #include <mutex> 14 #include <thread> 15 #include "port/port.h" 16 17 namespace ROCKSDB_NAMESPACE { 18 19 // Helper class that locks a mutex on construction and unlocks the mutex when 20 // the destructor of the MutexLock object is invoked. 21 // 22 // Typical usage: 23 // 24 // void MyClass::MyMethod() { 25 // MutexLock l(&mu_); // mu_ is an instance variable 26 // ... some complex code, possibly with multiple return paths ... 27 // } 28 29 class MutexLock { 30 public: MutexLock(port::Mutex * mu)31 explicit MutexLock(port::Mutex *mu) : mu_(mu) { 32 this->mu_->Lock(); 33 } 34 // No copying allowed 35 MutexLock(const MutexLock &) = delete; 36 void operator=(const MutexLock &) = delete; 37 ~MutexLock()38 ~MutexLock() { this->mu_->Unlock(); } 39 40 private: 41 port::Mutex *const mu_; 42 }; 43 44 // 45 // Acquire a ReadLock on the specified RWMutex. 46 // The Lock will be automatically released then the 47 // object goes out of scope. 48 // 49 class ReadLock { 50 public: ReadLock(port::RWMutex * mu)51 explicit ReadLock(port::RWMutex *mu) : mu_(mu) { 52 this->mu_->ReadLock(); 53 } 54 // No copying allowed 55 ReadLock(const ReadLock &) = delete; 56 void operator=(const ReadLock &) = delete; 57 ~ReadLock()58 ~ReadLock() { this->mu_->ReadUnlock(); } 59 60 private: 61 port::RWMutex *const mu_; 62 }; 63 64 // 65 // Automatically unlock a locked mutex when the object is destroyed 66 // 67 class ReadUnlock { 68 public: ReadUnlock(port::RWMutex * mu)69 explicit ReadUnlock(port::RWMutex *mu) : mu_(mu) { mu->AssertHeld(); } 70 // No copying allowed 71 ReadUnlock(const ReadUnlock &) = delete; 72 ReadUnlock &operator=(const ReadUnlock &) = delete; 73 ~ReadUnlock()74 ~ReadUnlock() { mu_->ReadUnlock(); } 75 76 private: 77 port::RWMutex *const mu_; 78 }; 79 80 // 81 // Acquire a WriteLock on the specified RWMutex. 82 // The Lock will be automatically released then the 83 // object goes out of scope. 84 // 85 class WriteLock { 86 public: WriteLock(port::RWMutex * mu)87 explicit WriteLock(port::RWMutex *mu) : mu_(mu) { 88 this->mu_->WriteLock(); 89 } 90 // No copying allowed 91 WriteLock(const WriteLock &) = delete; 92 void operator=(const WriteLock &) = delete; 93 ~WriteLock()94 ~WriteLock() { this->mu_->WriteUnlock(); } 95 96 private: 97 port::RWMutex *const mu_; 98 }; 99 100 // 101 // SpinMutex has very low overhead for low-contention cases. Method names 102 // are chosen so you can use std::unique_lock or std::lock_guard with it. 103 // 104 class SpinMutex { 105 public: SpinMutex()106 SpinMutex() : locked_(false) {} 107 try_lock()108 bool try_lock() { 109 auto currently_locked = locked_.load(std::memory_order_relaxed); 110 return !currently_locked && 111 locked_.compare_exchange_weak(currently_locked, true, 112 std::memory_order_acquire, 113 std::memory_order_relaxed); 114 } 115 lock()116 void lock() { 117 for (size_t tries = 0;; ++tries) { 118 if (try_lock()) { 119 // success 120 break; 121 } 122 port::AsmVolatilePause(); 123 if (tries > 100) { 124 std::this_thread::yield(); 125 } 126 } 127 } 128 unlock()129 void unlock() { locked_.store(false, std::memory_order_release); } 130 131 private: 132 std::atomic<bool> locked_; 133 }; 134 135 } // namespace ROCKSDB_NAMESPACE 136