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.
16 #include <folly/LockTraits.h>
17 #include <folly/LockTraitsBoost.h>
21 #include <folly/RWSpinLock.h>
22 #include <folly/SharedMutex.h>
23 #include <folly/SpinLock.h>
24 #include <folly/portability/GTest.h>
26 using namespace folly;
28 static constexpr auto one_ms = std::chrono::milliseconds(1);
31 * Test mutex to help to automate assertions
33 class FakeAllPowerfulAssertingMutex {
35 enum class CurrentLockState { UNLOCKED, SHARED, UPGRADE, UNIQUE };
38 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
39 this->lock_state = CurrentLockState::UNIQUE;
42 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
43 this->lock_state = CurrentLockState::UNLOCKED;
46 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
47 this->lock_state = CurrentLockState::SHARED;
49 void unlock_shared() {
50 EXPECT_EQ(this->lock_state, CurrentLockState::SHARED);
51 this->lock_state = CurrentLockState::UNLOCKED;
54 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
55 this->lock_state = CurrentLockState::UPGRADE;
57 void unlock_upgrade() {
58 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
59 this->lock_state = CurrentLockState::UNLOCKED;
62 void unlock_upgrade_and_lock() {
63 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
64 this->lock_state = CurrentLockState::UNIQUE;
66 void unlock_and_lock_upgrade() {
67 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
68 this->lock_state = CurrentLockState::UPGRADE;
70 void unlock_and_lock_shared() {
71 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
72 this->lock_state = CurrentLockState::SHARED;
74 void unlock_upgrade_and_lock_shared() {
75 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
76 this->lock_state = CurrentLockState::SHARED;
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;
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;
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;
102 * Initialize the FakeMutex with an unlocked state
104 CurrentLockState lock_state{CurrentLockState::UNLOCKED};
107 TEST(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");
115 traits::unlock(mutex);
117 lock_shared_or_unique(mutex);
118 unlock_shared_or_unique(mutex);
121 TEST(LockTraits, SharedMutex) {
122 using traits = LockTraits<SharedMutex>;
123 static_assert(traits::is_timed, "folly::SharedMutex is a timed lock");
124 static_assert(traits::is_shared, "folly::SharedMutex is a shared lock");
125 static_assert(traits::is_upgrade, "folly::SharedMutex is an upgradable lock");
129 traits::unlock(mutex);
131 traits::lock_shared(mutex);
132 traits::lock_shared(mutex);
133 traits::unlock_shared(mutex);
134 traits::unlock_shared(mutex);
136 lock_shared_or_unique(mutex);
137 lock_shared_or_unique(mutex);
138 unlock_shared_or_unique(mutex);
139 unlock_shared_or_unique(mutex);
141 traits::lock_upgrade(mutex);
142 traits::unlock_upgrade(mutex);
144 // test upgrade and downgrades
145 traits::lock_upgrade(mutex);
146 traits::unlock_upgrade_and_lock(mutex);
147 bool gotLock = traits::try_lock_for(mutex, one_ms);
148 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
149 "lock after upgrading to an exclusive lock";
150 gotLock = traits::try_lock_upgrade_for(mutex, one_ms);
151 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an upgrade "
152 "lock after upgrading to an exclusive lock";
153 gotLock = traits::try_lock_shared_for(mutex, one_ms);
154 EXPECT_FALSE(gotLock) << "Should not have been able to acquire a shared "
155 "lock after upgrading to an exclusive lock";
156 traits::unlock(mutex);
158 traits::lock_upgrade(mutex);
159 traits::unlock_upgrade_and_lock_shared(mutex);
160 gotLock = traits::try_lock_for(mutex, one_ms);
161 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
162 "mutex after downgrading from an upgrade to a "
164 traits::unlock_shared(mutex);
167 gotLock = traits::try_lock_for(mutex, one_ms);
168 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
169 "lock after acquiring an exclusive lock";
170 gotLock = traits::try_lock_upgrade_for(mutex, one_ms);
171 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an upgrade "
172 "lock after acquiring an exclusive lock";
173 gotLock = traits::try_lock_shared_for(mutex, one_ms);
174 EXPECT_FALSE(gotLock) << "Should not have been able to acquire a shared "
175 "lock after acquiring an exclusive lock";
176 traits::unlock_and_lock_upgrade(mutex);
177 gotLock = traits::try_lock_for(mutex, one_ms);
178 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
179 "lock after downgrading to an upgrade lock";
180 traits::unlock_upgrade(mutex);
183 traits::unlock_and_lock_shared(mutex);
184 gotLock = traits::try_lock_for(mutex, one_ms);
185 EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
186 "lock after downgrading to a shared lock";
187 traits::unlock_shared(mutex);
190 TEST(LockTraits, SpinLock) {
191 using traits = LockTraits<SpinLock>;
192 static_assert(!traits::is_timed, "folly::SpinLock is not a timed lock");
193 static_assert(!traits::is_shared, "folly::SpinLock is not a shared lock");
195 !traits::is_upgrade, "folly::SpinLock is not an upgradable lock");
199 traits::unlock(mutex);
201 lock_shared_or_unique(mutex);
202 unlock_shared_or_unique(mutex);
205 TEST(LockTraits, RWSpinLock) {
206 using traits = LockTraits<RWSpinLock>;
207 static_assert(!traits::is_timed, "folly::RWSpinLock is not a timed lock");
208 static_assert(traits::is_shared, "folly::RWSpinLock is a shared lock");
209 static_assert(traits::is_upgrade, "folly::RWSpinLock is an upgradable lock");
213 traits::unlock(mutex);
215 traits::lock_shared(mutex);
216 traits::lock_shared(mutex);
217 traits::unlock_shared(mutex);
218 traits::unlock_shared(mutex);
220 lock_shared_or_unique(mutex);
221 lock_shared_or_unique(mutex);
222 unlock_shared_or_unique(mutex);
223 unlock_shared_or_unique(mutex);
226 TEST(LockTraits, boost_mutex) {
227 using traits = LockTraits<boost::mutex>;
228 static_assert(!traits::is_timed, "boost::mutex is not a timed lock");
229 static_assert(!traits::is_shared, "boost::mutex is not a shared lock");
230 static_assert(!traits::is_upgrade, "boost::mutex is not an upgradable lock");
234 traits::unlock(mutex);
236 lock_shared_or_unique(mutex);
237 unlock_shared_or_unique(mutex);
240 TEST(LockTraits, boost_recursive_mutex) {
241 using traits = LockTraits<boost::recursive_mutex>;
243 !traits::is_timed, "boost::recursive_mutex is not a timed lock");
245 !traits::is_shared, "boost::recursive_mutex is not a shared lock");
247 !traits::is_upgrade, "boost::recursive_mutex is not an upgradable lock");
249 boost::recursive_mutex mutex;
252 traits::unlock(mutex);
253 traits::unlock(mutex);
255 lock_shared_or_unique(mutex);
256 lock_shared_or_unique(mutex);
257 unlock_shared_or_unique(mutex);
258 unlock_shared_or_unique(mutex);
261 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
262 TEST(LockTraits, timed_mutex) {
263 using traits = LockTraits<std::timed_mutex>;
264 static_assert(traits::is_timed, "std::timed_mutex is a timed lock");
265 static_assert(!traits::is_shared, "std::timed_mutex is not a shared lock");
267 !traits::is_upgrade, "std::timed_mutex is not an upgradable lock");
269 std::timed_mutex mutex;
271 bool gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
272 EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
273 << "timed_mutex a second time";
274 traits::unlock(mutex);
276 lock_shared_or_unique(mutex);
277 gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(1));
278 EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
279 << "timed_mutex a second time";
280 unlock_shared_or_unique(mutex);
283 TEST(LockTraits, recursive_timed_mutex) {
284 using traits = LockTraits<std::recursive_timed_mutex>;
285 static_assert(traits::is_timed, "std::recursive_timed_mutex is a timed lock");
287 !traits::is_shared, "std::recursive_timed_mutex is not a shared lock");
290 "std::recursive_timed_mutex is not an upgradable lock");
292 std::recursive_timed_mutex mutex;
294 auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(10));
295 EXPECT_TRUE(gotLock) << "should have been able to acquire the "
296 << "recursive_timed_mutex a second time";
297 traits::unlock(mutex);
298 traits::unlock(mutex);
300 lock_shared_or_unique(mutex);
301 gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(10));
302 EXPECT_TRUE(gotLock) << "should have been able to acquire the "
303 << "recursive_timed_mutex a second time";
304 unlock_shared_or_unique(mutex);
305 unlock_shared_or_unique(mutex);
308 TEST(LockTraits, boost_shared_mutex) {
309 using traits = LockTraits<boost::shared_mutex>;
310 static_assert(traits::is_timed, "boost::shared_mutex is a timed lock");
311 static_assert(traits::is_shared, "boost::shared_mutex is a shared lock");
313 traits::is_upgrade, "boost::shared_mutex is an upgradable lock");
315 boost::shared_mutex mutex;
317 auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
318 EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
319 << "shared_mutex a second time";
320 gotLock = traits::try_lock_shared_for(mutex, std::chrono::milliseconds(1));
321 EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
322 << "shared_mutex a second time";
323 traits::unlock(mutex);
325 traits::lock_shared(mutex);
326 gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
327 EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
328 << "shared_mutex a second time";
329 gotLock = traits::try_lock_shared_for(mutex, std::chrono::milliseconds(10));
330 EXPECT_TRUE(gotLock) << "should have been able to acquire the "
331 << "shared_mutex a second time in shared mode";
332 traits::unlock_shared(mutex);
333 traits::unlock_shared(mutex);
335 lock_shared_or_unique(mutex);
336 gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
337 EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
338 << "shared_mutex a second time";
339 gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(10));
340 EXPECT_TRUE(gotLock) << "should have been able to acquire the "
341 << "shared_mutex a second time in shared mode";
342 unlock_shared_or_unique(mutex);
343 unlock_shared_or_unique(mutex);
345 #endif // FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
348 * Chain the asserts from the previous test to the next lock, unlock or
349 * upgrade method calls. Each making sure that the previous was correct.
351 TEST(LockTraits, LockPolicy) {
352 using Mutex = FakeAllPowerfulAssertingMutex;
355 // test the lock and unlock functions
356 LockPolicyUpgrade::lock(mutex);
357 mutex.unlock_upgrade();
358 mutex.lock_upgrade();
359 LockPolicyUpgrade::unlock(mutex);
361 mutex.lock_upgrade();
362 LockPolicyFromUpgradeToExclusive::lock(mutex);
365 LockPolicyFromUpgradeToExclusive::unlock(mutex);
368 LockPolicyFromExclusiveToUpgrade::lock(mutex);
369 mutex.unlock_upgrade();
370 mutex.lock_upgrade();
371 LockPolicyFromExclusiveToUpgrade::unlock(mutex);
373 mutex.lock_upgrade();
374 LockPolicyFromUpgradeToShared::lock(mutex);
375 mutex.unlock_shared();
377 LockPolicyFromUpgradeToShared::unlock(mutex);
380 LockPolicyFromExclusiveToShared::lock(mutex);
381 mutex.unlock_shared();
383 LockPolicyFromExclusiveToShared::unlock(mutex);
385 EXPECT_EQ(mutex.lock_state, Mutex::CurrentLockState::UNLOCKED);
389 * Similar to the test above but tests the timed version of the updates
391 TEST(LockTraits, LockPolicyTimed) {
392 using Mutex = FakeAllPowerfulAssertingMutex;
395 bool gotLock = LockPolicyUpgrade::try_lock_for(mutex, one_ms);
396 EXPECT_TRUE(gotLock) << "Should have been able to acquire the fake mutex";
397 LockPolicyUpgrade::unlock(mutex);
399 mutex.lock_upgrade();
400 gotLock = LockPolicyFromUpgradeToExclusive::try_lock_for(mutex, one_ms);
402 << "Should have been able to upgrade from upgrade to unique";
406 gotLock = LockPolicyFromExclusiveToUpgrade::try_lock_for(mutex, one_ms);
407 EXPECT_TRUE(gotLock) << "Should have been able to downgrade from exclusive "
409 mutex.unlock_upgrade();
411 mutex.lock_upgrade();
412 gotLock = LockPolicyFromUpgradeToShared::try_lock_for(mutex, one_ms);
413 EXPECT_TRUE(gotLock) << "Should have been able to downgrade from upgrade to "
415 mutex.unlock_shared();
418 gotLock = LockPolicyFromExclusiveToShared::try_lock_for(mutex, one_ms);
419 EXPECT_TRUE(gotLock) << "Should have been able to downgrade from exclusive "
421 mutex.unlock_shared();