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 // Single level of SYNCHRONIZED and UNSYNCHRONIZED, although nested test are
334 // super set of it, it is possible single level test passes while nested tests
336 TEST_F(SynchronizedLockTest, SyncUnSync) {
337 folly::Synchronized<std::vector<int>, FakeMutex> obj;
338 EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
340 EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
341 UNSYNCHRONIZED(obj) {
342 EXPECT_EQ((CountPair{1, 1}), FakeMutex::getLockUnlockCount());
344 EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
346 EXPECT_EQ((CountPair{2, 2}), FakeMutex::getLockUnlockCount());
349 // Nested SYNCHRONIZED UNSYNCHRONIZED test, 2 levels of synchronization
350 TEST_F(SynchronizedLockTest, NestedSyncUnSync) {
351 folly::Synchronized<std::vector<int>, FakeMutex> obj;
352 EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
353 SYNCHRONIZED(objCopy, obj) {
354 EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
356 EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
357 // Note: UNSYNCHRONIZED has always been kind of broken here.
358 // The input parameter is ignored (other than to overwrite what the input
359 // variable name refers to), and it unlocks the most object acquired in
360 // the most recent SYNCHRONIZED scope.
361 UNSYNCHRONIZED(obj) {
362 EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
364 EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
365 UNSYNCHRONIZED(obj) {
366 EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
368 EXPECT_EQ((CountPair{4, 2}), FakeMutex::getLockUnlockCount());
370 EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
372 EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
375 // Different nesting behavior, UNSYNCHRONIZED called on different depth of
377 TEST_F(SynchronizedLockTest, NestedSyncUnSync2) {
378 folly::Synchronized<std::vector<int>, FakeMutex> obj;
379 EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
380 SYNCHRONIZED(objCopy, obj) {
381 EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
383 EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
384 UNSYNCHRONIZED(obj) {
385 EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
387 EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
389 EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
390 UNSYNCHRONIZED(obj) {
391 EXPECT_EQ((CountPair{3, 3}), FakeMutex::getLockUnlockCount());
393 EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
395 EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
398 TEST_F(SynchronizedLockTest, TestCopyConstructibleValues) {
399 struct NonCopyConstructible {
400 NonCopyConstructible(const NonCopyConstructible&) = delete;
401 NonCopyConstructible& operator=(const NonCopyConstructible&) = delete;
403 struct CopyConstructible {};
404 EXPECT_FALSE(std::is_copy_constructible<
405 folly::Synchronized<NonCopyConstructible>>::value);
406 EXPECT_FALSE(std::is_copy_assignable<
407 folly::Synchronized<NonCopyConstructible>>::value);
408 EXPECT_TRUE(std::is_copy_constructible<
409 folly::Synchronized<CopyConstructible>>::value);
411 std::is_copy_assignable<folly::Synchronized<CopyConstructible>>::value);
414 TEST_F(SynchronizedLockTest, UpgradableLocking) {
415 folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
419 std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
420 "The ulock function was not well configured, blame aary@instagram.com");
423 auto ulock = sync.ulock();
425 globalAllPowerfulAssertingMutex.lock_state,
426 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
429 // should be unlocked here
431 globalAllPowerfulAssertingMutex.lock_state,
432 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
434 // test going from upgrade to exclusive
436 auto ulock = sync.ulock();
437 auto wlock = ulock.moveFromUpgradeToWrite();
438 EXPECT_EQ(static_cast<bool>(ulock), false);
440 globalAllPowerfulAssertingMutex.lock_state,
441 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
444 // should be unlocked here
446 globalAllPowerfulAssertingMutex.lock_state,
447 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
449 // test going from upgrade to shared
451 auto ulock = sync.ulock();
452 auto slock = ulock.moveFromUpgradeToRead();
453 EXPECT_EQ(static_cast<bool>(ulock), false);
455 globalAllPowerfulAssertingMutex.lock_state,
456 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
459 // should be unlocked here
461 globalAllPowerfulAssertingMutex.lock_state,
462 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
464 // test going from exclusive to upgrade
466 auto wlock = sync.wlock();
467 auto ulock = wlock.moveFromWriteToUpgrade();
468 EXPECT_EQ(static_cast<bool>(wlock), false);
470 globalAllPowerfulAssertingMutex.lock_state,
471 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
474 // should be unlocked here
476 globalAllPowerfulAssertingMutex.lock_state,
477 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
479 // test going from exclusive to shared
481 auto wlock = sync.wlock();
482 auto slock = wlock.moveFromWriteToRead();
483 EXPECT_EQ(static_cast<bool>(wlock), false);
485 globalAllPowerfulAssertingMutex.lock_state,
486 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
489 // should be unlocked here
491 globalAllPowerfulAssertingMutex.lock_state,
492 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
495 TEST_F(SynchronizedLockTest, UpgradableLockingWithULock) {
496 folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
500 std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
501 "The ulock function was not well configured, blame aary@instagram.com");
503 // test from upgrade to write
504 sync.withULockPtr([](auto ulock) {
505 EXPECT_EQ(static_cast<bool>(ulock), true);
507 globalAllPowerfulAssertingMutex.lock_state,
508 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
510 auto wlock = ulock.moveFromUpgradeToWrite();
511 EXPECT_EQ(static_cast<bool>(ulock), false);
513 globalAllPowerfulAssertingMutex.lock_state,
514 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
517 // should be unlocked here
519 globalAllPowerfulAssertingMutex.lock_state,
520 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
522 // test from write to upgrade
523 sync.withWLockPtr([](auto wlock) {
524 EXPECT_EQ(static_cast<bool>(wlock), true);
526 globalAllPowerfulAssertingMutex.lock_state,
527 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
529 auto ulock = wlock.moveFromWriteToUpgrade();
530 EXPECT_EQ(static_cast<bool>(wlock), false);
532 globalAllPowerfulAssertingMutex.lock_state,
533 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
536 // should be unlocked here
538 globalAllPowerfulAssertingMutex.lock_state,
539 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
541 // test from upgrade to shared
542 sync.withULockPtr([](auto ulock) {
543 EXPECT_EQ(static_cast<bool>(ulock), true);
545 globalAllPowerfulAssertingMutex.lock_state,
546 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
548 auto slock = ulock.moveFromUpgradeToRead();
549 EXPECT_EQ(static_cast<bool>(ulock), false);
551 globalAllPowerfulAssertingMutex.lock_state,
552 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
555 // should be unlocked here
557 globalAllPowerfulAssertingMutex.lock_state,
558 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
560 // test from write to shared
561 sync.withWLockPtr([](auto wlock) {
562 EXPECT_EQ(static_cast<bool>(wlock), true);
564 globalAllPowerfulAssertingMutex.lock_state,
565 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
567 auto slock = wlock.moveFromWriteToRead();
568 EXPECT_EQ(static_cast<bool>(wlock), false);
570 globalAllPowerfulAssertingMutex.lock_state,
571 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
574 // should be unlocked here
576 globalAllPowerfulAssertingMutex.lock_state,
577 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);