Move folly/RWSpinLock.h to folly/synchronization/
[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 template <class Mutex>
102 class SynchronizedTimedTest : public testing::Test {};
103
104 using SynchronizedTimedTestTypes = testing::Types<
105 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
106     std::timed_mutex,
107     std::recursive_timed_mutex,
108     boost::timed_mutex,
109     boost::recursive_timed_mutex,
110     boost::shared_mutex,
111 #endif
112 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
113     folly::RWTicketSpinLock32,
114     folly::RWTicketSpinLock64,
115 #endif
116     folly::SharedMutexReadPriority,
117     folly::SharedMutexWritePriority>;
118 TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
119
120 TYPED_TEST(SynchronizedTimedTest, Timed) {
121   testTimed<TypeParam>();
122 }
123
124 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
125   testTimedSynchronized<TypeParam>();
126 }
127
128 template <class Mutex>
129 class SynchronizedTimedWithConstTest : public testing::Test {};
130
131 using SynchronizedTimedWithConstTestTypes = testing::Types<
132 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
133     boost::shared_mutex,
134 #endif
135 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
136     folly::RWTicketSpinLock32,
137     folly::RWTicketSpinLock64,
138 #endif
139     folly::SharedMutexReadPriority,
140     folly::SharedMutexWritePriority>;
141 TYPED_TEST_CASE(
142     SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes);
143
144 TYPED_TEST(SynchronizedTimedWithConstTest, TimedShared) {
145   testTimedShared<TypeParam>();
146 }
147
148 TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
149   testTimedSynchronizedWithConst<TypeParam>();
150 }
151
152 using CountPair = std::pair<int, int>;
153 // This class is specialized only to be uesed in SynchronizedLockTest
154 class FakeMutex {
155  public:
156   void lock() {
157     ++lockCount_;
158   }
159
160   void unlock() {
161     ++unlockCount_;
162   }
163
164   static CountPair getLockUnlockCount() {
165     return CountPair{lockCount_, unlockCount_};
166   }
167
168   static void resetLockUnlockCount() {
169     lockCount_ = 0;
170     unlockCount_ = 0;
171   }
172  private:
173   // Keep these two static for test access
174   // Keep them thread_local in case of tests are run in parallel within one
175   // process
176   static FOLLY_TLS int lockCount_;
177   static FOLLY_TLS int unlockCount_;
178 };
179 FOLLY_TLS int FakeMutex::lockCount_{0};
180 FOLLY_TLS int FakeMutex::unlockCount_{0};
181
182 // SynchronizedLockTest is used to verify the correct lock unlock behavior
183 // happens per design
184 class SynchronizedLockTest : public testing::Test {
185  public:
186   void SetUp() override {
187     FakeMutex::resetLockUnlockCount();
188   }
189 };
190
191 /**
192  * Test mutex to help to automate assertions, taken from LockTraitsTest.cpp
193  */
194 class FakeAllPowerfulAssertingMutexInternal {
195  public:
196   enum class CurrentLockState { UNLOCKED, SHARED, UPGRADE, UNIQUE };
197
198   void lock() {
199     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
200     this->lock_state = CurrentLockState::UNIQUE;
201   }
202   void unlock() {
203     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
204     this->lock_state = CurrentLockState::UNLOCKED;
205   }
206   void lock_shared() {
207     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
208     this->lock_state = CurrentLockState::SHARED;
209   }
210   void unlock_shared() {
211     EXPECT_EQ(this->lock_state, CurrentLockState::SHARED);
212     this->lock_state = CurrentLockState::UNLOCKED;
213   }
214   void lock_upgrade() {
215     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
216     this->lock_state = CurrentLockState::UPGRADE;
217   }
218   void unlock_upgrade() {
219     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
220     this->lock_state = CurrentLockState::UNLOCKED;
221   }
222
223   void unlock_upgrade_and_lock() {
224     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
225     this->lock_state = CurrentLockState::UNIQUE;
226   }
227   void unlock_and_lock_upgrade() {
228     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
229     this->lock_state = CurrentLockState::UPGRADE;
230   }
231   void unlock_and_lock_shared() {
232     EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
233     this->lock_state = CurrentLockState::SHARED;
234   }
235   void unlock_upgrade_and_lock_shared() {
236     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
237     this->lock_state = CurrentLockState::SHARED;
238   }
239
240   template <class Rep, class Period>
241   bool try_lock_for(const std::chrono::duration<Rep, Period>&) {
242     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
243     this->lock_state = CurrentLockState::UNIQUE;
244     return true;
245   }
246
247   template <class Rep, class Period>
248   bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>&) {
249     EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
250     this->lock_state = CurrentLockState::UPGRADE;
251     return true;
252   }
253
254   template <class Rep, class Period>
255   bool try_unlock_upgrade_and_lock_for(
256       const std::chrono::duration<Rep, Period>&) {
257     EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
258     this->lock_state = CurrentLockState::UNIQUE;
259     return true;
260   }
261
262   /*
263    * Initialize the FakeMutex with an unlocked state
264    */
265   CurrentLockState lock_state{CurrentLockState::UNLOCKED};
266 };
267
268 /**
269  * The following works around the internal mutex for synchronized being
270  * private
271  *
272  * This is horridly thread unsafe.
273  */
274 static FakeAllPowerfulAssertingMutexInternal globalAllPowerfulAssertingMutex;
275
276 class FakeAllPowerfulAssertingMutex {
277  public:
278   void lock() {
279     globalAllPowerfulAssertingMutex.lock();
280   }
281   void unlock() {
282     globalAllPowerfulAssertingMutex.unlock();
283   }
284   void lock_shared() {
285     globalAllPowerfulAssertingMutex.lock_shared();
286   }
287   void unlock_shared() {
288     globalAllPowerfulAssertingMutex.unlock_shared();
289   }
290   void lock_upgrade() {
291     globalAllPowerfulAssertingMutex.lock_upgrade();
292   }
293   void unlock_upgrade() {
294     globalAllPowerfulAssertingMutex.unlock_upgrade();
295   }
296
297   void unlock_upgrade_and_lock() {
298     globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock();
299   }
300   void unlock_and_lock_upgrade() {
301     globalAllPowerfulAssertingMutex.unlock_and_lock_upgrade();
302   }
303   void unlock_and_lock_shared() {
304     globalAllPowerfulAssertingMutex.unlock_and_lock_shared();
305   }
306   void unlock_upgrade_and_lock_shared() {
307     globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock_shared();
308   }
309
310   template <class Rep, class Period>
311   bool try_lock_for(const std::chrono::duration<Rep, Period>& arg) {
312     return globalAllPowerfulAssertingMutex.try_lock_for(arg);
313   }
314
315   template <class Rep, class Period>
316   bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>& arg) {
317     return globalAllPowerfulAssertingMutex.try_lock_upgrade_for(arg);
318   }
319
320   template <class Rep, class Period>
321   bool try_unlock_upgrade_and_lock_for(
322       const std::chrono::duration<Rep, Period>& arg) {
323     return globalAllPowerfulAssertingMutex.try_unlock_upgrade_and_lock_for(arg);
324   }
325
326   // reset state on destruction
327   ~FakeAllPowerfulAssertingMutex() {
328     globalAllPowerfulAssertingMutex = FakeAllPowerfulAssertingMutexInternal{};
329   }
330 };
331
332 TEST_F(SynchronizedLockTest, TestCopyConstructibleValues) {
333   struct NonCopyConstructible {
334     NonCopyConstructible(const NonCopyConstructible&) = delete;
335     NonCopyConstructible& operator=(const NonCopyConstructible&) = delete;
336   };
337   struct CopyConstructible {};
338   EXPECT_FALSE(std::is_copy_constructible<
339                folly::Synchronized<NonCopyConstructible>>::value);
340   EXPECT_FALSE(std::is_copy_assignable<
341                folly::Synchronized<NonCopyConstructible>>::value);
342   EXPECT_TRUE(std::is_copy_constructible<
343               folly::Synchronized<CopyConstructible>>::value);
344   EXPECT_TRUE(
345       std::is_copy_assignable<folly::Synchronized<CopyConstructible>>::value);
346 }
347
348 TEST_F(SynchronizedLockTest, UpgradableLocking) {
349   folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
350
351   // sanity assert
352   static_assert(
353       std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
354       "The ulock function was not well configured, blame aary@instagram.com");
355
356   {
357     auto ulock = sync.ulock();
358     EXPECT_EQ(
359         globalAllPowerfulAssertingMutex.lock_state,
360         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
361   }
362
363   // should be unlocked here
364   EXPECT_EQ(
365       globalAllPowerfulAssertingMutex.lock_state,
366       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
367
368   // test going from upgrade to exclusive
369   {
370     auto ulock = sync.ulock();
371     auto wlock = ulock.moveFromUpgradeToWrite();
372     EXPECT_EQ(static_cast<bool>(ulock), false);
373     EXPECT_EQ(
374         globalAllPowerfulAssertingMutex.lock_state,
375         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
376   }
377
378   // should be unlocked here
379   EXPECT_EQ(
380       globalAllPowerfulAssertingMutex.lock_state,
381       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
382
383   // test going from upgrade to shared
384   {
385     auto ulock = sync.ulock();
386     auto slock = ulock.moveFromUpgradeToRead();
387     EXPECT_EQ(static_cast<bool>(ulock), false);
388     EXPECT_EQ(
389         globalAllPowerfulAssertingMutex.lock_state,
390         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
391   }
392
393   // should be unlocked here
394   EXPECT_EQ(
395       globalAllPowerfulAssertingMutex.lock_state,
396       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
397
398   // test going from exclusive to upgrade
399   {
400     auto wlock = sync.wlock();
401     auto ulock = wlock.moveFromWriteToUpgrade();
402     EXPECT_EQ(static_cast<bool>(wlock), false);
403     EXPECT_EQ(
404         globalAllPowerfulAssertingMutex.lock_state,
405         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
406   }
407
408   // should be unlocked here
409   EXPECT_EQ(
410       globalAllPowerfulAssertingMutex.lock_state,
411       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
412
413   // test going from exclusive to shared
414   {
415     auto wlock = sync.wlock();
416     auto slock = wlock.moveFromWriteToRead();
417     EXPECT_EQ(static_cast<bool>(wlock), 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
429 TEST_F(SynchronizedLockTest, UpgradableLockingWithULock) {
430   folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
431
432   // sanity assert
433   static_assert(
434       std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
435       "The ulock function was not well configured, blame aary@instagram.com");
436
437   // test from upgrade to write
438   sync.withULockPtr([](auto ulock) {
439     EXPECT_EQ(static_cast<bool>(ulock), true);
440     EXPECT_EQ(
441         globalAllPowerfulAssertingMutex.lock_state,
442         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
443
444     auto wlock = ulock.moveFromUpgradeToWrite();
445     EXPECT_EQ(static_cast<bool>(ulock), false);
446     EXPECT_EQ(
447         globalAllPowerfulAssertingMutex.lock_state,
448         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
449   });
450
451   // should be unlocked here
452   EXPECT_EQ(
453       globalAllPowerfulAssertingMutex.lock_state,
454       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
455
456   // test from write to upgrade
457   sync.withWLockPtr([](auto wlock) {
458     EXPECT_EQ(static_cast<bool>(wlock), true);
459     EXPECT_EQ(
460         globalAllPowerfulAssertingMutex.lock_state,
461         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
462
463     auto ulock = wlock.moveFromWriteToUpgrade();
464     EXPECT_EQ(static_cast<bool>(wlock), false);
465     EXPECT_EQ(
466         globalAllPowerfulAssertingMutex.lock_state,
467         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
468   });
469
470   // should be unlocked here
471   EXPECT_EQ(
472       globalAllPowerfulAssertingMutex.lock_state,
473       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
474
475   // test from upgrade to shared
476   sync.withULockPtr([](auto ulock) {
477     EXPECT_EQ(static_cast<bool>(ulock), true);
478     EXPECT_EQ(
479         globalAllPowerfulAssertingMutex.lock_state,
480         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
481
482     auto slock = ulock.moveFromUpgradeToRead();
483     EXPECT_EQ(static_cast<bool>(ulock), 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   // test from write to shared
495   sync.withWLockPtr([](auto wlock) {
496     EXPECT_EQ(static_cast<bool>(wlock), true);
497     EXPECT_EQ(
498         globalAllPowerfulAssertingMutex.lock_state,
499         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
500
501     auto slock = wlock.moveFromWriteToRead();
502     EXPECT_EQ(static_cast<bool>(wlock), false);
503     EXPECT_EQ(
504         globalAllPowerfulAssertingMutex.lock_state,
505         FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
506   });
507
508   // should be unlocked here
509   EXPECT_EQ(
510       globalAllPowerfulAssertingMutex.lock_state,
511       FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
512 }