3b0d21a25d61e0867171ad9f07e7c764c9981a77
[folly.git] / folly / LockTraits.h
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /**
18  * This module provides a traits class for describing properties about mutex
19  * classes.
20  *
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.
24  */
25 #pragma once
26
27 #include <chrono>
28 #include <type_traits>
29
30 // Android, OSX, and Cygwin don't have timed mutexes
31 #if defined(ANDROID) || defined(__ANDROID__) || defined(__APPLE__) || \
32     defined(__CYGWIN__)
33 #define FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES 0
34 #else
35 #define FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES 1
36 #endif
37
38 namespace folly {
39 namespace detail {
40
41 /**
42  * An internal helper class for identifying if a lock type supports
43  * lock_shared() and try_lock_for() methods.
44  */
45 template <class Mutex>
46 class LockTraitsImpl {
47  private:
48   // Helper functions for implementing the traits using SFINAE
49   template <class T>
50   static auto timed_lock_test(T*) -> typename std::is_same<
51       decltype(std::declval<T>().try_lock_for(std::chrono::milliseconds(0))),
52       bool>::type;
53   template <class T>
54   static std::false_type timed_lock_test(...);
55
56   template <class T>
57   static auto lock_shared_test(T*) -> typename std::
58       is_same<decltype(std::declval<T>().lock_shared()), void>::type;
59   template <class T>
60   static std::false_type lock_shared_test(...);
61
62  public:
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;
67 };
68
69 template <class Mutex>
70 struct LockTraitsUniqueBase {
71   /**
72    * Acquire the lock exclusively.
73    */
74   static void lock(Mutex& mutex) {
75     mutex.lock();
76   }
77
78   /**
79    * Release an exclusively-held lock.
80    */
81   static void unlock(Mutex& mutex) {
82     mutex.unlock();
83   }
84 };
85
86 template <class Mutex>
87 struct LockTraitsSharedBase : public LockTraitsUniqueBase<Mutex> {
88   /**
89    * Acquire the lock in shared (read) mode.
90    */
91   static void lock_shared(Mutex& mutex) {
92     mutex.lock_shared();
93   }
94
95   /**
96    * Release a lock held in shared mode.
97    */
98   static void unlock_shared(Mutex& mutex) {
99     mutex.unlock_shared();
100   }
101 };
102
103 template <class Mutex, bool is_shared, bool is_timed>
104 struct LockTraitsBase {};
105
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;
111 };
112
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;
117 };
118
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;
123
124   /**
125    * Acquire the lock exclusively, with a timeout.
126    *
127    * Returns true or false indicating if the lock was acquired or not.
128    */
129   template <class Rep, class Period>
130   static bool try_lock_for(
131       Mutex& mutex,
132       const std::chrono::duration<Rep, Period>& timeout) {
133     return mutex.try_lock_for(timeout);
134   }
135 };
136
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;
141
142   /**
143    * Acquire the lock exclusively, with a timeout.
144    *
145    * Returns true or false indicating if the lock was acquired or not.
146    */
147   template <class Rep, class Period>
148   static bool try_lock_for(
149       Mutex& mutex,
150       const std::chrono::duration<Rep, Period>& timeout) {
151     return mutex.try_lock_for(timeout);
152   }
153
154   /**
155    * Acquire the lock in shared (read) mode, with a timeout.
156    *
157    * Returns true or false indicating if the lock was acquired or not.
158    */
159   template <class Rep, class Period>
160   static bool try_lock_shared_for(
161       Mutex& mutex,
162       const std::chrono::duration<Rep, Period>& timeout) {
163     return mutex.try_lock_shared_for(timeout);
164   }
165 };
166 } // detail
167
168 /**
169  * LockTraits describes details about a particular mutex type.
170  *
171  * The default implementation automatically attempts to detect traits
172  * based on the presence of various member functions.
173  *
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())
177  *
178  *
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.
184  *
185  * The following static methods always exist:
186  * - lock(Mutex& mutex)
187  * - unlock(Mutex& mutex)
188  *
189  * The following static methods may exist, depending on is_shared and
190  * is_timed:
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)
195  */
196 template <class Mutex>
197 struct LockTraits : public detail::LockTraitsBase<
198                         Mutex,
199                         detail::LockTraitsImpl<Mutex>::has_lock_shared,
200                         detail::LockTraitsImpl<Mutex>::has_timed_lock> {};
201
202 /**
203  * If the lock is a shared lock, acquire it in shared mode.
204  * Otherwise, for plain (exclusive-only) locks, perform a normal acquire.
205  */
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);
210 }
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);
215 }
216
217 /**
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.
221  *
222  * Returns true if the lock was acquired, or false on time out.
223  */
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(
227     Mutex& mutex,
228     const std::chrono::duration<Rep, Period>& timeout) {
229   return LockTraits<Mutex>::try_lock_shared_for(mutex, timeout);
230 }
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(
234     Mutex& mutex,
235     const std::chrono::duration<Rep, Period>& timeout) {
236   return LockTraits<Mutex>::try_lock_for(mutex, timeout);
237 }
238
239 /**
240  * Release a lock acquired with lock_shared_or_unique()
241  */
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);
246 }
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);
251 }
252 } // folly