Fix SimpleBarrier
[folly.git] / folly / test / SynchronizedTest.cpp
1 /*
2  * Copyright 2016 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
17 // @author: Andrei Alexandrescu (aalexandre)
18
19 // Test bed for folly/Synchronized.h
20
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>
29
30 using namespace folly::sync_tests;
31
32 template <class Mutex>
33 class SynchronizedTest : public testing::Test {};
34
35 using SynchronizedTestTypes = testing::Types<
36     folly::SharedMutexReadPriority,
37     folly::SharedMutexWritePriority,
38     std::mutex,
39     std::recursive_mutex,
40 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
41     std::timed_mutex,
42     std::recursive_timed_mutex,
43 #endif
44     boost::mutex,
45     boost::recursive_mutex,
46 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
47     boost::timed_mutex,
48     boost::recursive_timed_mutex,
49 #endif
50 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
51     folly::RWTicketSpinLock32,
52     folly::RWTicketSpinLock64,
53 #endif
54     boost::shared_mutex,
55     folly::SpinLock>;
56 TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes);
57
58 TYPED_TEST(SynchronizedTest, Basic) {
59   testBasic<TypeParam>();
60 }
61
62 TYPED_TEST(SynchronizedTest, WithLock) {
63   testWithLock<TypeParam>();
64 }
65
66 TYPED_TEST(SynchronizedTest, Unlock) {
67   testUnlock<TypeParam>();
68 }
69
70 TYPED_TEST(SynchronizedTest, Deprecated) {
71   testDeprecated<TypeParam>();
72 }
73
74 TYPED_TEST(SynchronizedTest, Concurrency) {
75   testConcurrency<TypeParam>();
76 }
77
78 TYPED_TEST(SynchronizedTest, AcquireLocked) {
79   testAcquireLocked<TypeParam>();
80 }
81
82 TYPED_TEST(SynchronizedTest, AcquireLockedWithConst) {
83   testAcquireLockedWithConst<TypeParam>();
84 }
85
86 TYPED_TEST(SynchronizedTest, DualLocking) {
87   testDualLocking<TypeParam>();
88 }
89
90 TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
91   testDualLockingWithConst<TypeParam>();
92 }
93
94 TYPED_TEST(SynchronizedTest, ConstCopy) {
95   testConstCopy<TypeParam>();
96 }
97
98 template <class Mutex>
99 class SynchronizedTimedTest : public testing::Test {};
100
101 using SynchronizedTimedTestTypes = testing::Types<
102 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
103     std::timed_mutex,
104     std::recursive_timed_mutex,
105     boost::timed_mutex,
106     boost::recursive_timed_mutex,
107     boost::shared_mutex,
108 #endif
109 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
110     folly::RWTicketSpinLock32,
111     folly::RWTicketSpinLock64,
112 #endif
113     folly::SharedMutexReadPriority,
114     folly::SharedMutexWritePriority>;
115 TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
116
117 TYPED_TEST(SynchronizedTimedTest, Timed) {
118   testTimed<TypeParam>();
119 }
120
121 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
122   testTimedSynchronized<TypeParam>();
123 }
124
125 template <class Mutex>
126 class SynchronizedTimedWithConstTest : public testing::Test {};
127
128 using SynchronizedTimedWithConstTestTypes = testing::Types<
129 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
130     boost::shared_mutex,
131 #endif
132 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
133     folly::RWTicketSpinLock32,
134     folly::RWTicketSpinLock64,
135 #endif
136     folly::SharedMutexReadPriority,
137     folly::SharedMutexWritePriority>;
138 TYPED_TEST_CASE(
139     SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes);
140
141 TYPED_TEST(SynchronizedTimedWithConstTest, TimedShared) {
142   testTimedShared<TypeParam>();
143 }
144
145 TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
146   testTimedSynchronizedWithConst<TypeParam>();
147 }
148
149 TYPED_TEST(SynchronizedTest, InPlaceConstruction) {
150   testInPlaceConstruction<TypeParam>();
151 }
152
153 using CountPair = std::pair<int, int>;
154 // This class is specialized only to be uesed in SynchronizedLockTest
155 class FakeMutex {
156  public:
157   void lock() {
158     ++lockCount_;
159   }
160
161   void unlock() {
162     ++unlockCount_;
163   }
164
165   static CountPair getLockUnlockCount() {
166     return CountPair{lockCount_, unlockCount_};
167   }
168
169   static void resetLockUnlockCount() {
170     lockCount_ = 0;
171     unlockCount_ = 0;
172   }
173  private:
174   // Keep these two static for test access
175   // Keep them thread_local in case of tests are run in parallel within one
176   // process
177   static FOLLY_TLS int lockCount_;
178   static FOLLY_TLS int unlockCount_;
179 };
180 FOLLY_TLS int FakeMutex::lockCount_{0};
181 FOLLY_TLS int FakeMutex::unlockCount_{0};
182
183 // SynchronizedLockTest is used to verify the correct lock unlock behavior
184 // happens per design
185 class SynchronizedLockTest : public testing::Test {
186  public:
187   void SetUp() override {
188     FakeMutex::resetLockUnlockCount();
189   }
190 };
191
192 /**
193  * Test mutex to help to automate assertions, taken from LockTraitsTest.cpp
194  */
195 class FakeAllPowerfulAssertingMutexInternal {
196  public:
197   enum class CurrentLockState { UNLOCKED, SHARED, UPGRADE, UNIQUE };
198
199   void lock() {
200     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
201     this->lock_state = CurrentLockState::UNIQUE;
202   }
203   void unlock() {
204     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
205     this->lock_state = CurrentLockState::UNLOCKED;
206   }
207   void lock_shared() {
208     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
209     this->lock_state = CurrentLockState::SHARED;
210   }
211   void unlock_shared() {
212     EXPECT_EQ(this->lock_state, CurrentLockState::SHARED);
213     this->lock_state = CurrentLockState::UNLOCKED;
214   }
215   void lock_upgrade() {
216     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
217     this->lock_state = CurrentLockState::UPGRADE;
218   }
219   void unlock_upgrade() {
220     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
221     this->lock_state = CurrentLockState::UNLOCKED;
222   }
223
224   void unlock_upgrade_and_lock() {
225     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
226     this->lock_state = CurrentLockState::UNIQUE;
227   }
228   void unlock_and_lock_upgrade() {
229     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
230     this->lock_state = CurrentLockState::UPGRADE;
231   }
232   void unlock_and_lock_shared() {
233     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
234     this->lock_state = CurrentLockState::SHARED;
235   }
236   void unlock_upgrade_and_lock_shared() {
237     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
238     this->lock_state = CurrentLockState::SHARED;
239   }
240
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;
245     return true;
246   }
247
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;
252     return true;
253   }
254
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;
260     return true;
261   }
262
263   /*
264    * Initialize the FakeMutex with an unlocked state
265    */
266   CurrentLockState lock_state{CurrentLockState::UNLOCKED};
267 };
268
269 /**
270  * The following works around the internal mutex for synchronized being
271  * private
272  *
273  * This is horridly thread unsafe.
274  */
275 static FakeAllPowerfulAssertingMutexInternal globalAllPowerfulAssertingMutex;
276
277 class FakeAllPowerfulAssertingMutex {
278  public:
279   void lock() {
280     globalAllPowerfulAssertingMutex.lock();
281   }
282   void unlock() {
283     globalAllPowerfulAssertingMutex.unlock();
284   }
285   void lock_shared() {
286     globalAllPowerfulAssertingMutex.lock_shared();
287   }
288   void unlock_shared() {
289     globalAllPowerfulAssertingMutex.unlock_shared();
290   }
291   void lock_upgrade() {
292     globalAllPowerfulAssertingMutex.lock_upgrade();
293   }
294   void unlock_upgrade() {
295     globalAllPowerfulAssertingMutex.unlock_upgrade();
296   }
297
298   void unlock_upgrade_and_lock() {
299     globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock();
300   }
301   void unlock_and_lock_upgrade() {
302     globalAllPowerfulAssertingMutex.unlock_and_lock_upgrade();
303   }
304   void unlock_and_lock_shared() {
305     globalAllPowerfulAssertingMutex.unlock_and_lock_shared();
306   }
307   void unlock_upgrade_and_lock_shared() {
308     globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock_shared();
309   }
310
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);
314   }
315
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);
319   }
320
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);
325   }
326
327   // reset state on destruction
328   ~FakeAllPowerfulAssertingMutex() {
329     globalAllPowerfulAssertingMutex = FakeAllPowerfulAssertingMutexInternal{};
330   }
331 };
332
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
335 // fail
336 TEST_F(SynchronizedLockTest, SyncUnSync) {
337   folly::Synchronized<std::vector<int>, FakeMutex> obj;
338   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
339   SYNCHRONIZED(obj) {
340     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
341     UNSYNCHRONIZED(obj) {
342       EXPECT_EQ((CountPair{1, 1}), FakeMutex::getLockUnlockCount());
343     }
344     EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
345   }
346   EXPECT_EQ((CountPair{2, 2}), FakeMutex::getLockUnlockCount());
347 }
348
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());
355     SYNCHRONIZED(obj) {
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());
363       }
364       EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
365       UNSYNCHRONIZED(obj) {
366         EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
367       }
368       EXPECT_EQ((CountPair{4, 2}), FakeMutex::getLockUnlockCount());
369     }
370     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
371   }
372   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
373 }
374
375 // Different nesting behavior, UNSYNCHRONIZED called on different depth of
376 // SYNCHRONIZED
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());
382     SYNCHRONIZED(obj) {
383       EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
384       UNSYNCHRONIZED(obj) {
385         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
386       }
387       EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
388     }
389     EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
390     UNSYNCHRONIZED(obj) {
391       EXPECT_EQ((CountPair{3, 3}), FakeMutex::getLockUnlockCount());
392     }
393     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
394   }
395   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
396 }
397
398 TEST_F(SynchronizedLockTest, TestCopyConstructibleValues) {
399   struct NonCopyConstructible {
400     NonCopyConstructible(const NonCopyConstructible&) = delete;
401     NonCopyConstructible& operator=(const NonCopyConstructible&) = delete;
402   };
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);
410   EXPECT_TRUE(
411       std::is_copy_assignable<folly::Synchronized<CopyConstructible>>::value);
412 }
413
414 TEST_F(SynchronizedLockTest, UpgradableLocking) {
415   folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
416
417   // sanity assert
418   static_assert(
419       std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
420       "The ulock function was not well configured, blame aary@instagram.com");
421
422   {
423     auto ulock = sync.ulock();
424     EXPECT_EQ(
425         globalAllPowerfulAssertingMutex.lock_state,
426         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
427   }
428
429   // should be unlocked here
430   EXPECT_EQ(
431       globalAllPowerfulAssertingMutex.lock_state,
432       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
433
434   // test going from upgrade to exclusive
435   {
436     auto ulock = sync.ulock();
437     auto wlock = ulock.moveFromUpgradeToWrite();
438     EXPECT_EQ(static_cast<bool>(ulock), false);
439     EXPECT_EQ(
440         globalAllPowerfulAssertingMutex.lock_state,
441         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
442   }
443
444   // should be unlocked here
445   EXPECT_EQ(
446       globalAllPowerfulAssertingMutex.lock_state,
447       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
448
449   // test going from upgrade to shared
450   {
451     auto ulock = sync.ulock();
452     auto slock = ulock.moveFromUpgradeToRead();
453     EXPECT_EQ(static_cast<bool>(ulock), false);
454     EXPECT_EQ(
455         globalAllPowerfulAssertingMutex.lock_state,
456         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
457   }
458
459   // should be unlocked here
460   EXPECT_EQ(
461       globalAllPowerfulAssertingMutex.lock_state,
462       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
463
464   // test going from exclusive to upgrade
465   {
466     auto wlock = sync.wlock();
467     auto ulock = wlock.moveFromWriteToUpgrade();
468     EXPECT_EQ(static_cast<bool>(wlock), false);
469     EXPECT_EQ(
470         globalAllPowerfulAssertingMutex.lock_state,
471         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
472   }
473
474   // should be unlocked here
475   EXPECT_EQ(
476       globalAllPowerfulAssertingMutex.lock_state,
477       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
478
479   // test going from exclusive to shared
480   {
481     auto wlock = sync.wlock();
482     auto slock = wlock.moveFromWriteToRead();
483     EXPECT_EQ(static_cast<bool>(wlock), false);
484     EXPECT_EQ(
485         globalAllPowerfulAssertingMutex.lock_state,
486         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
487   }
488
489   // should be unlocked here
490   EXPECT_EQ(
491       globalAllPowerfulAssertingMutex.lock_state,
492       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
493 }
494
495 TEST_F(SynchronizedLockTest, UpgradableLockingWithULock) {
496   folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
497
498   // sanity assert
499   static_assert(
500       std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
501       "The ulock function was not well configured, blame aary@instagram.com");
502
503   // test from upgrade to write
504   sync.withULockPtr([](auto ulock) {
505     EXPECT_EQ(static_cast<bool>(ulock), true);
506     EXPECT_EQ(
507         globalAllPowerfulAssertingMutex.lock_state,
508         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
509
510     auto wlock = ulock.moveFromUpgradeToWrite();
511     EXPECT_EQ(static_cast<bool>(ulock), false);
512     EXPECT_EQ(
513         globalAllPowerfulAssertingMutex.lock_state,
514         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
515   });
516
517   // should be unlocked here
518   EXPECT_EQ(
519       globalAllPowerfulAssertingMutex.lock_state,
520       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
521
522   // test from write to upgrade
523   sync.withWLockPtr([](auto wlock) {
524     EXPECT_EQ(static_cast<bool>(wlock), true);
525     EXPECT_EQ(
526         globalAllPowerfulAssertingMutex.lock_state,
527         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
528
529     auto ulock = wlock.moveFromWriteToUpgrade();
530     EXPECT_EQ(static_cast<bool>(wlock), false);
531     EXPECT_EQ(
532         globalAllPowerfulAssertingMutex.lock_state,
533         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
534   });
535
536   // should be unlocked here
537   EXPECT_EQ(
538       globalAllPowerfulAssertingMutex.lock_state,
539       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
540
541   // test from upgrade to shared
542   sync.withULockPtr([](auto ulock) {
543     EXPECT_EQ(static_cast<bool>(ulock), true);
544     EXPECT_EQ(
545         globalAllPowerfulAssertingMutex.lock_state,
546         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
547
548     auto slock = ulock.moveFromUpgradeToRead();
549     EXPECT_EQ(static_cast<bool>(ulock), false);
550     EXPECT_EQ(
551         globalAllPowerfulAssertingMutex.lock_state,
552         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
553   });
554
555   // should be unlocked here
556   EXPECT_EQ(
557       globalAllPowerfulAssertingMutex.lock_state,
558       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
559
560   // test from write to shared
561   sync.withWLockPtr([](auto wlock) {
562     EXPECT_EQ(static_cast<bool>(wlock), true);
563     EXPECT_EQ(
564         globalAllPowerfulAssertingMutex.lock_state,
565         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
566
567     auto slock = wlock.moveFromWriteToRead();
568     EXPECT_EQ(static_cast<bool>(wlock), false);
569     EXPECT_EQ(
570         globalAllPowerfulAssertingMutex.lock_state,
571         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
572   });
573
574   // should be unlocked here
575   EXPECT_EQ(
576       globalAllPowerfulAssertingMutex.lock_state,
577       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
578 }