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 #include <folly/Portability.h>
10 #include <folly/Utility.h>
11 
12 #include <cassert>
13 #include <memory>
14 #include <mutex>
15 #include <stdexcept>
16 #include <utility>
17 
18 namespace folly {
19 namespace detail {
20 namespace proxylockable_detail {
21 template <typename Bool>
throwIfAlreadyLocked(Bool && locked)22 void throwIfAlreadyLocked(Bool&& locked) {
23   if (kIsDebug && locked) {
24     throw std::system_error{
25         std::make_error_code(std::errc::resource_deadlock_would_occur)};
26   }
27 }
28 
29 template <typename Bool>
throwIfNotLocked(Bool && locked)30 void throwIfNotLocked(Bool&& locked) {
31   if (kIsDebug && !locked) {
32     throw std::system_error{
33         std::make_error_code(std::errc::operation_not_permitted)};
34   }
35 }
36 
37 template <typename Bool>
throwIfNoMutex(Bool && mutex)38 void throwIfNoMutex(Bool&& mutex) {
39   if (kIsDebug && !mutex) {
40     throw std::system_error{
41         std::make_error_code(std::errc::operation_not_permitted)};
42   }
43 }
44 } // namespace proxylockable_detail
45 
46 template <typename Mutex>
~ProxyLockableUniqueLock()47 ProxyLockableUniqueLock<Mutex>::~ProxyLockableUniqueLock() {
48   if (owns_lock()) {
49     unlock();
50   }
51 }
52 
53 template <typename Mutex>
ProxyLockableUniqueLock(mutex_type & mtx)54 ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock(
55     mutex_type& mtx) noexcept {
56   proxy_.emplace(mtx.lock());
57   mutex_ = std::addressof(mtx);
58 }
59 
60 template <typename Mutex>
ProxyLockableUniqueLock(ProxyLockableUniqueLock && a)61 ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock(
62     ProxyLockableUniqueLock&& a) noexcept {
63   *this = std::move(a);
64 }
65 
66 template <typename Mutex>
67 ProxyLockableUniqueLock<Mutex>& ProxyLockableUniqueLock<Mutex>::operator=(
68     ProxyLockableUniqueLock&& other) noexcept {
69   proxy_ = std::move(other.proxy_);
70   mutex_ = folly::exchange(other.mutex_, nullptr);
71   return *this;
72 }
73 
74 template <typename Mutex>
ProxyLockableUniqueLock(mutex_type & mtx,std::defer_lock_t)75 ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock(
76     mutex_type& mtx,
77     std::defer_lock_t) noexcept {
78   mutex_ = std::addressof(mtx);
79 }
80 
81 template <typename Mutex>
ProxyLockableUniqueLock(mutex_type & mtx,std::try_to_lock_t)82 ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock(
83     mutex_type& mtx,
84     std::try_to_lock_t) {
85   mutex_ = std::addressof(mtx);
86   if (auto state = mtx.try_lock()) {
87     proxy_.emplace(std::move(state));
88   }
89 }
90 
91 template <typename Mutex>
92 template <typename Rep, typename Period>
ProxyLockableUniqueLock(mutex_type & mtx,const std::chrono::duration<Rep,Period> & duration)93 ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock(
94     mutex_type& mtx,
95     const std::chrono::duration<Rep, Period>& duration) {
96   mutex_ = std::addressof(mtx);
97   if (auto state = mtx.try_lock_for(duration)) {
98     proxy_.emplace(std::move(state));
99   }
100 }
101 
102 template <typename Mutex>
103 template <typename Clock, typename Duration>
ProxyLockableUniqueLock(mutex_type & mtx,const std::chrono::time_point<Clock,Duration> & time)104 ProxyLockableUniqueLock<Mutex>::ProxyLockableUniqueLock(
105     mutex_type& mtx,
106     const std::chrono::time_point<Clock, Duration>& time) {
107   mutex_ = std::addressof(mtx);
108   if (auto state = mtx.try_lock_until(time)) {
109     proxy_.emplace(std::move(state));
110   }
111 }
112 
113 template <typename Mutex>
lock()114 void ProxyLockableUniqueLock<Mutex>::lock() {
115   proxylockable_detail::throwIfAlreadyLocked(proxy_);
116   proxylockable_detail::throwIfNoMutex(mutex_);
117 
118   proxy_.emplace(mutex_->lock());
119 }
120 
121 template <typename Mutex>
unlock()122 void ProxyLockableUniqueLock<Mutex>::unlock() {
123   proxylockable_detail::throwIfNoMutex(mutex_);
124   proxylockable_detail::throwIfNotLocked(proxy_);
125 
126   mutex_->unlock(std::move(*proxy_));
127   proxy_.reset();
128 }
129 
130 template <typename Mutex>
try_lock()131 bool ProxyLockableUniqueLock<Mutex>::try_lock() {
132   proxylockable_detail::throwIfNoMutex(mutex_);
133   proxylockable_detail::throwIfAlreadyLocked(proxy_);
134 
135   if (auto state = mutex_->try_lock()) {
136     proxy_.emplace(std::move(state));
137     return true;
138   }
139 
140   return false;
141 }
142 
143 template <typename Mutex>
144 template <typename Rep, typename Period>
try_lock_for(const std::chrono::duration<Rep,Period> & duration)145 bool ProxyLockableUniqueLock<Mutex>::try_lock_for(
146     const std::chrono::duration<Rep, Period>& duration) {
147   proxylockable_detail::throwIfNoMutex(mutex_);
148   proxylockable_detail::throwIfAlreadyLocked(proxy_);
149 
150   if (auto state = mutex_->try_lock_for(duration)) {
151     proxy_.emplace(std::move(state));
152     return true;
153   }
154 
155   return false;
156 }
157 
158 template <typename Mutex>
159 template <typename Clock, typename Duration>
try_lock_until(const std::chrono::time_point<Clock,Duration> & time)160 bool ProxyLockableUniqueLock<Mutex>::try_lock_until(
161     const std::chrono::time_point<Clock, Duration>& time) {
162   proxylockable_detail::throwIfNoMutex(mutex_);
163   proxylockable_detail::throwIfAlreadyLocked(proxy_);
164 
165   if (auto state = mutex_->try_lock_until(time)) {
166     proxy_.emplace(std::move(state));
167     return true;
168   }
169 
170   return false;
171 }
172 
173 template <typename Mutex>
swap(ProxyLockableUniqueLock & other)174 void ProxyLockableUniqueLock<Mutex>::swap(
175     ProxyLockableUniqueLock& other) noexcept {
176   std::swap(mutex_, other.mutex_);
177   std::swap(proxy_, other.proxy_);
178 }
179 
180 template <typename Mutex>
181 typename ProxyLockableUniqueLock<Mutex>::mutex_type*
mutex()182 ProxyLockableUniqueLock<Mutex>::mutex() const noexcept {
183   return mutex_;
184 }
185 
186 template <typename Mutex>
187 typename ProxyLockableUniqueLock<Mutex>::proxy_type*
proxy()188 ProxyLockableUniqueLock<Mutex>::proxy() const noexcept {
189   return proxy_ ? std::addressof(proxy_.value()) : nullptr;
190 }
191 
192 template <typename Mutex>
owns_lock()193 bool ProxyLockableUniqueLock<Mutex>::owns_lock() const noexcept {
194   return proxy_.has_value();
195 }
196 
197 template <typename Mutex>
198 ProxyLockableUniqueLock<Mutex>::operator bool() const noexcept {
199   return owns_lock();
200 }
201 
202 template <typename Mutex>
ProxyLockableLockGuard(mutex_type & mtx)203 ProxyLockableLockGuard<Mutex>::ProxyLockableLockGuard(mutex_type& mtx)
204     : ProxyLockableUniqueLock<Mutex>{mtx} {}
205 
206 } // namespace detail
207 } // namespace folly
208