Fix test/SynchronizedTest.cpp build break on Clang on OS X
[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/Portability.h>
22 #include <folly/Synchronized.h>
23 #include <folly/RWSpinLock.h>
24 #include <folly/SharedMutex.h>
25 #include <folly/test/SynchronizedTestLib.h>
26 #include <gtest/gtest.h>
27
28 namespace {
29
30 template <class Mutex>
31 class SynchronizedTest : public testing::Test {};
32
33 using SynchronizedTestTypes = testing::Types
34   < folly::SharedMutexReadPriority
35   , folly::SharedMutexWritePriority
36   , std::mutex
37   , std::recursive_mutex
38 #if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
39   , std::timed_mutex
40   , std::recursive_timed_mutex
41 #endif
42   , boost::mutex
43   , boost::recursive_mutex
44 #if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
45   , boost::timed_mutex
46   , boost::recursive_timed_mutex
47 #endif
48   , boost::shared_mutex
49 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
50   , folly::RWTicketSpinLock32
51   , folly::RWTicketSpinLock64
52 #endif
53   >;
54 TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes);
55
56 TYPED_TEST(SynchronizedTest, Basic) {
57   testBasic<TypeParam>();
58 }
59
60 TYPED_TEST(SynchronizedTest, Concurrency) {
61   testConcurrency<TypeParam>();
62 }
63
64 TYPED_TEST(SynchronizedTest, DualLocking) {
65   testDualLocking<TypeParam>();
66 }
67
68 TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
69   testDualLockingWithConst<TypeParam>();
70 }
71
72 TYPED_TEST(SynchronizedTest, ConstCopy) {
73   testConstCopy<TypeParam>();
74 }
75
76 template <class Mutex>
77 class SynchronizedTimedTest : public testing::Test {};
78
79 using SynchronizedTimedTestTypes = testing::Types
80   < folly::SharedMutexReadPriority
81   , folly::SharedMutexWritePriority
82 #if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
83   , std::timed_mutex
84   , std::recursive_timed_mutex
85   , boost::timed_mutex
86   , boost::recursive_timed_mutex
87   , boost::shared_mutex
88 #endif
89 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
90   , folly::RWTicketSpinLock32
91   , folly::RWTicketSpinLock64
92 #endif
93   >;
94 TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
95
96 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
97   testTimedSynchronized<TypeParam>();
98 }
99
100 template <class Mutex>
101 class SynchronizedTimedWithConstTest : public testing::Test {};
102
103 using SynchronizedTimedWithConstTestTypes = testing::Types
104   < folly::SharedMutexReadPriority
105   , folly::SharedMutexWritePriority
106 #if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
107   , boost::shared_mutex
108 #endif
109 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
110   , folly::RWTicketSpinLock32
111   , folly::RWTicketSpinLock64
112 #endif
113   >;
114 TYPED_TEST_CASE(
115     SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes);
116
117 TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
118   testTimedSynchronizedWithConst<TypeParam>();
119 }
120
121 TYPED_TEST(SynchronizedTest, InPlaceConstruction) {
122   testInPlaceConstruction<TypeParam>();
123 }
124
125 using CountPair = std::pair<int, int>;
126 // This class is specialized only to be uesed in SynchronizedLockTest
127 class FakeMutex {
128  public:
129   bool lock() {
130     ++lockCount_;
131     return true;
132   }
133
134   bool unlock() {
135     ++unlockCount_;
136     return true;
137   }
138
139   static CountPair getLockUnlockCount() {
140     return CountPair{lockCount_, unlockCount_};
141   }
142
143   static void resetLockUnlockCount() {
144     lockCount_ = 0;
145     unlockCount_ = 0;
146   }
147  private:
148   // Keep these two static for test access
149   // Keep them thread_local in case of tests are run in parallel within one
150   // process
151   static FOLLY_TLS int lockCount_;
152   static FOLLY_TLS int unlockCount_;
153
154   // Adapters for Synchronized<>
155   friend void acquireReadWrite(FakeMutex& lock) { lock.lock(); }
156   friend void releaseReadWrite(FakeMutex& lock) { lock.unlock(); }
157 };
158 FOLLY_TLS int FakeMutex::lockCount_{0};
159 FOLLY_TLS int FakeMutex::unlockCount_{0};
160
161 // SynchronizedLockTest is used to verify the correct lock unlock behavior
162 // happens per design
163 class SynchronizedLockTest : public testing::Test {
164  public:
165   void SetUp() override {
166     FakeMutex::resetLockUnlockCount();
167   }
168 };
169
170 // Single level of SYNCHRONIZED and UNSYNCHRONIZED, although nested test are
171 // super set of it, it is possible single level test passes while nested tests
172 // fail
173 TEST_F(SynchronizedLockTest, SyncUnSync) {
174   folly::Synchronized<std::vector<int>, FakeMutex> obj;
175   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
176   SYNCHRONIZED(obj) {
177     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
178     UNSYNCHRONIZED(obj) {
179       EXPECT_EQ((CountPair{1, 1}), FakeMutex::getLockUnlockCount());
180     }
181     EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
182   }
183   EXPECT_EQ((CountPair{2, 2}), FakeMutex::getLockUnlockCount());
184 }
185
186 // Nested SYNCHRONIZED UNSYNCHRONIZED test, 2 levels for each are used here
187 TEST_F(SynchronizedLockTest, NestedSyncUnSync) {
188   folly::Synchronized<std::vector<int>, FakeMutex> obj;
189   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
190   SYNCHRONIZED(objCopy, obj) {
191     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
192     SYNCHRONIZED(obj) {
193       EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
194       UNSYNCHRONIZED(obj) {
195         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
196         UNSYNCHRONIZED(obj) {
197           EXPECT_EQ((CountPair{2, 2}),
198                     FakeMutex::getLockUnlockCount());
199         }
200         EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
201       }
202       EXPECT_EQ((CountPair{4, 2}), FakeMutex::getLockUnlockCount());
203     }
204     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
205   }
206   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
207 }
208
209 // Different nesting behavior, UNSYNCHRONIZED called on differen depth of
210 // SYNCHRONIZED
211 TEST_F(SynchronizedLockTest, NestedSyncUnSync2) {
212   folly::Synchronized<std::vector<int>, FakeMutex> obj;
213   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
214   SYNCHRONIZED(objCopy, obj) {
215     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
216     SYNCHRONIZED(obj) {
217       EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
218       UNSYNCHRONIZED(obj) {
219         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
220       }
221       EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
222     }
223     EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
224     UNSYNCHRONIZED(obj) {
225       EXPECT_EQ((CountPair{3, 3}), FakeMutex::getLockUnlockCount());
226     }
227     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
228   }
229   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
230 }
231 }