d89b9f448e1fc7a06b24f09083042e84113f64f2
[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 <gtest/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, Concurrency) {
63   testConcurrency<TypeParam>();
64 }
65
66 TYPED_TEST(SynchronizedTest, DualLocking) {
67   testDualLocking<TypeParam>();
68 }
69
70 TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
71   testDualLockingWithConst<TypeParam>();
72 }
73
74 TYPED_TEST(SynchronizedTest, ConstCopy) {
75   testConstCopy<TypeParam>();
76 }
77
78 template <class Mutex>
79 class SynchronizedTimedTest : public testing::Test {};
80
81 using SynchronizedTimedTestTypes = testing::Types<
82 #if FOLLY_LOCK_TRAITS_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     folly::SharedMutexReadPriority,
94     folly::SharedMutexWritePriority>;
95 TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
96
97 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
98   testTimedSynchronized<TypeParam>();
99 }
100
101 template <class Mutex>
102 class SynchronizedTimedWithConstTest : public testing::Test {};
103
104 using SynchronizedTimedWithConstTestTypes = testing::Types<
105 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
106     boost::shared_mutex,
107 #endif
108 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
109     folly::RWTicketSpinLock32,
110     folly::RWTicketSpinLock64,
111 #endif
112     folly::SharedMutexReadPriority,
113     folly::SharedMutexWritePriority>;
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 FOLLY_TLS int FakeMutex::lockCount_{0};
155 FOLLY_TLS int FakeMutex::unlockCount_{0};
156
157 // SynchronizedLockTest is used to verify the correct lock unlock behavior
158 // happens per design
159 class SynchronizedLockTest : public testing::Test {
160  public:
161   void SetUp() override {
162     FakeMutex::resetLockUnlockCount();
163   }
164 };
165
166 // Single level of SYNCHRONIZED and UNSYNCHRONIZED, although nested test are
167 // super set of it, it is possible single level test passes while nested tests
168 // fail
169 TEST_F(SynchronizedLockTest, SyncUnSync) {
170   folly::Synchronized<std::vector<int>, FakeMutex> obj;
171   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
172   SYNCHRONIZED(obj) {
173     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
174     UNSYNCHRONIZED(obj) {
175       EXPECT_EQ((CountPair{1, 1}), FakeMutex::getLockUnlockCount());
176     }
177     EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
178   }
179   EXPECT_EQ((CountPair{2, 2}), FakeMutex::getLockUnlockCount());
180 }
181
182 // Nested SYNCHRONIZED UNSYNCHRONIZED test, 2 levels for each are used here
183 TEST_F(SynchronizedLockTest, NestedSyncUnSync) {
184   folly::Synchronized<std::vector<int>, FakeMutex> obj;
185   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
186   SYNCHRONIZED(objCopy, obj) {
187     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
188     SYNCHRONIZED(obj) {
189       EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
190       UNSYNCHRONIZED(obj) {
191         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
192         UNSYNCHRONIZED(obj) {
193           EXPECT_EQ((CountPair{2, 2}),
194                     FakeMutex::getLockUnlockCount());
195         }
196         EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
197       }
198       EXPECT_EQ((CountPair{4, 2}), FakeMutex::getLockUnlockCount());
199     }
200     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
201   }
202   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
203 }
204
205 // Different nesting behavior, UNSYNCHRONIZED called on differen depth of
206 // SYNCHRONIZED
207 TEST_F(SynchronizedLockTest, NestedSyncUnSync2) {
208   folly::Synchronized<std::vector<int>, FakeMutex> obj;
209   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
210   SYNCHRONIZED(objCopy, obj) {
211     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
212     SYNCHRONIZED(obj) {
213       EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
214       UNSYNCHRONIZED(obj) {
215         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
216       }
217       EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
218     }
219     EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
220     UNSYNCHRONIZED(obj) {
221       EXPECT_EQ((CountPair{3, 3}), FakeMutex::getLockUnlockCount());
222     }
223     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
224   }
225   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
226 }