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