2 * Copyright 2016 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * This module provides a traits class for describing properties about mutex
21 * This is a primitive for building higher-level abstractions that can work
22 * with a variety of mutex classes. For instance, this allows
23 * folly::Synchronized to support a number of different mutex types.
28 #include <type_traits>
30 // Android, OSX, and Cygwin don't have timed mutexes
31 #if defined(ANDROID) || defined(__ANDROID__) || defined(__APPLE__) || \
33 #define FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES 0
35 #define FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES 1
42 * An internal helper class for identifying if a lock type supports
43 * lock_shared() and try_lock_for() methods.
45 template <class Mutex>
46 class LockTraitsImpl {
48 // Helper functions for implementing the traits using SFINAE
50 static auto timed_lock_test(T*) -> typename std::is_same<
51 decltype(std::declval<T>().try_lock_for(std::chrono::milliseconds(0))),
54 static std::false_type timed_lock_test(...);
57 static auto lock_shared_test(T*) -> typename std::
58 is_same<decltype(std::declval<T>().lock_shared()), void>::type;
60 static std::false_type lock_shared_test(...);
63 static constexpr bool has_timed_lock =
64 decltype(timed_lock_test<Mutex>(0))::value;
65 static constexpr bool has_lock_shared =
66 decltype(lock_shared_test<Mutex>(0))::value;
69 template <class Mutex>
70 struct LockTraitsUniqueBase {
72 * Acquire the lock exclusively.
74 static void lock(Mutex& mutex) {
79 * Release an exclusively-held lock.
81 static void unlock(Mutex& mutex) {
86 template <class Mutex>
87 struct LockTraitsSharedBase : public LockTraitsUniqueBase<Mutex> {
89 * Acquire the lock in shared (read) mode.
91 static void lock_shared(Mutex& mutex) {
96 * Release a lock held in shared mode.
98 static void unlock_shared(Mutex& mutex) {
99 mutex.unlock_shared();
103 template <class Mutex, bool is_shared, bool is_timed>
104 struct LockTraitsBase {};
106 template <class Mutex>
107 struct LockTraitsBase<Mutex, false, false>
108 : public LockTraitsUniqueBase<Mutex> {
109 static constexpr bool is_shared = false;
110 static constexpr bool is_timed = false;
113 template <class Mutex>
114 struct LockTraitsBase<Mutex, true, false> : public LockTraitsSharedBase<Mutex> {
115 static constexpr bool is_shared = true;
116 static constexpr bool is_timed = false;
119 template <class Mutex>
120 struct LockTraitsBase<Mutex, false, true> : public LockTraitsUniqueBase<Mutex> {
121 static constexpr bool is_shared = false;
122 static constexpr bool is_timed = true;
125 * Acquire the lock exclusively, with a timeout.
127 * Returns true or false indicating if the lock was acquired or not.
129 template <class Rep, class Period>
130 static bool try_lock_for(
132 const std::chrono::duration<Rep, Period>& timeout) {
133 return mutex.try_lock_for(timeout);
137 template <class Mutex>
138 struct LockTraitsBase<Mutex, true, true> : public LockTraitsSharedBase<Mutex> {
139 static constexpr bool is_shared = true;
140 static constexpr bool is_timed = true;
143 * Acquire the lock exclusively, with a timeout.
145 * Returns true or false indicating if the lock was acquired or not.
147 template <class Rep, class Period>
148 static bool try_lock_for(
150 const std::chrono::duration<Rep, Period>& timeout) {
151 return mutex.try_lock_for(timeout);
155 * Acquire the lock in shared (read) mode, with a timeout.
157 * Returns true or false indicating if the lock was acquired or not.
159 template <class Rep, class Period>
160 static bool try_lock_shared_for(
162 const std::chrono::duration<Rep, Period>& timeout) {
163 return mutex.try_lock_shared_for(timeout);
169 * LockTraits describes details about a particular mutex type.
171 * The default implementation automatically attempts to detect traits
172 * based on the presence of various member functions.
174 * You can specialize LockTraits to provide custom behavior for lock
175 * classes that do not use the standard method names
176 * (lock()/unlock()/lock_shared()/unlock_shared()/try_lock_for())
179 * LockTraits contains the following members variables:
180 * - static constexpr bool is_shared
181 * True if the lock supports separate shared vs exclusive locking states.
182 * - static constexpr bool is_timed
183 * True if the lock supports acquiring the lock with a timeout.
185 * The following static methods always exist:
186 * - lock(Mutex& mutex)
187 * - unlock(Mutex& mutex)
189 * The following static methods may exist, depending on is_shared and
191 * - try_lock_for(Mutex& mutex, <std_chrono_duration> timeout)
192 * - lock_shared(Mutex& mutex)
193 * - unlock_shared(Mutex& mutex)
194 * - try_lock_shared_for(Mutex& mutex, <std_chrono_duration> timeout)
196 template <class Mutex>
197 struct LockTraits : public detail::LockTraitsBase<
199 detail::LockTraitsImpl<Mutex>::has_lock_shared,
200 detail::LockTraitsImpl<Mutex>::has_timed_lock> {};
203 * If the lock is a shared lock, acquire it in shared mode.
204 * Otherwise, for plain (exclusive-only) locks, perform a normal acquire.
206 template <class Mutex>
207 typename std::enable_if<LockTraits<Mutex>::is_shared>::type
208 lock_shared_or_unique(Mutex& mutex) {
209 LockTraits<Mutex>::lock_shared(mutex);
211 template <class Mutex>
212 typename std::enable_if<!LockTraits<Mutex>::is_shared>::type
213 lock_shared_or_unique(Mutex& mutex) {
214 LockTraits<Mutex>::lock(mutex);
218 * If the lock is a shared lock, try to acquire it in shared mode, for up to
219 * the given timeout. Otherwise, for plain (exclusive-only) locks, try to
220 * perform a normal acquire.
222 * Returns true if the lock was acquired, or false on time out.
224 template <class Mutex, class Rep, class Period>
225 typename std::enable_if<LockTraits<Mutex>::is_shared, bool>::type
226 try_lock_shared_or_unique_for(
228 const std::chrono::duration<Rep, Period>& timeout) {
229 return LockTraits<Mutex>::try_lock_shared_for(mutex, timeout);
231 template <class Mutex, class Rep, class Period>
232 typename std::enable_if<!LockTraits<Mutex>::is_shared, bool>::type
233 try_lock_shared_or_unique_for(
235 const std::chrono::duration<Rep, Period>& timeout) {
236 return LockTraits<Mutex>::try_lock_for(mutex, timeout);
240 * Release a lock acquired with lock_shared_or_unique()
242 template <class Mutex>
243 typename std::enable_if<LockTraits<Mutex>::is_shared>::type
244 unlock_shared_or_unique(Mutex& mutex) {
245 LockTraits<Mutex>::unlock_shared(mutex);
247 template <class Mutex>
248 typename std::enable_if<!LockTraits<Mutex>::is_shared>::type
249 unlock_shared_or_unique(Mutex& mutex) {
250 LockTraits<Mutex>::unlock(mutex);
254 * Lock policy classes.
256 * These can be used as template parameters to provide compile-time
257 * selection over the type of lock operation to perform.
261 * A lock policy that performs exclusive lock operations.
263 class LockPolicyExclusive {
265 template <class Mutex>
266 static void lock(Mutex& mutex) {
267 LockTraits<Mutex>::lock(mutex);
269 template <class Mutex, class Rep, class Period>
270 static bool try_lock_for(
272 const std::chrono::duration<Rep, Period>& timeout) {
273 return LockTraits<Mutex>::try_lock_for(mutex, timeout);
275 template <class Mutex>
276 static void unlock(Mutex& mutex) {
277 LockTraits<Mutex>::unlock(mutex);
282 * A lock policy that performs shared lock operations.
283 * This policy only works with shared mutex types.
285 class LockPolicyShared {
287 template <class Mutex>
288 static void lock(Mutex& mutex) {
289 LockTraits<Mutex>::lock_shared(mutex);
291 template <class Mutex, class Rep, class Period>
292 static bool try_lock_for(
294 const std::chrono::duration<Rep, Period>& timeout) {
295 return LockTraits<Mutex>::try_lock_shared_for(mutex, timeout);
297 template <class Mutex>
298 static void unlock(Mutex& mutex) {
299 LockTraits<Mutex>::unlock_shared(mutex);
304 * A lock policy that performs a shared lock operation if a shared mutex type
305 * is given, or a normal exclusive lock operation on non-shared mutex types.
307 class LockPolicyShareable {
309 template <class Mutex>
310 static void lock(Mutex& mutex) {
311 lock_shared_or_unique(mutex);
313 template <class Mutex, class Rep, class Period>
314 static bool try_lock_for(
316 const std::chrono::duration<Rep, Period>& timeout) {
317 return try_lock_shared_or_unique_for(mutex, timeout);
319 template <class Mutex>
320 static void unlock(Mutex& mutex) {
321 unlock_shared_or_unique(mutex);