Futex::futexWait returns FutexResult
[folly.git] / folly / test / SynchronizedTest.cpp
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/LockTraitsBoost.h>
22 #include <folly/Portability.h>
23 #include <folly/SharedMutex.h>
24 #include <folly/SpinLock.h>
25 #include <folly/portability/GTest.h>
26 #include <folly/synchronization/RWSpinLock.h>
27 #include <folly/test/SynchronizedTestLib.h>
28
29 using namespace folly::sync_tests;
30
31 template <class Mutex>
32 class SynchronizedTest : public testing::Test {};
33
34 using SynchronizedTestTypes = testing::Types<
35     folly::SharedMutexReadPriority,
36     folly::SharedMutexWritePriority,
37     std::mutex,
38     std::recursive_mutex,
39 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
40     std::timed_mutex,
41     std::recursive_timed_mutex,
42 #endif
43     boost::mutex,
44     boost::recursive_mutex,
45 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
46     boost::timed_mutex,
47     boost::recursive_timed_mutex,
48 #endif
49 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
50     folly::RWTicketSpinLock32,
51     folly::RWTicketSpinLock64,
52 #endif
53     boost::shared_mutex,
54     folly::SpinLock>;
55 TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes);
56
57 TYPED_TEST(SynchronizedTest, Basic) {
58   testBasic<TypeParam>();
59 }
60
61 TYPED_TEST(SynchronizedTest, WithLock) {
62   testWithLock<TypeParam>();
63 }
64
65 TYPED_TEST(SynchronizedTest, Unlock) {
66   testUnlock<TypeParam>();
67 }
68
69 TYPED_TEST(SynchronizedTest, Deprecated) {
70   testDeprecated<TypeParam>();
71 }
72
73 TYPED_TEST(SynchronizedTest, Concurrency) {
74   testConcurrency<TypeParam>();
75 }
76
77 TYPED_TEST(SynchronizedTest, AcquireLocked) {
78   testAcquireLocked<TypeParam>();
79 }
80
81 TYPED_TEST(SynchronizedTest, AcquireLockedWithConst) {
82   testAcquireLockedWithConst<TypeParam>();
83 }
84
85 TYPED_TEST(SynchronizedTest, DualLocking) {
86   testDualLocking<TypeParam>();
87 }
88
89 TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
90   testDualLockingWithConst<TypeParam>();
91 }
92
93 TYPED_TEST(SynchronizedTest, ConstCopy) {
94   testConstCopy<TypeParam>();
95 }
96
97 TYPED_TEST(SynchronizedTest, InPlaceConstruction) {
98   testInPlaceConstruction<TypeParam>();
99 }
100
101 TYPED_TEST(SynchronizedTest, Exchange) {
102   testExchange<TypeParam>();
103 }
104
105 template <class Mutex>
106 class SynchronizedTimedTest : public testing::Test {};
107
108 using SynchronizedTimedTestTypes = testing::Types<
109 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
110     std::timed_mutex,
111     std::recursive_timed_mutex,
112     boost::timed_mutex,
113     boost::recursive_timed_mutex,
114     boost::shared_mutex,
115 #endif
116 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
117     folly::RWTicketSpinLock32,
118     folly::RWTicketSpinLock64,
119 #endif
120     folly::SharedMutexReadPriority,
121     folly::SharedMutexWritePriority>;
122 TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
123
124 TYPED_TEST(SynchronizedTimedTest, Timed) {
125   testTimed<TypeParam>();
126 }
127
128 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
129   testTimedSynchronized<TypeParam>();
130 }
131
132 template <class Mutex>
133 class SynchronizedTimedWithConstTest : public testing::Test {};
134
135 using SynchronizedTimedWithConstTestTypes = testing::Types<
136 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
137     boost::shared_mutex,
138 #endif
139 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
140     folly::RWTicketSpinLock32,
141     folly::RWTicketSpinLock64,
142 #endif
143     folly::SharedMutexReadPriority,
144     folly::SharedMutexWritePriority>;
145 TYPED_TEST_CASE(
146     SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes);
147
148 TYPED_TEST(SynchronizedTimedWithConstTest, TimedShared) {
149   testTimedShared<TypeParam>();
150 }
151
152 TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
153   testTimedSynchronizedWithConst<TypeParam>();
154 }
155
156 using CountPair = std::pair<int, int>;
157 // This class is specialized only to be uesed in SynchronizedLockTest
158 class FakeMutex {
159  public:
160   void lock() {
161     ++lockCount_;
162   }
163
164   void unlock() {
165     ++unlockCount_;
166   }
167
168   static CountPair getLockUnlockCount() {
169     return CountPair{lockCount_, unlockCount_};
170   }
171
172   static void resetLockUnlockCount() {
173     lockCount_ = 0;
174     unlockCount_ = 0;
175   }
176  private:
177   // Keep these two static for test access
178   // Keep them thread_local in case of tests are run in parallel within one
179   // process
180   static FOLLY_TLS int lockCount_;
181   static FOLLY_TLS int unlockCount_;
182 };
183 FOLLY_TLS int FakeMutex::lockCount_{0};
184 FOLLY_TLS int FakeMutex::unlockCount_{0};
185
186 // SynchronizedLockTest is used to verify the correct lock unlock behavior
187 // happens per design
188 class SynchronizedLockTest : public testing::Test {
189  public:
190   void SetUp() override {
191     FakeMutex::resetLockUnlockCount();
192   }
193 };
194
195 /**
196  * Test mutex to help to automate assertions, taken from LockTraitsTest.cpp
197  */
198 class FakeAllPowerfulAssertingMutexInternal {
199  public:
200   enum class CurrentLockState { UNLOCKED, SHARED, UPGRADE, UNIQUE };
201
202   void lock() {
203     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
204     this->lock_state = CurrentLockState::UNIQUE;
205   }
206   void unlock() {
207     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
208     this->lock_state = CurrentLockState::UNLOCKED;
209   }
210   void lock_shared() {
211     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
212     this->lock_state = CurrentLockState::SHARED;
213   }
214   void unlock_shared() {
215     EXPECT_EQ(this->lock_state, CurrentLockState::SHARED);
216     this->lock_state = CurrentLockState::UNLOCKED;
217   }
218   void lock_upgrade() {
219     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
220     this->lock_state = CurrentLockState::UPGRADE;
221   }
222   void unlock_upgrade() {
223     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
224     this->lock_state = CurrentLockState::UNLOCKED;
225   }
226
227   void unlock_upgrade_and_lock() {
228     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
229     this->lock_state = CurrentLockState::UNIQUE;
230   }
231   void unlock_and_lock_upgrade() {
232     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
233     this->lock_state = CurrentLockState::UPGRADE;
234   }
235   void unlock_and_lock_shared() {
236     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
237     this->lock_state = CurrentLockState::SHARED;
238   }
239   void unlock_upgrade_and_lock_shared() {
240     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
241     this->lock_state = CurrentLockState::SHARED;
242   }
243
244   template <class Rep, class Period>
245   bool try_lock_for(const std::chrono::duration<Rep, Period>&) {
246     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
247     this->lock_state = CurrentLockState::UNIQUE;
248     return true;
249   }
250
251   template <class Rep, class Period>
252   bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>&) {
253     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
254     this->lock_state = CurrentLockState::UPGRADE;
255     return true;
256   }
257
258   template <class Rep, class Period>
259   bool try_unlock_upgrade_and_lock_for(
260       const std::chrono::duration<Rep, Period>&) {
261     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
262     this->lock_state = CurrentLockState::UNIQUE;
263     return true;
264   }
265
266   /*
267    * Initialize the FakeMutex with an unlocked state
268    */
269   CurrentLockState lock_state{CurrentLockState::UNLOCKED};
270 };
271
272 /**
273  * The following works around the internal mutex for synchronized being
274  * private
275  *
276  * This is horridly thread unsafe.
277  */
278 static FakeAllPowerfulAssertingMutexInternal globalAllPowerfulAssertingMutex;
279
280 class FakeAllPowerfulAssertingMutex {
281  public:
282   void lock() {
283     globalAllPowerfulAssertingMutex.lock();
284   }
285   void unlock() {
286     globalAllPowerfulAssertingMutex.unlock();
287   }
288   void lock_shared() {
289     globalAllPowerfulAssertingMutex.lock_shared();
290   }
291   void unlock_shared() {
292     globalAllPowerfulAssertingMutex.unlock_shared();
293   }
294   void lock_upgrade() {
295     globalAllPowerfulAssertingMutex.lock_upgrade();
296   }
297   void unlock_upgrade() {
298     globalAllPowerfulAssertingMutex.unlock_upgrade();
299   }
300
301   void unlock_upgrade_and_lock() {
302     globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock();
303   }
304   void unlock_and_lock_upgrade() {
305     globalAllPowerfulAssertingMutex.unlock_and_lock_upgrade();
306   }
307   void unlock_and_lock_shared() {
308     globalAllPowerfulAssertingMutex.unlock_and_lock_shared();
309   }
310   void unlock_upgrade_and_lock_shared() {
311     globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock_shared();
312   }
313
314   template <class Rep, class Period>
315   bool try_lock_for(const std::chrono::duration<Rep, Period>& arg) {
316     return globalAllPowerfulAssertingMutex.try_lock_for(arg);
317   }
318
319   template <class Rep, class Period>
320   bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>& arg) {
321     return globalAllPowerfulAssertingMutex.try_lock_upgrade_for(arg);
322   }
323
324   template <class Rep, class Period>
325   bool try_unlock_upgrade_and_lock_for(
326       const std::chrono::duration<Rep, Period>& arg) {
327     return globalAllPowerfulAssertingMutex.try_unlock_upgrade_and_lock_for(arg);
328   }
329
330   // reset state on destruction
331   ~FakeAllPowerfulAssertingMutex() {
332     globalAllPowerfulAssertingMutex = FakeAllPowerfulAssertingMutexInternal{};
333   }
334 };
335
336 TEST_F(SynchronizedLockTest, TestCopyConstructibleValues) {
337   struct NonCopyConstructible {
338     NonCopyConstructible(const NonCopyConstructible&) = delete;
339     NonCopyConstructible& operator=(const NonCopyConstructible&) = delete;
340   };
341   struct CopyConstructible {};
342   EXPECT_FALSE(std::is_copy_constructible<
343                folly::Synchronized<NonCopyConstructible>>::value);
344   EXPECT_FALSE(std::is_copy_assignable<
345                folly::Synchronized<NonCopyConstructible>>::value);
346   EXPECT_TRUE(std::is_copy_constructible<
347               folly::Synchronized<CopyConstructible>>::value);
348   EXPECT_TRUE(
349       std::is_copy_assignable<folly::Synchronized<CopyConstructible>>::value);
350 }
351
352 TEST_F(SynchronizedLockTest, UpgradableLocking) {
353   folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
354
355   // sanity assert
356   static_assert(
357       std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
358       "The ulock function was not well configured, blame aary@instagram.com");
359
360   {
361     auto ulock = sync.ulock();
362     EXPECT_EQ(
363         globalAllPowerfulAssertingMutex.lock_state,
364         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
365   }
366
367   // should be unlocked here
368   EXPECT_EQ(
369       globalAllPowerfulAssertingMutex.lock_state,
370       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
371
372   // test going from upgrade to exclusive
373   {
374     auto ulock = sync.ulock();
375     auto wlock = ulock.moveFromUpgradeToWrite();
376     EXPECT_EQ(static_cast<bool>(ulock), false);
377     EXPECT_EQ(
378         globalAllPowerfulAssertingMutex.lock_state,
379         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
380   }
381
382   // should be unlocked here
383   EXPECT_EQ(
384       globalAllPowerfulAssertingMutex.lock_state,
385       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
386
387   // test going from upgrade to shared
388   {
389     auto ulock = sync.ulock();
390     auto slock = ulock.moveFromUpgradeToRead();
391     EXPECT_EQ(static_cast<bool>(ulock), false);
392     EXPECT_EQ(
393         globalAllPowerfulAssertingMutex.lock_state,
394         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
395   }
396
397   // should be unlocked here
398   EXPECT_EQ(
399       globalAllPowerfulAssertingMutex.lock_state,
400       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
401
402   // test going from exclusive to upgrade
403   {
404     auto wlock = sync.wlock();
405     auto ulock = wlock.moveFromWriteToUpgrade();
406     EXPECT_EQ(static_cast<bool>(wlock), false);
407     EXPECT_EQ(
408         globalAllPowerfulAssertingMutex.lock_state,
409         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
410   }
411
412   // should be unlocked here
413   EXPECT_EQ(
414       globalAllPowerfulAssertingMutex.lock_state,
415       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
416
417   // test going from exclusive to shared
418   {
419     auto wlock = sync.wlock();
420     auto slock = wlock.moveFromWriteToRead();
421     EXPECT_EQ(static_cast<bool>(wlock), false);
422     EXPECT_EQ(
423         globalAllPowerfulAssertingMutex.lock_state,
424         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
425   }
426
427   // should be unlocked here
428   EXPECT_EQ(
429       globalAllPowerfulAssertingMutex.lock_state,
430       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
431 }
432
433 TEST_F(SynchronizedLockTest, UpgradableLockingWithULock) {
434   folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
435
436   // sanity assert
437   static_assert(
438       std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
439       "The ulock function was not well configured, blame aary@instagram.com");
440
441   // test from upgrade to write
442   sync.withULockPtr([](auto ulock) {
443     EXPECT_EQ(static_cast<bool>(ulock), true);
444     EXPECT_EQ(
445         globalAllPowerfulAssertingMutex.lock_state,
446         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
447
448     auto wlock = ulock.moveFromUpgradeToWrite();
449     EXPECT_EQ(static_cast<bool>(ulock), false);
450     EXPECT_EQ(
451         globalAllPowerfulAssertingMutex.lock_state,
452         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
453   });
454
455   // should be unlocked here
456   EXPECT_EQ(
457       globalAllPowerfulAssertingMutex.lock_state,
458       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
459
460   // test from write to upgrade
461   sync.withWLockPtr([](auto wlock) {
462     EXPECT_EQ(static_cast<bool>(wlock), true);
463     EXPECT_EQ(
464         globalAllPowerfulAssertingMutex.lock_state,
465         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
466
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 from upgrade to shared
480   sync.withULockPtr([](auto ulock) {
481     EXPECT_EQ(static_cast<bool>(ulock), true);
482     EXPECT_EQ(
483         globalAllPowerfulAssertingMutex.lock_state,
484         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
485
486     auto slock = ulock.moveFromUpgradeToRead();
487     EXPECT_EQ(static_cast<bool>(ulock), false);
488     EXPECT_EQ(
489         globalAllPowerfulAssertingMutex.lock_state,
490         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
491   });
492
493   // should be unlocked here
494   EXPECT_EQ(
495       globalAllPowerfulAssertingMutex.lock_state,
496       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
497
498   // test from write to shared
499   sync.withWLockPtr([](auto wlock) {
500     EXPECT_EQ(static_cast<bool>(wlock), true);
501     EXPECT_EQ(
502         globalAllPowerfulAssertingMutex.lock_state,
503         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
504
505     auto slock = wlock.moveFromWriteToRead();
506     EXPECT_EQ(static_cast<bool>(wlock), false);
507     EXPECT_EQ(
508         globalAllPowerfulAssertingMutex.lock_state,
509         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
510   });
511
512   // should be unlocked here
513   EXPECT_EQ(
514       globalAllPowerfulAssertingMutex.lock_state,
515       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
516 }