Adding support for upgradable mutexes to LockTraits
[folly.git] / folly / test / LockTraitsTest.cpp
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 #include <folly/LockTraits.h>
17 #include <folly/LockTraitsBoost.h>
18
19 #include <gtest/gtest.h>
20 #include <mutex>
21
22 #include <folly/RWSpinLock.h>
23 #include <folly/SharedMutex.h>
24 #include <folly/SpinLock.h>
25
26 using namespace folly;
27
28 static constexpr auto one_ms = std::chrono::milliseconds(1);
29
30 TEST(LockTraits, std_mutex) {
31   using traits = LockTraits<std::mutex>;
32   static_assert(!traits::is_timed, "std:mutex is not a timed lock");
33   static_assert(!traits::is_shared, "std:mutex is not a shared lock");
34   static_assert(!traits::is_upgrade, "std::mutex is not an upgradable lock");
35
36   std::mutex mutex;
37   traits::lock(mutex);
38   traits::unlock(mutex);
39
40   lock_shared_or_unique(mutex);
41   unlock_shared_or_unique(mutex);
42 }
43
44 TEST(LockTraits, SharedMutex) {
45   using traits = LockTraits<SharedMutex>;
46   static_assert(traits::is_timed, "folly::SharedMutex is a timed lock");
47   static_assert(traits::is_shared, "folly::SharedMutex is a shared lock");
48   static_assert(traits::is_upgrade, "folly::SharedMutex is an upgradable lock");
49
50   SharedMutex mutex;
51   traits::lock(mutex);
52   traits::unlock(mutex);
53
54   traits::lock_shared(mutex);
55   traits::lock_shared(mutex);
56   traits::unlock_shared(mutex);
57   traits::unlock_shared(mutex);
58
59   lock_shared_or_unique(mutex);
60   lock_shared_or_unique(mutex);
61   unlock_shared_or_unique(mutex);
62   unlock_shared_or_unique(mutex);
63
64   traits::lock_upgrade(mutex);
65   traits::unlock_upgrade(mutex);
66
67   // test upgrade and downgrades
68   traits::lock_upgrade(mutex);
69   traits::unlock_upgrade_and_lock(mutex);
70   bool gotLock = traits::try_lock_for(mutex, one_ms);
71   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
72                            "lock after upgrading to an exclusive lock";
73   gotLock = traits::try_lock_upgrade_for(mutex, one_ms);
74   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an upgrade "
75                            "lock after upgrading to an exclusive lock";
76   gotLock = traits::try_lock_shared_for(mutex, one_ms);
77   EXPECT_FALSE(gotLock) << "Should not have been able to acquire a shared "
78                            "lock after upgrading to an exclusive lock";
79   traits::unlock(mutex);
80
81   traits::lock_upgrade(mutex);
82   traits::unlock_upgrade_and_lock_shared(mutex);
83   gotLock = traits::try_lock_for(mutex, one_ms);
84   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
85                            "mutex after downgrading from an upgrade to a "
86                            "shared lock";
87   traits::unlock_shared(mutex);
88
89   traits::lock(mutex);
90   gotLock = traits::try_lock_for(mutex, one_ms);
91   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
92                            "lock after acquiring an exclusive lock";
93   gotLock = traits::try_lock_upgrade_for(mutex, one_ms);
94   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an upgrade "
95                            "lock after acquiring an exclusive lock";
96   gotLock = traits::try_lock_shared_for(mutex, one_ms);
97   EXPECT_FALSE(gotLock) << "Should not have been able to acquire a shared "
98                            "lock after acquiring an exclusive lock";
99   traits::unlock_and_lock_upgrade(mutex);
100   gotLock = traits::try_lock_for(mutex, one_ms);
101   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
102                            "lock after downgrading to an upgrade lock";
103   traits::unlock_upgrade(mutex);
104
105   traits::lock(mutex);
106   traits::unlock_and_lock_shared(mutex);
107   gotLock = traits::try_lock_for(mutex, one_ms);
108   EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
109                            "lock after downgrading to a shared lock";
110   traits::unlock_shared(mutex);
111 }
112
113 TEST(LockTraits, SpinLock) {
114   using traits = LockTraits<SpinLock>;
115   static_assert(!traits::is_timed, "folly::SpinLock is not a timed lock");
116   static_assert(!traits::is_shared, "folly::SpinLock is not a shared lock");
117   static_assert(
118       !traits::is_upgrade, "folly::SpinLock is not an upgradable lock");
119
120   SpinLock mutex;
121   traits::lock(mutex);
122   traits::unlock(mutex);
123
124   lock_shared_or_unique(mutex);
125   unlock_shared_or_unique(mutex);
126 }
127
128 TEST(LockTraits, RWSpinLock) {
129   using traits = LockTraits<RWSpinLock>;
130   static_assert(!traits::is_timed, "folly::RWSpinLock is not a timed lock");
131   static_assert(traits::is_shared, "folly::RWSpinLock is a shared lock");
132   static_assert(traits::is_upgrade, "folly::RWSpinLock is an upgradable lock");
133
134   RWSpinLock mutex;
135   traits::lock(mutex);
136   traits::unlock(mutex);
137
138   traits::lock_shared(mutex);
139   traits::lock_shared(mutex);
140   traits::unlock_shared(mutex);
141   traits::unlock_shared(mutex);
142
143   lock_shared_or_unique(mutex);
144   lock_shared_or_unique(mutex);
145   unlock_shared_or_unique(mutex);
146   unlock_shared_or_unique(mutex);
147 }
148
149 TEST(LockTraits, boost_mutex) {
150   using traits = LockTraits<boost::mutex>;
151   static_assert(!traits::is_timed, "boost::mutex is not a timed lock");
152   static_assert(!traits::is_shared, "boost::mutex is not a shared lock");
153   static_assert(!traits::is_upgrade, "boost::mutex is not an upgradable lock");
154
155   boost::mutex mutex;
156   traits::lock(mutex);
157   traits::unlock(mutex);
158
159   lock_shared_or_unique(mutex);
160   unlock_shared_or_unique(mutex);
161 }
162
163 TEST(LockTraits, boost_recursive_mutex) {
164   using traits = LockTraits<boost::recursive_mutex>;
165   static_assert(
166       !traits::is_timed, "boost::recursive_mutex is not a timed lock");
167   static_assert(
168       !traits::is_shared, "boost::recursive_mutex is not a shared lock");
169   static_assert(
170       !traits::is_upgrade, "boost::recursive_mutex is not an upgradable lock");
171
172   boost::recursive_mutex mutex;
173   traits::lock(mutex);
174   traits::lock(mutex);
175   traits::unlock(mutex);
176   traits::unlock(mutex);
177
178   lock_shared_or_unique(mutex);
179   lock_shared_or_unique(mutex);
180   unlock_shared_or_unique(mutex);
181   unlock_shared_or_unique(mutex);
182 }
183
184 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
185 TEST(LockTraits, timed_mutex) {
186   using traits = LockTraits<std::timed_mutex>;
187   static_assert(traits::is_timed, "std::timed_mutex is a timed lock");
188   static_assert(!traits::is_shared, "std::timed_mutex is not a shared lock");
189   static_assert(
190       !traits::is_upgrade, "std::timed_mutex is not an upgradable lock");
191
192   std::timed_mutex mutex;
193   traits::lock(mutex);
194   bool gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
195   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
196                         << "timed_mutex a second time";
197   traits::unlock(mutex);
198
199   lock_shared_or_unique(mutex);
200   gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(1));
201   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
202                         << "timed_mutex a second time";
203   unlock_shared_or_unique(mutex);
204 }
205
206 TEST(LockTraits, recursive_timed_mutex) {
207   using traits = LockTraits<std::recursive_timed_mutex>;
208   static_assert(traits::is_timed, "std::recursive_timed_mutex is a timed lock");
209   static_assert(
210       !traits::is_shared, "std::recursive_timed_mutex is not a shared lock");
211   static_assert(
212       !traits::is_upgrade,
213       "std::recursive_timed_mutex is not an upgradable lock");
214
215   std::recursive_timed_mutex mutex;
216   traits::lock(mutex);
217   auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(10));
218   EXPECT_TRUE(gotLock) << "should have been able to acquire the "
219                        << "recursive_timed_mutex a second time";
220   traits::unlock(mutex);
221   traits::unlock(mutex);
222
223   lock_shared_or_unique(mutex);
224   gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(10));
225   EXPECT_TRUE(gotLock) << "should have been able to acquire the "
226                        << "recursive_timed_mutex a second time";
227   unlock_shared_or_unique(mutex);
228   unlock_shared_or_unique(mutex);
229 }
230
231 TEST(LockTraits, boost_shared_mutex) {
232   using traits = LockTraits<boost::shared_mutex>;
233   static_assert(traits::is_timed, "boost::shared_mutex is a timed lock");
234   static_assert(traits::is_shared, "boost::shared_mutex is a shared lock");
235   static_assert(
236       traits::is_upgrade, "boost::shared_mutex is an upgradable lock");
237
238   boost::shared_mutex mutex;
239   traits::lock(mutex);
240   auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
241   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
242                         << "shared_mutex a second time";
243   gotLock = traits::try_lock_shared_for(mutex, std::chrono::milliseconds(1));
244   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
245                         << "shared_mutex a second time";
246   traits::unlock(mutex);
247
248   traits::lock_shared(mutex);
249   gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
250   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
251                         << "shared_mutex a second time";
252   gotLock = traits::try_lock_shared_for(mutex, std::chrono::milliseconds(10));
253   EXPECT_TRUE(gotLock) << "should have been able to acquire the "
254                        << "shared_mutex a second time in shared mode";
255   traits::unlock_shared(mutex);
256   traits::unlock_shared(mutex);
257
258   lock_shared_or_unique(mutex);
259   gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
260   EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
261                         << "shared_mutex a second time";
262   gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(10));
263   EXPECT_TRUE(gotLock) << "should have been able to acquire the "
264                        << "shared_mutex a second time in shared mode";
265   unlock_shared_or_unique(mutex);
266   unlock_shared_or_unique(mutex);
267 }
268 #endif // FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES