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