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