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.
17 // @author: Andrei Alexandrescu (aalexandre)
19 // Test bed for folly/Synchronized.h
21 #include <folly/LockTraitsBoost.h>
22 #include <folly/Portability.h>
23 #include <folly/RWSpinLock.h>
24 #include <folly/SharedMutex.h>
25 #include <folly/SpinLock.h>
26 #include <folly/Synchronized.h>
27 #include <folly/test/SynchronizedTestLib.h>
28 #include <folly/portability/GTest.h>
30 using namespace folly::sync_tests;
32 template <class Mutex>
33 class SynchronizedTest : public testing::Test {};
35 using SynchronizedTestTypes = testing::Types<
36 folly::SharedMutexReadPriority,
37 folly::SharedMutexWritePriority,
40 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
42 std::recursive_timed_mutex,
45 boost::recursive_mutex,
46 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
48 boost::recursive_timed_mutex,
50 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
51 folly::RWTicketSpinLock32,
52 folly::RWTicketSpinLock64,
56 TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes);
58 TYPED_TEST(SynchronizedTest, Basic) {
59 testBasic<TypeParam>();
62 TYPED_TEST(SynchronizedTest, WithLock) {
63 testWithLock<TypeParam>();
66 TYPED_TEST(SynchronizedTest, Unlock) {
67 testUnlock<TypeParam>();
70 TYPED_TEST(SynchronizedTest, Deprecated) {
71 testDeprecated<TypeParam>();
74 TYPED_TEST(SynchronizedTest, Concurrency) {
75 testConcurrency<TypeParam>();
78 TYPED_TEST(SynchronizedTest, AcquireLocked) {
79 testAcquireLocked<TypeParam>();
82 TYPED_TEST(SynchronizedTest, AcquireLockedWithConst) {
83 testAcquireLockedWithConst<TypeParam>();
86 TYPED_TEST(SynchronizedTest, DualLocking) {
87 testDualLocking<TypeParam>();
90 TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
91 testDualLockingWithConst<TypeParam>();
94 TYPED_TEST(SynchronizedTest, ConstCopy) {
95 testConstCopy<TypeParam>();
98 template <class Mutex>
99 class SynchronizedTimedTest : public testing::Test {};
101 using SynchronizedTimedTestTypes = testing::Types<
102 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
104 std::recursive_timed_mutex,
106 boost::recursive_timed_mutex,
109 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
110 folly::RWTicketSpinLock32,
111 folly::RWTicketSpinLock64,
113 folly::SharedMutexReadPriority,
114 folly::SharedMutexWritePriority>;
115 TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
117 TYPED_TEST(SynchronizedTimedTest, Timed) {
118 testTimed<TypeParam>();
121 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
122 testTimedSynchronized<TypeParam>();
125 template <class Mutex>
126 class SynchronizedTimedWithConstTest : public testing::Test {};
128 using SynchronizedTimedWithConstTestTypes = testing::Types<
129 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
132 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
133 folly::RWTicketSpinLock32,
134 folly::RWTicketSpinLock64,
136 folly::SharedMutexReadPriority,
137 folly::SharedMutexWritePriority>;
139 SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes);
141 TYPED_TEST(SynchronizedTimedWithConstTest, TimedShared) {
142 testTimedShared<TypeParam>();
145 TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
146 testTimedSynchronizedWithConst<TypeParam>();
149 TYPED_TEST(SynchronizedTest, InPlaceConstruction) {
150 testInPlaceConstruction<TypeParam>();
153 using CountPair = std::pair<int, int>;
154 // This class is specialized only to be uesed in SynchronizedLockTest
165 static CountPair getLockUnlockCount() {
166 return CountPair{lockCount_, unlockCount_};
169 static void resetLockUnlockCount() {
174 // Keep these two static for test access
175 // Keep them thread_local in case of tests are run in parallel within one
177 static FOLLY_TLS int lockCount_;
178 static FOLLY_TLS int unlockCount_;
180 FOLLY_TLS int FakeMutex::lockCount_{0};
181 FOLLY_TLS int FakeMutex::unlockCount_{0};
183 // SynchronizedLockTest is used to verify the correct lock unlock behavior
184 // happens per design
185 class SynchronizedLockTest : public testing::Test {
187 void SetUp() override {
188 FakeMutex::resetLockUnlockCount();
193 * Test mutex to help to automate assertions, taken from LockTraitsTest.cpp
195 class FakeAllPowerfulAssertingMutexInternal {
197 enum class CurrentLockState { UNLOCKED, SHARED, UPGRADE, UNIQUE };
200 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
201 this->lock_state = CurrentLockState::UNIQUE;
204 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
205 this->lock_state = CurrentLockState::UNLOCKED;
208 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
209 this->lock_state = CurrentLockState::SHARED;
211 void unlock_shared() {
212 EXPECT_EQ(this->lock_state, CurrentLockState::SHARED);
213 this->lock_state = CurrentLockState::UNLOCKED;
215 void lock_upgrade() {
216 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
217 this->lock_state = CurrentLockState::UPGRADE;
219 void unlock_upgrade() {
220 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
221 this->lock_state = CurrentLockState::UNLOCKED;
224 void unlock_upgrade_and_lock() {
225 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
226 this->lock_state = CurrentLockState::UNIQUE;
228 void unlock_and_lock_upgrade() {
229 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
230 this->lock_state = CurrentLockState::UPGRADE;
232 void unlock_and_lock_shared() {
233 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
234 this->lock_state = CurrentLockState::SHARED;
236 void unlock_upgrade_and_lock_shared() {
237 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
238 this->lock_state = CurrentLockState::SHARED;
241 template <class Rep, class Period>
242 bool try_lock_for(const std::chrono::duration<Rep, Period>&) {
243 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
244 this->lock_state = CurrentLockState::UNIQUE;
248 template <class Rep, class Period>
249 bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>&) {
250 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
251 this->lock_state = CurrentLockState::UPGRADE;
255 template <class Rep, class Period>
256 bool try_unlock_upgrade_and_lock_for(
257 const std::chrono::duration<Rep, Period>&) {
258 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
259 this->lock_state = CurrentLockState::UNIQUE;
264 * Initialize the FakeMutex with an unlocked state
266 CurrentLockState lock_state{CurrentLockState::UNLOCKED};
270 * The following works around the internal mutex for synchronized being
273 * This is horridly thread unsafe.
275 static FakeAllPowerfulAssertingMutexInternal globalAllPowerfulAssertingMutex;
277 class FakeAllPowerfulAssertingMutex {
280 globalAllPowerfulAssertingMutex.lock();
283 globalAllPowerfulAssertingMutex.unlock();
286 globalAllPowerfulAssertingMutex.lock_shared();
288 void unlock_shared() {
289 globalAllPowerfulAssertingMutex.unlock_shared();
291 void lock_upgrade() {
292 globalAllPowerfulAssertingMutex.lock_upgrade();
294 void unlock_upgrade() {
295 globalAllPowerfulAssertingMutex.unlock_upgrade();
298 void unlock_upgrade_and_lock() {
299 globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock();
301 void unlock_and_lock_upgrade() {
302 globalAllPowerfulAssertingMutex.unlock_and_lock_upgrade();
304 void unlock_and_lock_shared() {
305 globalAllPowerfulAssertingMutex.unlock_and_lock_shared();
307 void unlock_upgrade_and_lock_shared() {
308 globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock_shared();
311 template <class Rep, class Period>
312 bool try_lock_for(const std::chrono::duration<Rep, Period>& arg) {
313 return globalAllPowerfulAssertingMutex.try_lock_for(arg);
316 template <class Rep, class Period>
317 bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>& arg) {
318 return globalAllPowerfulAssertingMutex.try_lock_upgrade_for(arg);
321 template <class Rep, class Period>
322 bool try_unlock_upgrade_and_lock_for(
323 const std::chrono::duration<Rep, Period>& arg) {
324 return globalAllPowerfulAssertingMutex.try_unlock_upgrade_and_lock_for(arg);
327 // reset state on destruction
328 ~FakeAllPowerfulAssertingMutex() {
329 globalAllPowerfulAssertingMutex = FakeAllPowerfulAssertingMutexInternal{};
333 TEST_F(SynchronizedLockTest, TestCopyConstructibleValues) {
334 struct NonCopyConstructible {
335 NonCopyConstructible(const NonCopyConstructible&) = delete;
336 NonCopyConstructible& operator=(const NonCopyConstructible&) = delete;
338 struct CopyConstructible {};
339 EXPECT_FALSE(std::is_copy_constructible<
340 folly::Synchronized<NonCopyConstructible>>::value);
341 EXPECT_FALSE(std::is_copy_assignable<
342 folly::Synchronized<NonCopyConstructible>>::value);
343 EXPECT_TRUE(std::is_copy_constructible<
344 folly::Synchronized<CopyConstructible>>::value);
346 std::is_copy_assignable<folly::Synchronized<CopyConstructible>>::value);
349 TEST_F(SynchronizedLockTest, UpgradableLocking) {
350 folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
354 std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
355 "The ulock function was not well configured, blame aary@instagram.com");
358 auto ulock = sync.ulock();
360 globalAllPowerfulAssertingMutex.lock_state,
361 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
364 // should be unlocked here
366 globalAllPowerfulAssertingMutex.lock_state,
367 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
369 // test going from upgrade to exclusive
371 auto ulock = sync.ulock();
372 auto wlock = ulock.moveFromUpgradeToWrite();
373 EXPECT_EQ(static_cast<bool>(ulock), false);
375 globalAllPowerfulAssertingMutex.lock_state,
376 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
379 // should be unlocked here
381 globalAllPowerfulAssertingMutex.lock_state,
382 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
384 // test going from upgrade to shared
386 auto ulock = sync.ulock();
387 auto slock = ulock.moveFromUpgradeToRead();
388 EXPECT_EQ(static_cast<bool>(ulock), false);
390 globalAllPowerfulAssertingMutex.lock_state,
391 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
394 // should be unlocked here
396 globalAllPowerfulAssertingMutex.lock_state,
397 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
399 // test going from exclusive to upgrade
401 auto wlock = sync.wlock();
402 auto ulock = wlock.moveFromWriteToUpgrade();
403 EXPECT_EQ(static_cast<bool>(wlock), false);
405 globalAllPowerfulAssertingMutex.lock_state,
406 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
409 // should be unlocked here
411 globalAllPowerfulAssertingMutex.lock_state,
412 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
414 // test going from exclusive to shared
416 auto wlock = sync.wlock();
417 auto slock = wlock.moveFromWriteToRead();
418 EXPECT_EQ(static_cast<bool>(wlock), false);
420 globalAllPowerfulAssertingMutex.lock_state,
421 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
424 // should be unlocked here
426 globalAllPowerfulAssertingMutex.lock_state,
427 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
430 TEST_F(SynchronizedLockTest, UpgradableLockingWithULock) {
431 folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
435 std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
436 "The ulock function was not well configured, blame aary@instagram.com");
438 // test from upgrade to write
439 sync.withULockPtr([](auto ulock) {
440 EXPECT_EQ(static_cast<bool>(ulock), true);
442 globalAllPowerfulAssertingMutex.lock_state,
443 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
445 auto wlock = ulock.moveFromUpgradeToWrite();
446 EXPECT_EQ(static_cast<bool>(ulock), false);
448 globalAllPowerfulAssertingMutex.lock_state,
449 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
452 // should be unlocked here
454 globalAllPowerfulAssertingMutex.lock_state,
455 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
457 // test from write to upgrade
458 sync.withWLockPtr([](auto wlock) {
459 EXPECT_EQ(static_cast<bool>(wlock), true);
461 globalAllPowerfulAssertingMutex.lock_state,
462 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
464 auto ulock = wlock.moveFromWriteToUpgrade();
465 EXPECT_EQ(static_cast<bool>(wlock), false);
467 globalAllPowerfulAssertingMutex.lock_state,
468 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
471 // should be unlocked here
473 globalAllPowerfulAssertingMutex.lock_state,
474 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
476 // test from upgrade to shared
477 sync.withULockPtr([](auto ulock) {
478 EXPECT_EQ(static_cast<bool>(ulock), true);
480 globalAllPowerfulAssertingMutex.lock_state,
481 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
483 auto slock = ulock.moveFromUpgradeToRead();
484 EXPECT_EQ(static_cast<bool>(ulock), false);
486 globalAllPowerfulAssertingMutex.lock_state,
487 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
490 // should be unlocked here
492 globalAllPowerfulAssertingMutex.lock_state,
493 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
495 // test from write to shared
496 sync.withWLockPtr([](auto wlock) {
497 EXPECT_EQ(static_cast<bool>(wlock), true);
499 globalAllPowerfulAssertingMutex.lock_state,
500 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
502 auto slock = wlock.moveFromWriteToRead();
503 EXPECT_EQ(static_cast<bool>(wlock), false);
505 globalAllPowerfulAssertingMutex.lock_state,
506 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
509 // should be unlocked here
511 globalAllPowerfulAssertingMutex.lock_state,
512 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);