1/*
2 * Copyright 2016-present 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 <mutex>
20
21#include <folly/SharedMutex.h>
22#include <folly/SpinLock.h>
23#include <folly/portability/GTest.h>
24#include <folly/synchronization/RWSpinLock.h>
25
26using namespace folly;
27
28static constexpr auto one_ms = std::chrono::milliseconds(1);
29
30/**
31 * Test mutex to help to automate assertions
32 */
33class FakeAllPowerfulAssertingMutex {
34 public:
35 enum class CurrentLockState { UNLOCKED, SHARED, UPGRADE, UNIQUE };
36
37 void lock() {
38 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
39 this->lock_state = CurrentLockState::UNIQUE;
40 }
41 void unlock() {
42 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
43 this->lock_state = CurrentLockState::UNLOCKED;
44 }
45 void lock_shared() {
46 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
47 this->lock_state = CurrentLockState::SHARED;
48 }
49 void unlock_shared() {
50 EXPECT_EQ(this->lock_state, CurrentLockState::SHARED);
51 this->lock_state = CurrentLockState::UNLOCKED;
52 }
53 void lock_upgrade() {
54 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
55 this->lock_state = CurrentLockState::UPGRADE;
56 }
57 void unlock_upgrade() {
58 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
59 this->lock_state = CurrentLockState::UNLOCKED;
60 }
61
62 void unlock_upgrade_and_lock() {
63 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
64 this->lock_state = CurrentLockState::UNIQUE;
65 }
66 void unlock_and_lock_upgrade() {
67 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
68 this->lock_state = CurrentLockState::UPGRADE;
69 }
70 void unlock_and_lock_shared() {
71 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
72 this->lock_state = CurrentLockState::SHARED;
73 }
74 void unlock_upgrade_and_lock_shared() {
75 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
76 this->lock_state = CurrentLockState::SHARED;
77 }
78
79 template <class Rep, class Period>
80 bool try_lock_for(const std::chrono::duration<Rep, Period>&) {
81 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
82 this->lock_state = CurrentLockState::UNIQUE;
83 return true;
84 }
85
86 template <class Rep, class Period>
87 bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>&) {
88 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
89 this->lock_state = CurrentLockState::UPGRADE;
90 return true;
91 }
92
93 template <class Rep, class Period>
94 bool try_unlock_upgrade_and_lock_for(
95 const std::chrono::duration<Rep, Period>&) {
96 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
97 this->lock_state = CurrentLockState::UNIQUE;
98 return true;
99 }
100
101 /*
102 * Initialize the FakeMutex with an unlocked state
103 */
104 CurrentLockState lock_state{CurrentLockState::UNLOCKED};
105};
106
107TEST(LockTraits, std_mutex) {
108 using traits = LockTraits<std::mutex>;
109 static_assert(!traits::is_timed, "std:mutex is not a timed lock");
110 static_assert(!traits::is_shared, "std:mutex is not a shared lock");
111 static_assert(!traits::is_upgrade, "std::mutex is not an upgradable lock");
112
113 std::mutex mutex;
114 traits::lock(mutex);
115 traits::unlock(mutex);
116}
117
118TEST(LockTraits, SharedMutex) {
119 using traits = LockTraits<SharedMutex>;
120 static_assert(traits::is_timed, "folly::SharedMutex is a timed lock");
121 static_assert(traits::is_shared, "folly::SharedMutex is a shared lock");
122 static_assert(traits::is_upgrade, "folly::SharedMutex is an upgradable lock");
123
124 SharedMutex mutex;
125 traits::lock(mutex);
126 traits::unlock(mutex);
127
128 traits::lock_shared(mutex);
129 traits::lock_shared(mutex);
130 traits::unlock_shared(mutex);
131 traits::unlock_shared(mutex);
132
133 traits::lock_upgrade(mutex);
134 traits::unlock_upgrade(mutex);
135
136 // test upgrade and downgrades
137 traits::lock_upgrade(mutex);
138 traits::unlock_upgrade_and_lock(mutex);
139 bool gotLock = traits::try_lock_for(mutex, one_ms);
140 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
141 "lock after upgrading to an exclusive lock";
142 gotLock = traits::try_lock_upgrade_for(mutex, one_ms);
143 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an upgrade "
144 "lock after upgrading to an exclusive lock";
145 gotLock = traits::try_lock_shared_for(mutex, one_ms);
146 EXPECT_FALSE(gotLock) << "Should not have been able to acquire a shared "
147 "lock after upgrading to an exclusive lock";
148 traits::unlock(mutex);
149
150 traits::lock_upgrade(mutex);
151 traits::unlock_upgrade_and_lock_shared(mutex);
152 gotLock = traits::try_lock_for(mutex, one_ms);
153 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
154 "mutex after downgrading from an upgrade to a "
155 "shared lock";
156 traits::unlock_shared(mutex);
157
158 traits::lock(mutex);
159 gotLock = traits::try_lock_for(mutex, one_ms);
160 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
161 "lock after acquiring an exclusive lock";
162 gotLock = traits::try_lock_upgrade_for(mutex, one_ms);
163 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an upgrade "
164 "lock after acquiring an exclusive lock";
165 gotLock = traits::try_lock_shared_for(mutex, one_ms);
166 EXPECT_FALSE(gotLock) << "Should not have been able to acquire a shared "
167 "lock after acquiring an exclusive lock";
168 traits::unlock_and_lock_upgrade(mutex);
169 gotLock = traits::try_lock_for(mutex, one_ms);
170 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
171 "lock after downgrading to an upgrade lock";
172 traits::unlock_upgrade(mutex);
173
174 traits::lock(mutex);
175 traits::unlock_and_lock_shared(mutex);
176 gotLock = traits::try_lock_for(mutex, one_ms);
177 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
178 "lock after downgrading to a shared lock";
179 traits::unlock_shared(mutex);
180}
181
182TEST(LockTraits, SpinLock) {
183 using traits = LockTraits<SpinLock>;
184 static_assert(!traits::is_timed, "folly::SpinLock is not a timed lock");
185 static_assert(!traits::is_shared, "folly::SpinLock is not a shared lock");
186 static_assert(
187 !traits::is_upgrade, "folly::SpinLock is not an upgradable lock");
188
189 SpinLock mutex;
190 traits::lock(mutex);
191 traits::unlock(mutex);
192}
193
194TEST(LockTraits, RWSpinLock) {
195 using traits = LockTraits<RWSpinLock>;
196 static_assert(!traits::is_timed, "folly::RWSpinLock is not a timed lock");
197 static_assert(traits::is_shared, "folly::RWSpinLock is a shared lock");
198 static_assert(traits::is_upgrade, "folly::RWSpinLock is an upgradable lock");
199
200 RWSpinLock mutex;
201 traits::lock(mutex);
202 traits::unlock(mutex);
203
204 traits::lock_shared(mutex);
205 traits::lock_shared(mutex);
206 traits::unlock_shared(mutex);
207 traits::unlock_shared(mutex);
208}
209
210TEST(LockTraits, boost_mutex) {
211 using traits = LockTraits<boost::mutex>;
212 static_assert(!traits::is_timed, "boost::mutex is not a timed lock");
213 static_assert(!traits::is_shared, "boost::mutex is not a shared lock");
214 static_assert(!traits::is_upgrade, "boost::mutex is not an upgradable lock");
215
216 boost::mutex mutex;
217 traits::lock(mutex);
218 traits::unlock(mutex);
219}
220
221TEST(LockTraits, boost_recursive_mutex) {
222 using traits = LockTraits<boost::recursive_mutex>;
223 static_assert(
224 !traits::is_timed, "boost::recursive_mutex is not a timed lock");
225 static_assert(
226 !traits::is_shared, "boost::recursive_mutex is not a shared lock");
227 static_assert(
228 !traits::is_upgrade, "boost::recursive_mutex is not an upgradable lock");
229
230 boost::recursive_mutex mutex;
231 traits::lock(mutex);
232 traits::lock(mutex);
233 traits::unlock(mutex);
234 traits::unlock(mutex);
235}
236
237#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
238TEST(LockTraits, timed_mutex) {
239 using traits = LockTraits<std::timed_mutex>;
240 static_assert(traits::is_timed, "std::timed_mutex is a timed lock");
241 static_assert(!traits::is_shared, "std::timed_mutex is not a shared lock");
242 static_assert(
243 !traits::is_upgrade, "std::timed_mutex is not an upgradable lock");
244
245 std::timed_mutex mutex;
246 traits::lock(mutex);
247 bool gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
248 EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
249 << "timed_mutex a second time";
250 traits::unlock(mutex);
251}
252
253TEST(LockTraits, recursive_timed_mutex) {
254 using traits = LockTraits<std::recursive_timed_mutex>;
255 static_assert(traits::is_timed, "std::recursive_timed_mutex is a timed lock");
256 static_assert(
257 !traits::is_shared, "std::recursive_timed_mutex is not a shared lock");
258 static_assert(
259 !traits::is_upgrade,
260 "std::recursive_timed_mutex is not an upgradable lock");
261
262 std::recursive_timed_mutex mutex;
263 traits::lock(mutex);
264 auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(10));
265 EXPECT_TRUE(gotLock) << "should have been able to acquire the "
266 << "recursive_timed_mutex a second time";
267 traits::unlock(mutex);
268 traits::unlock(mutex);
269}
270
271TEST(LockTraits, boost_shared_mutex) {
272 using traits = LockTraits<boost::shared_mutex>;
273 static_assert(traits::is_timed, "boost::shared_mutex is a timed lock");
274 static_assert(traits::is_shared, "boost::shared_mutex is a shared lock");
275 static_assert(
276 traits::is_upgrade, "boost::shared_mutex is an upgradable lock");
277
278 boost::shared_mutex mutex;
279 traits::lock(mutex);
280 auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
281 EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
282 << "shared_mutex a second time";
283 gotLock = traits::try_lock_shared_for(mutex, std::chrono::milliseconds(1));
284 EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
285 << "shared_mutex a second time";
286 traits::unlock(mutex);
287
288 traits::lock_shared(mutex);
289 gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
290 EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
291 << "shared_mutex a second time";
292 gotLock = traits::try_lock_shared_for(mutex, std::chrono::milliseconds(10));
293 EXPECT_TRUE(gotLock) << "should have been able to acquire the "
294 << "shared_mutex a second time in shared mode";
295 traits::unlock_shared(mutex);
296 traits::unlock_shared(mutex);
297}
298#endif // FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
299
300/**
301 * Chain the asserts from the previous test to the next lock, unlock or
302 * upgrade method calls. Each making sure that the previous was correct.
303 */
304TEST(LockTraits, LockPolicy) {
305 using Mutex = FakeAllPowerfulAssertingMutex;
306 Mutex mutex;
307
308 // test the lock and unlock functions
309 LockPolicyUpgrade::lock(mutex);
310 mutex.unlock_upgrade();
311 mutex.lock_upgrade();
312 LockPolicyUpgrade::unlock(mutex);
313
314 mutex.lock_upgrade();
315 LockPolicyFromUpgradeToExclusive::lock(mutex);
316 mutex.unlock();
317 mutex.lock();
318 LockPolicyFromUpgradeToExclusive::unlock(mutex);
319
320 mutex.lock();
321 LockPolicyFromExclusiveToUpgrade::lock(mutex);
322 mutex.unlock_upgrade();
323 mutex.lock_upgrade();
324 LockPolicyFromExclusiveToUpgrade::unlock(mutex);
325
326 mutex.lock_upgrade();
327 LockPolicyFromUpgradeToShared::lock(mutex);
328 mutex.unlock_shared();
329 mutex.lock_shared();
330 LockPolicyFromUpgradeToShared::unlock(mutex);
331
332 mutex.lock();
333 LockPolicyFromExclusiveToShared::lock(mutex);
334 mutex.unlock_shared();
335 mutex.lock_shared();
336 LockPolicyFromExclusiveToShared::unlock(mutex);
337
338 EXPECT_EQ(mutex.lock_state, Mutex::CurrentLockState::UNLOCKED);
339}
340
341/**
342 * Similar to the test above but tests the timed version of the updates
343 */
344TEST(LockTraits, LockPolicyTimed) {
345 using Mutex = FakeAllPowerfulAssertingMutex;
346 Mutex mutex;
347
348 bool gotLock = LockPolicyUpgrade::try_lock_for(mutex, one_ms);
349 EXPECT_TRUE(gotLock) << "Should have been able to acquire the fake mutex";
350 LockPolicyUpgrade::unlock(mutex);
351
352 mutex.lock_upgrade();
353 gotLock = LockPolicyFromUpgradeToExclusive::try_lock_for(mutex, one_ms);
354 EXPECT_TRUE(gotLock)
355 << "Should have been able to upgrade from upgrade to unique";
356 mutex.unlock();
357
358 mutex.lock();
359 gotLock = LockPolicyFromExclusiveToUpgrade::try_lock_for(mutex, one_ms);
360 EXPECT_TRUE(gotLock) << "Should have been able to downgrade from exclusive "
361 "to upgrade";
362 mutex.unlock_upgrade();
363
364 mutex.lock_upgrade();
365 gotLock = LockPolicyFromUpgradeToShared::try_lock_for(mutex, one_ms);
366 EXPECT_TRUE(gotLock) << "Should have been able to downgrade from upgrade to "
367 "shared";
368 mutex.unlock_shared();
369
370 mutex.lock();
371 gotLock = LockPolicyFromExclusiveToShared::try_lock_for(mutex, one_ms);
372 EXPECT_TRUE(gotLock) << "Should have been able to downgrade from exclusive "
373 "to shared";
374 mutex.unlock_shared();
375}
376
377/**
378 * Test compatibility of the different lock policies
379 *
380 * This should be correct because the compatibilities here are used to
381 * determine whether or not the different LockedPtr instances can be moved
382 * from each other
383 */
384TEST(LockTraits, LockPolicyCompatibilities) {
385 EXPECT_TRUE((std::is_same<
386 LockPolicyExclusive::UnlockPolicy,
387 LockPolicyTryExclusive::UnlockPolicy>::value));
388 EXPECT_TRUE((std::is_same<
389 LockPolicyExclusive::UnlockPolicy,
390 LockPolicyFromUpgradeToExclusive::UnlockPolicy>::value));
391
392 EXPECT_TRUE((std::is_same<
393 LockPolicyShared::UnlockPolicy,
394 LockPolicyTryShared::UnlockPolicy>::value));
395 EXPECT_TRUE((std::is_same<
396 LockPolicyShared::UnlockPolicy,
397 LockPolicyFromUpgradeToShared::UnlockPolicy>::value));
398 EXPECT_TRUE((std::is_same<
399 LockPolicyShared::UnlockPolicy,
400 LockPolicyFromExclusiveToShared::UnlockPolicy>::value));
401
402 EXPECT_TRUE((std::is_same<
403 LockPolicyUpgrade::UnlockPolicy,
404 LockPolicyTryUpgrade::UnlockPolicy>::value));
405 EXPECT_TRUE((std::is_same<
406 LockPolicyUpgrade::UnlockPolicy,
407 LockPolicyFromExclusiveToUpgrade::UnlockPolicy>::value));
408}
409