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 #pragma once 7 8 #include <folly/Optional.h> 9 10 #include <mutex> 11 12 namespace folly { 13 namespace detail { 14 15 /** 16 * ProxyLockable is a "concept" that is used usually for mutexes that don't 17 * return void, but rather a proxy object that contains data that should be 18 * passed to the unlock function. 19 * 20 * This is in contrast with the normal Lockable concept that imposes no 21 * requirement on the return type of lock(), and requires an unlock() with no 22 * parameters. Here we require that lock() returns non-void and that unlock() 23 * accepts the return type of lock() by value, rvalue-reference or 24 * const-reference 25 * 26 * Here we define two classes, that can be used by the top level to implement 27 * specializations for std::unique_lock and std::lock_guard. Both 28 * ProxyLockableUniqueLock and ProxyLockableLockGuard implement the entire 29 * interface of std::unique_lock and std::lock_guard respectively 30 */ 31 template <typename Mutex> 32 class ProxyLockableUniqueLock { 33 public: 34 using mutex_type = Mutex; 35 using proxy_type = 36 _t<std::decay<decltype(std::declval<mutex_type>().lock())>>; 37 38 /** 39 * Default constructor initializes the unique_lock to an empty state 40 */ 41 ProxyLockableUniqueLock() = default; 42 43 /** 44 * Destructor releases the mutex if it is locked 45 */ 46 ~ProxyLockableUniqueLock(); 47 48 /** 49 * Move constructor and move assignment operators take state from the other 50 * lock 51 */ 52 ProxyLockableUniqueLock(ProxyLockableUniqueLock&& other) noexcept; 53 ProxyLockableUniqueLock& operator=(ProxyLockableUniqueLock&&) noexcept; 54 55 /** 56 * Locks the mutex, blocks until the mutex can be acquired. 57 * 58 * The mutex is guaranteed to be acquired after this function returns. 59 */ 60 ProxyLockableUniqueLock(mutex_type&) noexcept; 61 62 /** 63 * Explicit locking constructors to control how the lock() method is called 64 * 65 * std::defer_lock_t causes the mutex to get tracked, but not locked 66 * std::try_to_lock_t causes try_lock() to be called. The current object is 67 * converts to true if the lock was successful 68 */ 69 ProxyLockableUniqueLock(mutex_type& mtx, std::defer_lock_t) noexcept; 70 ProxyLockableUniqueLock(mutex_type& mtx, std::try_to_lock_t); 71 72 /** 73 * Timed locking constructors 74 */ 75 template <typename Rep, typename Period> 76 ProxyLockableUniqueLock( 77 mutex_type& mtx, 78 const std::chrono::duration<Rep, Period>& duration); 79 template <typename Clock, typename Duration> 80 ProxyLockableUniqueLock( 81 mutex_type& mtx, 82 const std::chrono::time_point<Clock, Duration>& time); 83 84 /** 85 * Lock and unlock methods 86 * 87 * lock() and try_lock() throw if the mutex is already locked, or there is 88 * no mutex. unlock() throws if there is no mutex or if the mutex was not 89 * locked 90 */ 91 void lock(); 92 void unlock(); 93 bool try_lock(); 94 95 /** 96 * Timed locking methods 97 * 98 * These throw if there was no mutex, or if the mutex was already locked 99 */ 100 template <typename Rep, typename Period> 101 bool try_lock_for(const std::chrono::duration<Rep, Period>& duration); 102 template <typename Clock, typename Duration> 103 bool try_lock_until(const std::chrono::time_point<Clock, Duration>& time); 104 105 /** 106 * Swap this unique lock with the other one 107 */ 108 void swap(ProxyLockableUniqueLock& other) noexcept; 109 110 /** 111 * Returns true if the unique lock contains a lock and also has acquired an 112 * exclusive lock successfully 113 */ 114 bool owns_lock() const noexcept; 115 explicit operator bool() const noexcept; 116 117 /** 118 * mutex() return a pointer to the mutex if there is a contained mutex and 119 * proxy() returns a pointer to the contained proxy if the mutex is locked 120 * 121 * If the unique lock was not constructed with a mutex, then mutex() returns 122 * nullptr. If the mutex is not locked, then proxy() returns nullptr 123 */ 124 mutex_type* mutex() const noexcept; 125 proxy_type* proxy() const noexcept; 126 127 private: 128 friend class ProxyLockableTest; 129 130 /** 131 * If the optional has a value, the mutex is locked, if it is empty, it is 132 * not 133 */ 134 mutable folly::Optional<proxy_type> proxy_{}; 135 mutex_type* mutex_{nullptr}; 136 }; 137 138 template <typename Mutex> 139 class ProxyLockableLockGuard : private ProxyLockableUniqueLock<Mutex> { 140 public: 141 using mutex_type = Mutex; 142 143 /** 144 * Constructor locks the mutex, and destructor unlocks 145 */ 146 ProxyLockableLockGuard(mutex_type& mtx); 147 ~ProxyLockableLockGuard() = default; 148 149 /** 150 * This class is not movable or assignable 151 * 152 * For more complicated usecases, consider the UniqueLock variant, which 153 * provides more options 154 */ 155 ProxyLockableLockGuard(const ProxyLockableLockGuard&) = delete; 156 ProxyLockableLockGuard(ProxyLockableLockGuard&&) = delete; 157 ProxyLockableLockGuard& operator=(ProxyLockableLockGuard&&) = delete; 158 ProxyLockableLockGuard& operator=(const ProxyLockableLockGuard&) = delete; 159 }; 160 161 } // namespace detail 162 } // namespace folly 163 164 #include <folly/synchronization/detail/ProxyLockable-inl.h> 165