proxygen
SynchronizedTest.cpp
Go to the documentation of this file.
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>
30 
31 using namespace folly::sync_tests;
32 
33 namespace folly {
34 
35 template <class Mutex>
36 class SynchronizedTest : public testing::Test {};
37 
38 using SynchronizedTestTypes = testing::Types<
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
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,
60 
62  testBasic<TypeParam>();
63 }
64 
66  testWithLock<TypeParam>();
67 }
68 
70  testUnlock<TypeParam>();
71 }
72 
74  testDeprecated<TypeParam>();
75 }
76 
78  testConcurrency<TypeParam>();
79 }
80 
81 TYPED_TEST(SynchronizedTest, AcquireLocked) {
82  testAcquireLocked<TypeParam>();
83 }
84 
85 TYPED_TEST(SynchronizedTest, AcquireLockedWithConst) {
86  testAcquireLockedWithConst<TypeParam>();
87 }
88 
90  testDualLocking<TypeParam>();
91 }
92 
93 TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
94  testDualLockingWithConst<TypeParam>();
95 }
96 
98  testConstCopy<TypeParam>();
99 }
100 
101 TYPED_TEST(SynchronizedTest, InPlaceConstruction) {
102  testInPlaceConstruction<TypeParam>();
103 }
104 
106  testExchange<TypeParam>();
107 }
108 
109 template <class Mutex>
111 
112 using 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
125  folly::SharedMutexWritePriority>;
127 
129  testTimed<TypeParam>();
130 }
131 
132 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
133  testTimedSynchronized<TypeParam>();
134 }
135 
136 template <class Mutex>
138 
139 using 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
148  folly::SharedMutexWritePriority>;
152 
154  testTimedShared<TypeParam>();
155 }
156 
157 TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
158  testTimedSynchronizedWithConst<TypeParam>();
159 }
160 
161 using CountPair = std::pair<int, int>;
162 // This class is specialized only to be uesed in SynchronizedLockTest
163 class FakeMutex {
164  public:
165  void lock() {
166  ++lockCount_;
167  }
168 
169  void unlock() {
170  ++unlockCount_;
171  }
172 
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 };
189 FOLLY_TLS int FakeMutex::lockCount_{0};
190 FOLLY_TLS int FakeMutex::unlockCount_{0};
191 
192 // SynchronizedLockTest is used to verify the correct lock unlock behavior
193 // happens per design
195  public:
196  void SetUp() override {
198  }
199 };
200 
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 
234  EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
235  this->lock_state = CurrentLockState::UNIQUE;
236  }
238  EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
239  this->lock_state = CurrentLockState::UPGRADE;
240  }
242  EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
243  this->lock_state = CurrentLockState::SHARED;
244  }
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>
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 
285 
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 
308  globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock();
309  }
311  globalAllPowerfulAssertingMutex.unlock_and_lock_upgrade();
312  }
314  globalAllPowerfulAssertingMutex.unlock_and_lock_shared();
315  }
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>
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
338  globalAllPowerfulAssertingMutex = FakeAllPowerfulAssertingMutexInternal{};
339  }
340 };
341 
343  public:
344  explicit NonDefaultConstructibleMutex(int valueIn) {
345  value = valueIn;
346  }
347  NonDefaultConstructibleMutex() = delete;
351  delete;
353  delete;
354 
355  static int value;
356 
357  void lock() {}
358  void unlock() {}
359 };
361 
362 TEST_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<
370  EXPECT_FALSE(std::is_copy_assignable<
372  EXPECT_TRUE(std::is_copy_constructible<
374  EXPECT_TRUE(
375  std::is_copy_assignable<folly::Synchronized<CopyConstructible>>::value);
376 }
377 
378 TEST_F(SynchronizedLockTest, UpgradableLocking) {
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,
391  }
392 
393  // should be unlocked here
394  EXPECT_EQ(
395  globalAllPowerfulAssertingMutex.lock_state,
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,
406  }
407 
408  // should be unlocked here
409  EXPECT_EQ(
410  globalAllPowerfulAssertingMutex.lock_state,
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,
421  }
422 
423  // should be unlocked here
424  EXPECT_EQ(
425  globalAllPowerfulAssertingMutex.lock_state,
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,
436  }
437 
438  // should be unlocked here
439  EXPECT_EQ(
440  globalAllPowerfulAssertingMutex.lock_state,
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,
451  }
452 
453  // should be unlocked here
454  EXPECT_EQ(
455  globalAllPowerfulAssertingMutex.lock_state,
457 }
458 
459 TEST_F(SynchronizedLockTest, UpgradableLockingWithULock) {
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,
473 
474  auto wlock = ulock.moveFromUpgradeToWrite();
475  EXPECT_EQ(static_cast<bool>(ulock), false);
476  EXPECT_EQ(
477  globalAllPowerfulAssertingMutex.lock_state,
479  });
480 
481  // should be unlocked here
482  EXPECT_EQ(
483  globalAllPowerfulAssertingMutex.lock_state,
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,
492 
493  auto ulock = wlock.moveFromWriteToUpgrade();
494  EXPECT_EQ(static_cast<bool>(wlock), false);
495  EXPECT_EQ(
496  globalAllPowerfulAssertingMutex.lock_state,
498  });
499 
500  // should be unlocked here
501  EXPECT_EQ(
502  globalAllPowerfulAssertingMutex.lock_state,
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,
511 
512  auto slock = ulock.moveFromUpgradeToRead();
513  EXPECT_EQ(static_cast<bool>(ulock), false);
514  EXPECT_EQ(
515  globalAllPowerfulAssertingMutex.lock_state,
517  });
518 
519  // should be unlocked here
520  EXPECT_EQ(
521  globalAllPowerfulAssertingMutex.lock_state,
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,
530 
531  auto slock = wlock.moveFromWriteToRead();
532  EXPECT_EQ(static_cast<bool>(wlock), false);
533  EXPECT_EQ(
534  globalAllPowerfulAssertingMutex.lock_state,
536  });
537 
538  // should be unlocked here
539  EXPECT_EQ(
540  globalAllPowerfulAssertingMutex.lock_state,
542 }
543 
544 TEST_F(SynchronizedLockTest, TestPieceWiseConstruct) {
546  std::piecewise_construct,
547  std::forward_as_tuple(3),
548  std::forward_as_tuple(1)};
549 
550  EXPECT_EQ(*synchronized.lock(), 3);
552 }
553 
554 namespace {
555 constexpr auto kLockable = 1;
556 constexpr auto kWLockable = 2;
557 constexpr auto kRLockable = 4;
558 constexpr auto kULockable = 8;
559 
560 template <int kLockableType>
561 class 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;
635 };
636 
637 struct 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 
667 struct 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();
683  }
684 
688  std::function<void()> onLock{[] {}};
689  std::function<void()> onUnlock{[] {}};
690 };
691 
692 template <int kLockable, typename Func>
693 void testTryLock(Func func) {
694  {
695  auto locked = 0;
696  auto unlocked = 0;
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;
714  std::piecewise_construct,
715  std::make_tuple(),
716  std::make_tuple(false, [&] { ++locked; }, [&] { ++unlocked; })};
717 
718  {
719  auto lock = func(synchronized);
721  EXPECT_EQ(locked, 0);
722  }
723  EXPECT_EQ(locked, 0);
724  EXPECT_EQ(unlocked, 0);
725  }
726 }
727 
728 class MutexTrack {
729  public:
730  static int gId;
731  static int gOrder;
732 
733  void lock_shared() {}
734  void unlock_shared() {}
735  void lock() {
737  }
738  void unlock() {
739  order = -1;
740  --gOrder;
741  }
742 
743  int current{gId++};
744  int order{-1};
745 };
746 int MutexTrack::gId{0};
747 int MutexTrack::gOrder{0};
748 } // namespace
749 
751  testTryLock<kLockable>(
752  [](auto& synchronized) { return synchronized.tryLock(); });
753 }
754 
755 TEST_F(SynchronizedLockTest, TestTryWLock) {
756  testTryLock<kWLockable>(
757  [](auto& synchronized) { return synchronized.tryWLock(); });
758 }
759 
760 TEST_F(SynchronizedLockTest, TestTryRLock) {
761  testTryLock<kRLockable>(
762  [](auto& synchronized) { return synchronized.tryRLock(); });
763 }
764 
765 TEST_F(SynchronizedLockTest, TestTryULock) {
766  testTryLock<kULockable>(
767  [](auto& synchronized) { return synchronized.tryULock(); });
768 }
769 
770 template <typename LockPolicy>
771 using LPtr = LockedPtr<Synchronized<int>, LockPolicy>;
772 
773 namespace {
774 template <template <typename...> class Trait>
775 void testLockedPtrCompatibilityExclusive() {
776  EXPECT_TRUE((
778  EXPECT_TRUE((Trait<
781 
782  EXPECT_FALSE(
784  EXPECT_FALSE(
786  EXPECT_FALSE(
788  EXPECT_FALSE(
790  EXPECT_FALSE((Trait<
793  EXPECT_FALSE((Trait<
796  EXPECT_FALSE(
798  value));
799 }
800 
801 template <template <typename...> class Trait>
802 void testLockedPtrCompatibilityShared() {
803  EXPECT_TRUE(
805  EXPECT_TRUE(
807  value));
808  EXPECT_TRUE(
810  value));
811 
812  EXPECT_FALSE(
814  EXPECT_FALSE(
816  EXPECT_FALSE(
818  EXPECT_FALSE(
820  EXPECT_FALSE(
822  value));
823  EXPECT_FALSE(
825  value));
826 }
827 
828 template <template <typename...> class Trait>
829 void testLockedPtrCompatibilityUpgrade() {
830  EXPECT_TRUE(
832  EXPECT_TRUE((
834  value));
835 
836  EXPECT_FALSE(
838  EXPECT_FALSE(
840  EXPECT_FALSE(
842  EXPECT_FALSE(
844  EXPECT_FALSE(
846  value));
847  EXPECT_FALSE(
849  value));
850  EXPECT_FALSE((
852  value));
853 }
854 } // namespace
855 
856 TEST_F(SynchronizedLockTest, TestLockedPtrCompatibilityExclusive) {
857  testLockedPtrCompatibilityExclusive<std::is_assignable>();
858  testLockedPtrCompatibilityExclusive<std::is_constructible>();
859 }
860 
861 TEST_F(SynchronizedLockTest, TestLockedPtrCompatibilityShared) {
862  testLockedPtrCompatibilityShared<std::is_assignable>();
863  testLockedPtrCompatibilityShared<std::is_constructible>();
864 }
865 
866 TEST_F(SynchronizedLockTest, TestLockedPtrCompatibilityUpgrade) {
867  testLockedPtrCompatibilityUpgrade<std::is_assignable>();
868  testLockedPtrCompatibilityUpgrade<std::is_constructible>();
869 }
870 
871 TEST_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 
884 TEST(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 
904 TEST(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 
915 TEST(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 
983 TEST(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 
989 TEST(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 
995 TEST(Synchronized, SynchronizedFunctionNonConst) {
996  auto locked = 0;
997  auto unlocked = 0;
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 
1008 TEST(Synchronized, SynchronizedFunctionConst) {
1009  auto locked = 0;
1010  auto unlocked = 0;
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 
1021 TEST(Synchronized, SynchronizedFunctionManyObjects) {
1022  auto fail = [] { EXPECT_TRUE(false); };
1023  auto pass = [] {};
1024 
1026  std::piecewise_construct,
1027  std::make_tuple(0),
1028  std::make_tuple(pass, pass, fail, fail)};
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
bool shouldTryLockSucceed
static void resetLockUnlockCount()
bool try_lock_upgrade_for(const std::chrono::duration< Rep, Period > &arg)
auto wlock(Synchronized< D, M > &synchronized, Args &&...args)
static std::unique_ptr< SSLLock[]> & locks()
SharedMutexImpl< true > SharedMutexReadPriority
Definition: SharedMutex.h:1509
static FakeAllPowerfulAssertingMutexInternal globalAllPowerfulAssertingMutex
PskType type
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
folly::Function< void()> onUnlock
static int gOrder
const bool kShouldSucceed
TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
tuple make_tuple()
Definition: gtest-tuple.h:675
testing::Types< folly::SharedMutexReadPriority, folly::SharedMutexWritePriority > SynchronizedTimedTestTypes
auto rlock(const Synchronized< Data, Mutex > &synchronized, Args &&...args)
static FOLLY_TLS int unlockCount_
int current
Function< void()> Func
Definition: Executor.h:27
testing::Types< folly::SharedMutexReadPriority, folly::SharedMutexWritePriority, std::mutex, std::recursive_mutex, boost::mutex, boost::recursive_mutex, boost::shared_mutex, folly::SpinLock > SynchronizedTestTypes
bool try_unlock_upgrade_and_lock_for(const std::chrono::duration< Rep, Period > &arg)
void fail()
TYPED_TEST(SynchronizedTest, Basic)
#define UPGRADE
Definition: http_parser.c:144
static FOLLY_TLS int lockCount_
std::function< void()> onUnlock_
int numTimesUnlocked
auto lock(Synchronized< D, M > &synchronized, Args &&...args)
std::function< void()> onLock_
folly::Function< void()> onLock
std::pair< int, int > CountPair
std::function< void()> onUnlockShared_
TEST_F(AsyncSSLSocketWriteTest, write_coalescing1)
bool try_lock_for(const std::chrono::duration< Rep, Period > &)
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184
bool try_lock_for(const std::chrono::duration< Rep, Period > &arg)
static int gId
testing::Types< folly::SharedMutexReadPriority, folly::SharedMutexWritePriority > SynchronizedTimedWithConstTestTypes
std::mutex mutex
static CountPair getLockUnlockCount()
static set< string > s
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
auto ulock(Synchronized< D, M > &synchronized, Args &&...args)
SharedMutexImpl< false > SharedMutexWritePriority
Definition: SharedMutex.h:1510
int numTimesLocked
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
int order
std::function< void()> onLockShared_
TEST(SequencedExecutor, CPUThreadPoolExecutor)
bool try_lock_upgrade_for(const std::chrono::duration< Rep, Period > &)
bool try_unlock_upgrade_and_lock_for(const std::chrono::duration< Rep, Period > &)