1/*
2 * Copyright 2011-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// @author: Andrei Alexandrescu (aalexandre)
17
18// Test bed for folly/Synchronized.h
19
20#include <folly/Synchronized.h>
21#include <folly/Function.h>
22#include <folly/LockTraitsBoost.h>
23#include <folly/Portability.h>
24#include <folly/ScopeGuard.h>
25#include <folly/SharedMutex.h>
26#include <folly/SpinLock.h>
27#include <folly/portability/GTest.h>
28#include <folly/synchronization/RWSpinLock.h>
29#include <folly/test/SynchronizedTestLib.h>
30
31using namespace folly::sync_tests;
32
33namespace folly {
34
35template <class Mutex>
36class SynchronizedTest : public testing::Test {};
37
38using SynchronizedTestTypes = testing::Types<
39 folly::SharedMutexReadPriority,
40 folly::SharedMutexWritePriority,
41 std::mutex,
42 std::recursive_mutex,
43#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
44 std::timed_mutex,
45 std::recursive_timed_mutex,
46#endif
47 boost::mutex,
48 boost::recursive_mutex,
49#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
50 boost::timed_mutex,
51 boost::recursive_timed_mutex,
52#endif
53#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
54 folly::RWTicketSpinLock32,
55 folly::RWTicketSpinLock64,
56#endif
57 boost::shared_mutex,
58 folly::SpinLock>;
59TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes);
60
61TYPED_TEST(SynchronizedTest, Basic) {
62 testBasic<TypeParam>();
63}
64
65TYPED_TEST(SynchronizedTest, WithLock) {
66 testWithLock<TypeParam>();
67}
68
69TYPED_TEST(SynchronizedTest, Unlock) {
70 testUnlock<TypeParam>();
71}
72
73TYPED_TEST(SynchronizedTest, Deprecated) {
74 testDeprecated<TypeParam>();
75}
76
77TYPED_TEST(SynchronizedTest, Concurrency) {
78 testConcurrency<TypeParam>();
79}
80
81TYPED_TEST(SynchronizedTest, AcquireLocked) {
82 testAcquireLocked<TypeParam>();
83}
84
85TYPED_TEST(SynchronizedTest, AcquireLockedWithConst) {
86 testAcquireLockedWithConst<TypeParam>();
87}
88
89TYPED_TEST(SynchronizedTest, DualLocking) {
90 testDualLocking<TypeParam>();
91}
92
93TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
94 testDualLockingWithConst<TypeParam>();
95}
96
97TYPED_TEST(SynchronizedTest, ConstCopy) {
98 testConstCopy<TypeParam>();
99}
100
101TYPED_TEST(SynchronizedTest, InPlaceConstruction) {
102 testInPlaceConstruction<TypeParam>();
103}
104
105TYPED_TEST(SynchronizedTest, Exchange) {
106 testExchange<TypeParam>();
107}
108
109template <class Mutex>
110class SynchronizedTimedTest : public testing::Test {};
111
112using SynchronizedTimedTestTypes = testing::Types<
113#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
114 std::timed_mutex,
115 std::recursive_timed_mutex,
116 boost::timed_mutex,
117 boost::recursive_timed_mutex,
118 boost::shared_mutex,
119#endif
120#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
121 folly::RWTicketSpinLock32,
122 folly::RWTicketSpinLock64,
123#endif
124 folly::SharedMutexReadPriority,
125 folly::SharedMutexWritePriority>;
126TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
127
128TYPED_TEST(SynchronizedTimedTest, Timed) {
129 testTimed<TypeParam>();
130}
131
132TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
133 testTimedSynchronized<TypeParam>();
134}
135
136template <class Mutex>
137class SynchronizedTimedWithConstTest : public testing::Test {};
138
139using SynchronizedTimedWithConstTestTypes = testing::Types<
140#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
141 boost::shared_mutex,
142#endif
143#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
144 folly::RWTicketSpinLock32,
145 folly::RWTicketSpinLock64,
146#endif
147 folly::SharedMutexReadPriority,
148 folly::SharedMutexWritePriority>;
149TYPED_TEST_CASE(
150 SynchronizedTimedWithConstTest,
151 SynchronizedTimedWithConstTestTypes);
152
153TYPED_TEST(SynchronizedTimedWithConstTest, TimedShared) {
154 testTimedShared<TypeParam>();
155}
156
157TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
158 testTimedSynchronizedWithConst<TypeParam>();
159}
160
161using CountPair = std::pair<int, int>;
162// This class is specialized only to be uesed in SynchronizedLockTest
163class FakeMutex {
164 public:
165 void lock() {
166 ++lockCount_;
167 }
168
169 void unlock() {
170 ++unlockCount_;
171 }
172
173 static CountPair getLockUnlockCount() {
174 return CountPair{lockCount_, unlockCount_};
175 }
176
177 static void resetLockUnlockCount() {
178 lockCount_ = 0;
179 unlockCount_ = 0;
180 }
181
182 private:
183 // Keep these two static for test access
184 // Keep them thread_local in case of tests are run in parallel within one
185 // process
186 static FOLLY_TLS int lockCount_;
187 static FOLLY_TLS int unlockCount_;
188};
189FOLLY_TLS int FakeMutex::lockCount_{0};
190FOLLY_TLS int FakeMutex::unlockCount_{0};
191
192// SynchronizedLockTest is used to verify the correct lock unlock behavior
193// happens per design
194class SynchronizedLockTest : public testing::Test {
195 public:
196 void SetUp() override {
197 FakeMutex::resetLockUnlockCount();
198 }
199};
200
201/**
202 * Test mutex to help to automate assertions, taken from LockTraitsTest.cpp
203 */
204class FakeAllPowerfulAssertingMutexInternal {
205 public:
206 enum class CurrentLockState { UNLOCKED, SHARED, UPGRADE, UNIQUE };
207
208 void lock() {
209 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
210 this->lock_state = CurrentLockState::UNIQUE;
211 }
212 void unlock() {
213 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
214 this->lock_state = CurrentLockState::UNLOCKED;
215 }
216 void lock_shared() {
217 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
218 this->lock_state = CurrentLockState::SHARED;
219 }
220 void unlock_shared() {
221 EXPECT_EQ(this->lock_state, CurrentLockState::SHARED);
222 this->lock_state = CurrentLockState::UNLOCKED;
223 }
224 void lock_upgrade() {
225 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
226 this->lock_state = CurrentLockState::UPGRADE;
227 }
228 void unlock_upgrade() {
229 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
230 this->lock_state = CurrentLockState::UNLOCKED;
231 }
232
233 void unlock_upgrade_and_lock() {
234 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
235 this->lock_state = CurrentLockState::UNIQUE;
236 }
237 void unlock_and_lock_upgrade() {
238 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
239 this->lock_state = CurrentLockState::UPGRADE;
240 }
241 void unlock_and_lock_shared() {
242 EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
243 this->lock_state = CurrentLockState::SHARED;
244 }
245 void unlock_upgrade_and_lock_shared() {
246 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
247 this->lock_state = CurrentLockState::SHARED;
248 }
249
250 template <class Rep, class Period>
251 bool try_lock_for(const std::chrono::duration<Rep, Period>&) {
252 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
253 this->lock_state = CurrentLockState::UNIQUE;
254 return true;
255 }
256
257 template <class Rep, class Period>
258 bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>&) {
259 EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
260 this->lock_state = CurrentLockState::UPGRADE;
261 return true;
262 }
263
264 template <class Rep, class Period>
265 bool try_unlock_upgrade_and_lock_for(
266 const std::chrono::duration<Rep, Period>&) {
267 EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
268 this->lock_state = CurrentLockState::UNIQUE;
269 return true;
270 }
271
272 /*
273 * Initialize the FakeMutex with an unlocked state
274 */
275 CurrentLockState lock_state{CurrentLockState::UNLOCKED};
276};
277
278/**
279 * The following works around the internal mutex for synchronized being
280 * private
281 *
282 * This is horridly thread unsafe.
283 */
284static FakeAllPowerfulAssertingMutexInternal globalAllPowerfulAssertingMutex;
285
286class FakeAllPowerfulAssertingMutex {
287 public:
288 void lock() {
289 globalAllPowerfulAssertingMutex.lock();
290 }
291 void unlock() {
292 globalAllPowerfulAssertingMutex.unlock();
293 }
294 void lock_shared() {
295 globalAllPowerfulAssertingMutex.lock_shared();
296 }
297 void unlock_shared() {
298 globalAllPowerfulAssertingMutex.unlock_shared();
299 }
300 void lock_upgrade() {
301 globalAllPowerfulAssertingMutex.lock_upgrade();
302 }
303 void unlock_upgrade() {
304 globalAllPowerfulAssertingMutex.unlock_upgrade();
305 }
306
307 void unlock_upgrade_and_lock() {
308 globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock();
309 }
310 void unlock_and_lock_upgrade() {
311 globalAllPowerfulAssertingMutex.unlock_and_lock_upgrade();
312 }
313 void unlock_and_lock_shared() {
314 globalAllPowerfulAssertingMutex.unlock_and_lock_shared();
315 }
316 void unlock_upgrade_and_lock_shared() {
317 globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock_shared();
318 }
319
320 template <class Rep, class Period>
321 bool try_lock_for(const std::chrono::duration<Rep, Period>& arg) {
322 return globalAllPowerfulAssertingMutex.try_lock_for(arg);
323 }
324
325 template <class Rep, class Period>
326 bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>& arg) {
327 return globalAllPowerfulAssertingMutex.try_lock_upgrade_for(arg);
328 }
329
330 template <class Rep, class Period>
331 bool try_unlock_upgrade_and_lock_for(
332 const std::chrono::duration<Rep, Period>& arg) {
333 return globalAllPowerfulAssertingMutex.try_unlock_upgrade_and_lock_for(arg);
334 }
335
336 // reset state on destruction
337 ~FakeAllPowerfulAssertingMutex() {
338 globalAllPowerfulAssertingMutex = FakeAllPowerfulAssertingMutexInternal{};
339 }
340};
341
342class NonDefaultConstructibleMutex {
343 public:
344 explicit NonDefaultConstructibleMutex(int valueIn) {
345 value = valueIn;
346 }
347 NonDefaultConstructibleMutex() = delete;
348 NonDefaultConstructibleMutex(const NonDefaultConstructibleMutex&) = delete;
349 NonDefaultConstructibleMutex(NonDefaultConstructibleMutex&&) = delete;
350 NonDefaultConstructibleMutex& operator=(const NonDefaultConstructibleMutex&) =
351 delete;
352 NonDefaultConstructibleMutex& operator=(NonDefaultConstructibleMutex&&) =
353 delete;
354
355 static int value;
356
357 void lock() {}
358 void unlock() {}
359};
360int NonDefaultConstructibleMutex::value{0};
361
362TEST_F(SynchronizedLockTest, TestCopyConstructibleValues) {
363 struct NonCopyConstructible {
364 NonCopyConstructible(const NonCopyConstructible&) = delete;
365 NonCopyConstructible& operator=(const NonCopyConstructible&) = delete;
366 };
367 struct CopyConstructible {};
368 EXPECT_FALSE(std::is_copy_constructible<
369 folly::Synchronized<NonCopyConstructible>>::value);
370 EXPECT_FALSE(std::is_copy_assignable<
371 folly::Synchronized<NonCopyConstructible>>::value);
372 EXPECT_TRUE(std::is_copy_constructible<
373 folly::Synchronized<CopyConstructible>>::value);
374 EXPECT_TRUE(
375 std::is_copy_assignable<folly::Synchronized<CopyConstructible>>::value);
376}
377
378TEST_F(SynchronizedLockTest, UpgradeLocking) {
379 folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
380
381 // sanity assert
382 static_assert(
383 std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
384 "The ulock function was not well configured, blame aary@instagram.com");
385
386 {
387 auto ulock = sync.ulock();
388 EXPECT_EQ(
389 globalAllPowerfulAssertingMutex.lock_state,
390 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
391 }
392
393 // should be unlocked here
394 EXPECT_EQ(
395 globalAllPowerfulAssertingMutex.lock_state,
396 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
397
398 // test going from upgrade to exclusive
399 {
400 auto ulock = sync.ulock();
401 auto wlock = ulock.moveFromUpgradeToWrite();
402 EXPECT_EQ(static_cast<bool>(ulock), false);
403 EXPECT_EQ(
404 globalAllPowerfulAssertingMutex.lock_state,
405 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
406 }
407
408 // should be unlocked here
409 EXPECT_EQ(
410 globalAllPowerfulAssertingMutex.lock_state,
411 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
412
413 // test going from upgrade to shared
414 {
415 auto ulock = sync.ulock();
416 auto slock = ulock.moveFromUpgradeToRead();
417 EXPECT_EQ(static_cast<bool>(ulock), false);
418 EXPECT_EQ(
419 globalAllPowerfulAssertingMutex.lock_state,
420 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
421 }
422
423 // should be unlocked here
424 EXPECT_EQ(
425 globalAllPowerfulAssertingMutex.lock_state,
426 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
427
428 // test going from exclusive to upgrade
429 {
430 auto wlock = sync.wlock();
431 auto ulock = wlock.moveFromWriteToUpgrade();
432 EXPECT_EQ(static_cast<bool>(wlock), false);
433 EXPECT_EQ(
434 globalAllPowerfulAssertingMutex.lock_state,
435 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
436 }
437
438 // should be unlocked here
439 EXPECT_EQ(
440 globalAllPowerfulAssertingMutex.lock_state,
441 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
442
443 // test going from exclusive to shared
444 {
445 auto wlock = sync.wlock();
446 auto slock = wlock.moveFromWriteToRead();
447 EXPECT_EQ(static_cast<bool>(wlock), false);
448 EXPECT_EQ(
449 globalAllPowerfulAssertingMutex.lock_state,
450 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
451 }
452
453 // should be unlocked here
454 EXPECT_EQ(
455 globalAllPowerfulAssertingMutex.lock_state,
456 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
457}
458
459TEST_F(SynchronizedLockTest, UpgradeLockingWithULock) {
460 folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
461
462 // sanity assert
463 static_assert(
464 std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
465 "The ulock function was not well configured, blame aary@instagram.com");
466
467 // test from upgrade to write
468 sync.withULockPtr([](auto ulock) {
469 EXPECT_EQ(static_cast<bool>(ulock), true);
470 EXPECT_EQ(
471 globalAllPowerfulAssertingMutex.lock_state,
472 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
473
474 auto wlock = ulock.moveFromUpgradeToWrite();
475 EXPECT_EQ(static_cast<bool>(ulock), false);
476 EXPECT_EQ(
477 globalAllPowerfulAssertingMutex.lock_state,
478 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
479 });
480
481 // should be unlocked here
482 EXPECT_EQ(
483 globalAllPowerfulAssertingMutex.lock_state,
484 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
485
486 // test from write to upgrade
487 sync.withWLockPtr([](auto wlock) {
488 EXPECT_EQ(static_cast<bool>(wlock), true);
489 EXPECT_EQ(
490 globalAllPowerfulAssertingMutex.lock_state,
491 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
492
493 auto ulock = wlock.moveFromWriteToUpgrade();
494 EXPECT_EQ(static_cast<bool>(wlock), false);
495 EXPECT_EQ(
496 globalAllPowerfulAssertingMutex.lock_state,
497 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
498 });
499
500 // should be unlocked here
501 EXPECT_EQ(
502 globalAllPowerfulAssertingMutex.lock_state,
503 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
504
505 // test from upgrade to shared
506 sync.withULockPtr([](auto ulock) {
507 EXPECT_EQ(static_cast<bool>(ulock), true);
508 EXPECT_EQ(
509 globalAllPowerfulAssertingMutex.lock_state,
510 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
511
512 auto slock = ulock.moveFromUpgradeToRead();
513 EXPECT_EQ(static_cast<bool>(ulock), false);
514 EXPECT_EQ(
515 globalAllPowerfulAssertingMutex.lock_state,
516 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
517 });
518
519 // should be unlocked here
520 EXPECT_EQ(
521 globalAllPowerfulAssertingMutex.lock_state,
522 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
523
524 // test from write to shared
525 sync.withWLockPtr([](auto wlock) {
526 EXPECT_EQ(static_cast<bool>(wlock), true);
527 EXPECT_EQ(
528 globalAllPowerfulAssertingMutex.lock_state,
529 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
530
531 auto slock = wlock.moveFromWriteToRead();
532 EXPECT_EQ(static_cast<bool>(wlock), false);
533 EXPECT_EQ(
534 globalAllPowerfulAssertingMutex.lock_state,
535 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
536 });
537
538 // should be unlocked here
539 EXPECT_EQ(
540 globalAllPowerfulAssertingMutex.lock_state,
541 FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
542}
543
544TEST_F(SynchronizedLockTest, TestPieceWiseConstruct) {
545 auto&& synchronized = folly::Synchronized<int, NonDefaultConstructibleMutex>{
546 std::piecewise_construct,
547 std::forward_as_tuple(3),
548 std::forward_as_tuple(1)};
549
550 EXPECT_EQ(*synchronized.lock(), 3);
551 EXPECT_EQ(NonDefaultConstructibleMutex::value, 1);
552}
553
554namespace {
555constexpr auto kLockable = 1;
556constexpr auto kWLockable = 2;
557constexpr auto kRLockable = 4;
558constexpr auto kULockable = 8;
559
560template <int kLockableType>
561class TryLockable {
562 public:
563 explicit TryLockable(
564 bool shouldSucceed,
565 folly::Function<void()> onLockIn,
566 folly::Function<void()> onUnlockIn)
567 : kShouldSucceed{shouldSucceed},
568 onLock{std::move(onLockIn)},
569 onUnlock{std::move(onUnlockIn)} {}
570
571 void lock() {
572 EXPECT_TRUE(false);
573 }
574 template <
575 int LockableType = kLockableType,
576 std::enable_if_t<LockableType != kLockable>* = nullptr>
577 void lock_shared() {
578 EXPECT_TRUE(false);
579 }
580 template <
581 int LockableType = kLockableType,
582 std::enable_if_t<LockableType == kULockable>* = nullptr>
583 void lock_upgrade() {
584 EXPECT_TRUE(false);
585 }
586
587 bool tryLockImpl(int lockableMask) {
588 // if the lockable type of this instance is one of the possible options as
589 // expressed in the mask go through the usual test code
590 if (kLockableType | lockableMask) {
591 if (kShouldSucceed) {
592 onLock();
593 return true;
594 } else {
595 return false;
596 }
597 }
598
599 // else fail the test
600 EXPECT_TRUE(false);
601 return false;
602 }
603 void unlockImpl(int lockableMask) {
604 if (kLockableType | lockableMask) {
605 onUnlock();
606 return;
607 }
608
609 EXPECT_TRUE(false);
610 }
611
612 bool try_lock() {
613 return tryLockImpl(kLockable | kWLockable);
614 }
615 bool try_lock_shared() {
616 return tryLockImpl(kRLockable);
617 }
618 bool try_lock_upgrade() {
619 return tryLockImpl(kULockable);
620 }
621
622 void unlock() {
623 unlockImpl(kLockable | kWLockable);
624 }
625 void unlock_shared() {
626 unlockImpl(kLockable | kRLockable);
627 }
628 void unlock_upgrade() {
629 unlockImpl(kLockable | kULockable);
630 }
631
632 const bool kShouldSucceed;
633 folly::Function<void()> onLock;
634 folly::Function<void()> onUnlock;
635};
636
637struct TestSharedMutex {
638 public:
639 void lock() {
640 onLock_();
641 }
642 void unlock() {
643 onUnlock_();
644 }
645 void lock_shared() {
646 onLockShared_();
647 }
648 void unlock_shared() {
649 onUnlockShared_();
650 }
651
652 bool try_lock() {
653 onLock_();
654 return true;
655 }
656 bool try_lock_shared() {
657 onLockShared_();
658 return true;
659 }
660
661 std::function<void()> onLock_;
662 std::function<void()> onUnlock_;
663 std::function<void()> onLockShared_;
664 std::function<void()> onUnlockShared_;
665};
666
667struct TestMutex {
668 public:
669 void lock() {
670 onLock();
671 ++numTimesLocked;
672 }
673 bool try_lock() {
674 if (shouldTryLockSucceed) {
675 lock();
676 return true;
677 }
678 return false;
679 }
680 void unlock() {
681 onUnlock();
682 ++numTimesUnlocked;
683 }
684
685 int numTimesLocked{0};
686 int numTimesUnlocked{0};
687 bool shouldTryLockSucceed{true};
688 std::function<void()> onLock{[] {}};
689 std::function<void()> onUnlock{[] {}};
690};
691
692template <int kLockable, typename Func>
693void testTryLock(Func func) {
694 {
695 auto locked = 0;
696 auto unlocked = 0;
697 folly::Synchronized<int, TryLockable<kLockable>> synchronized{
698 std::piecewise_construct,
699 std::make_tuple(),
700 std::make_tuple(true, [&] { ++locked; }, [&] { ++unlocked; })};
701
702 {
703 auto lock = func(synchronized);
704 EXPECT_TRUE(lock);
705 EXPECT_EQ(locked, 1);
706 }
707 EXPECT_EQ(locked, 1);
708 EXPECT_EQ(unlocked, 1);
709 }
710 {
711 auto locked = 0;
712 auto unlocked = 0;
713 folly::Synchronized<int, TryLockable<kLockable>> synchronized{
714 std::piecewise_construct,
715 std::make_tuple(),
716 std::make_tuple(false, [&] { ++locked; }, [&] { ++unlocked; })};
717
718 {
719 auto lock = func(synchronized);
720 EXPECT_FALSE(lock);
721 EXPECT_EQ(locked, 0);
722 }
723 EXPECT_EQ(locked, 0);
724 EXPECT_EQ(unlocked, 0);
725 }
726}
727
728class MutexTrack {
729 public:
730 static int gId;
731 static int gOrder;
732
733 void lock_shared() {}
734 void unlock_shared() {}
735 void lock() {
736 order = MutexTrack::gOrder++;
737 }
738 void unlock() {
739 order = -1;
740 --gOrder;
741 }
742
743 int current{gId++};
744 int order{-1};
745};
746int MutexTrack::gId{0};
747int MutexTrack::gOrder{0};
748} // namespace
749
750TEST_F(SynchronizedLockTest, TestTryLock) {
751 testTryLock<kLockable>(
752 [](auto& synchronized) { return synchronized.tryLock(); });
753}
754
755TEST_F(SynchronizedLockTest, TestTryWLock) {
756 testTryLock<kWLockable>(
757 [](auto& synchronized) { return synchronized.tryWLock(); });
758}
759
760TEST_F(SynchronizedLockTest, TestTryRLock) {
761 testTryLock<kRLockable>(
762 [](auto& synchronized) { return synchronized.tryRLock(); });
763}
764
765TEST_F(SynchronizedLockTest, TestTryULock) {
766 testTryLock<kULockable>(
767 [](auto& synchronized) { return synchronized.tryULock(); });
768}
769
770template <typename LockPolicy>
771using LPtr = LockedPtr<Synchronized<int>, LockPolicy>;
772
773namespace {
774template <template <typename...> class Trait>
775void testLockedPtrCompatibilityExclusive() {
776 EXPECT_TRUE((
777 Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyTryExclusive>&&>::value));
778 EXPECT_TRUE((Trait<
779 LPtr<LockPolicyExclusive>,
780 LPtr<LockPolicyFromUpgradeToExclusive>&&>::value));
781
782 EXPECT_FALSE(
783 (Trait<LPtr<LockPolicyExclusive>&, LPtr<LockPolicyShared>&&>::value));
784 EXPECT_FALSE(
785 (Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyTryShared>&&>::value));
786 EXPECT_FALSE(
787 (Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyUpgrade>&&>::value));
788 EXPECT_FALSE(
789 (Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyTryUpgrade>&&>::value));
790 EXPECT_FALSE((Trait<
791 LPtr<LockPolicyExclusive>,
792 LPtr<LockPolicyFromExclusiveToUpgrade>&&>::value));
793 EXPECT_FALSE((Trait<
794 LPtr<LockPolicyExclusive>,
795 LPtr<LockPolicyFromExclusiveToShared>&&>::value));
796 EXPECT_FALSE(
797 (Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyFromUpgradeToShared>&&>::
798 value));
799}
800
801template <template <typename...> class Trait>
802void testLockedPtrCompatibilityShared() {
803 EXPECT_TRUE(
804 (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyTryShared>&&>::value));
805 EXPECT_TRUE(
806 (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyFromUpgradeToShared>&&>::
807 value));
808 EXPECT_TRUE(
809 (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyFromExclusiveToShared>&&>::
810 value));
811
812 EXPECT_FALSE(
813 (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyExclusive>&&>::value));
814 EXPECT_FALSE(
815 (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyTryExclusive>&&>::value));
816 EXPECT_FALSE(
817 (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyUpgrade>&&>::value));
818 EXPECT_FALSE(
819 (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyTryUpgrade>&&>::value));
820 EXPECT_FALSE(
821 (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyFromExclusiveToUpgrade>&&>::
822 value));
823 EXPECT_FALSE(
824 (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyFromUpgradeToExclusive>&&>::
825 value));
826}
827
828template <template <typename...> class Trait>
829void testLockedPtrCompatibilityUpgrade() {
830 EXPECT_TRUE(
831 (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyTryUpgrade>&&>::value));
832 EXPECT_TRUE((
833 Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyFromExclusiveToUpgrade>&&>::
834 value));
835
836 EXPECT_FALSE(
837 (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyExclusive>&&>::value));
838 EXPECT_FALSE(
839 (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyTryExclusive>&&>::value));
840 EXPECT_FALSE(
841 (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyShared>&&>::value));
842 EXPECT_FALSE(
843 (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyTryShared>&&>::value));
844 EXPECT_FALSE(
845 (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyFromExclusiveToShared>&&>::
846 value));
847 EXPECT_FALSE(
848 (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyFromUpgradeToShared>&&>::
849 value));
850 EXPECT_FALSE((
851 Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyFromUpgradeToExclusive>&&>::
852 value));
853}
854} // namespace
855
856TEST_F(SynchronizedLockTest, TestLockedPtrCompatibilityExclusive) {
857 testLockedPtrCompatibilityExclusive<std::is_assignable>();
858 testLockedPtrCompatibilityExclusive<std::is_constructible>();
859}
860
861TEST_F(SynchronizedLockTest, TestLockedPtrCompatibilityShared) {
862 testLockedPtrCompatibilityShared<std::is_assignable>();
863 testLockedPtrCompatibilityShared<std::is_constructible>();
864}
865
866TEST_F(SynchronizedLockTest, TestLockedPtrCompatibilityUpgrade) {
867 testLockedPtrCompatibilityUpgrade<std::is_assignable>();
868 testLockedPtrCompatibilityUpgrade<std::is_constructible>();
869}
870
871TEST_F(SynchronizedLockTest, TestConvertTryLockToLock) {
872 auto synchronized = folly::Synchronized<int>{0};
873 auto wlock = synchronized.wlock();
874 wlock.unlock();
875
876 auto ulock = synchronized.ulock();
877 wlock = ulock.moveFromUpgradeToWrite();
878 wlock.unlock();
879
880 auto value = synchronized.withWLock([](auto& integer) { return integer; });
881 EXPECT_EQ(value, 0);
882}
883
884TEST(FollyLockTest, TestVariadicLockWithSynchronized) {
885 {
886 auto syncs = std::array<folly::Synchronized<int>, 3>{};
887 auto& one = syncs[0];
888 auto const& two = syncs[1];
889 auto& three = syncs[2];
890 auto locks =
891 lock(folly::wlock(one), folly::rlock(two), folly::wlock(three));
892 EXPECT_TRUE(std::get<0>(locks));
893 EXPECT_TRUE(std::get<1>(locks));
894 EXPECT_TRUE(std::get<2>(locks));
895 }
896 {
897 auto syncs = std::array<folly::Synchronized<int, std::mutex>, 2>{};
898 auto locks = lock(folly::lock(syncs[0]), folly::lock(syncs[1]));
899 EXPECT_TRUE(std::get<0>(locks));
900 EXPECT_TRUE(std::get<1>(locks));
901 }
902}
903
904TEST(FollyLockTest, TestVariadicLockWithArbitraryLockables) {
905 auto&& one = std::mutex{};
906 auto&& two = std::mutex{};
907
908 auto lckOne = std::unique_lock<std::mutex>{one, std::defer_lock};
909 auto lckTwo = std::unique_lock<std::mutex>{two, std::defer_lock};
910 folly::lock(lckOne, lckTwo);
911 EXPECT_TRUE(lckOne);
912 EXPECT_TRUE(lckTwo);
913}
914
915TEST(FollyLockTest, TestVariadicLockSmartAndPoliteAlgorithm) {
916 auto one = TestMutex{};
917 auto two = TestMutex{};
918 auto three = TestMutex{};
919 auto makeReset = [&] {
920 return folly::makeGuard([&] {
921 one = TestMutex{};
922 two = TestMutex{};
923 three = TestMutex{};
924 });
925 };
926
927 {
928 auto reset = makeReset();
929 folly::lock(one, two, three);
930 EXPECT_EQ(one.numTimesLocked, 1);
931 EXPECT_EQ(one.numTimesUnlocked, 0);
932 EXPECT_EQ(two.numTimesLocked, 1);
933 EXPECT_EQ(two.numTimesUnlocked, 0);
934 EXPECT_EQ(three.numTimesLocked, 1);
935 EXPECT_EQ(three.numTimesUnlocked, 0);
936 }
937
938 {
939 auto reset = makeReset();
940 two.shouldTryLockSucceed = false;
941 folly::lock(one, two, three);
942 EXPECT_EQ(one.numTimesLocked, 2);
943 EXPECT_EQ(one.numTimesUnlocked, 1);
944 EXPECT_EQ(two.numTimesLocked, 1);
945 EXPECT_EQ(two.numTimesUnlocked, 0);
946 EXPECT_EQ(three.numTimesLocked, 1);
947 EXPECT_EQ(three.numTimesUnlocked, 0);
948 }
949
950 {
951 auto reset = makeReset();
952 three.shouldTryLockSucceed = false;
953 folly::lock(one, two, three);
954 EXPECT_EQ(one.numTimesLocked, 2);
955 EXPECT_EQ(one.numTimesUnlocked, 1);
956 EXPECT_EQ(two.numTimesLocked, 2);
957 EXPECT_EQ(two.numTimesUnlocked, 1);
958 EXPECT_EQ(three.numTimesLocked, 1);
959 EXPECT_EQ(three.numTimesUnlocked, 0);
960 }
961
962 {
963 auto reset = makeReset();
964 three.shouldTryLockSucceed = false;
965
966 three.onLock = [&] {
967 // when three gets locked make one fail
968 one.shouldTryLockSucceed = false;
969 // then when one gets locked make three succeed to finish the test
970 one.onLock = [&] { three.shouldTryLockSucceed = true; };
971 };
972
973 folly::lock(one, two, three);
974 EXPECT_EQ(one.numTimesLocked, 2);
975 EXPECT_EQ(one.numTimesUnlocked, 1);
976 EXPECT_EQ(two.numTimesLocked, 2);
977 EXPECT_EQ(two.numTimesUnlocked, 1);
978 EXPECT_EQ(three.numTimesLocked, 2);
979 EXPECT_EQ(three.numTimesUnlocked, 1);
980 }
981}
982
983TEST(SynchronizedAlgorithmTest, Basic) {
984 auto sync = Synchronized<int>{0};
985 auto value = synchronized([](auto s) { return *s; }, wlock(sync));
986 EXPECT_EQ(value, 0);
987}
988
989TEST(SynchronizedAlgorithmTest, BasicNonShareableMutex) {
990 auto sync = Synchronized<int, std::mutex>{0};
991 auto value = synchronized([](auto s) { return *s; }, lock(sync));
992 EXPECT_EQ(value, 0);
993}
994
995TEST(Synchronized, SynchronizedFunctionNonConst) {
996 auto locked = 0;
997 auto unlocked = 0;
998 auto sync = Synchronized<int, TestSharedMutex>{
999 std::piecewise_construct,
1000 std::make_tuple(0),
1001 std::make_tuple([&] { ++locked; }, [&] { ++unlocked; }, [] {}, [] {})};
1002
1003 synchronized([](auto) {}, wlock(sync));
1004 EXPECT_EQ(locked, 1);
1005 EXPECT_EQ(unlocked, 1);
1006}
1007
1008TEST(Synchronized, SynchronizedFunctionConst) {
1009 auto locked = 0;
1010 auto unlocked = 0;
1011 auto sync = Synchronized<int, TestSharedMutex>{
1012 std::piecewise_construct,
1013 std::make_tuple(0),
1014 std::make_tuple([] {}, [] {}, [&] { ++locked; }, [&] { ++unlocked; })};
1015
1016 synchronized([](auto) {}, rlock(sync));
1017 EXPECT_EQ(locked, 1);
1018 EXPECT_EQ(unlocked, 1);
1019}
1020
1021TEST(Synchronized, SynchronizedFunctionManyObjects) {
1022 auto fail = [] { EXPECT_TRUE(false); };
1023 auto pass = [] {};
1024
1025 auto one = Synchronized<int, TestSharedMutex>{
1026 std::piecewise_construct,
1027 std::make_tuple(0),
1028 std::make_tuple(pass, pass, fail, fail)};
1029 auto two = Synchronized<std::string, TestSharedMutex>{
1030 std::piecewise_construct,
1031 std::make_tuple(),
1032 std::make_tuple(fail, fail, pass, pass)};
1033
1034 synchronized([](auto, auto) {}, wlock(one), rlock(two));
1035}
1036
1037} // namespace folly
1038