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