update Synchronized to use LockTraits
[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_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, Concurrency) {
62   testConcurrency<TypeParam>();
63 }
64
65 TYPED_TEST(SynchronizedTest, DualLocking) {
66   testDualLocking<TypeParam>();
67 }
68
69 TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
70   testDualLockingWithConst<TypeParam>();
71 }
72
73 TYPED_TEST(SynchronizedTest, ConstCopy) {
74   testConstCopy<TypeParam>();
75 }
76
77 template <class Mutex>
78 class SynchronizedTimedTest : public testing::Test {};
79
80 using SynchronizedTimedTestTypes = testing::Types<
81 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
82     std::timed_mutex,
83     std::recursive_timed_mutex,
84     boost::timed_mutex,
85     boost::recursive_timed_mutex,
86     boost::shared_mutex,
87 #endif
88 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
89     folly::RWTicketSpinLock32,
90     folly::RWTicketSpinLock64,
91 #endif
92     folly::SharedMutexReadPriority,
93     folly::SharedMutexWritePriority>;
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 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
105     boost::shared_mutex,
106 #endif
107 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
108     folly::RWTicketSpinLock32,
109     folly::RWTicketSpinLock64,
110 #endif
111     folly::SharedMutexReadPriority,
112     folly::SharedMutexWritePriority>;
113 TYPED_TEST_CASE(
114     SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes);
115
116 TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
117   testTimedSynchronizedWithConst<TypeParam>();
118 }
119
120 TYPED_TEST(SynchronizedTest, InPlaceConstruction) {
121   testInPlaceConstruction<TypeParam>();
122 }
123
124 using CountPair = std::pair<int, int>;
125 // This class is specialized only to be uesed in SynchronizedLockTest
126 class FakeMutex {
127  public:
128   bool lock() {
129     ++lockCount_;
130     return true;
131   }
132
133   bool unlock() {
134     ++unlockCount_;
135     return true;
136   }
137
138   static CountPair getLockUnlockCount() {
139     return CountPair{lockCount_, unlockCount_};
140   }
141
142   static void resetLockUnlockCount() {
143     lockCount_ = 0;
144     unlockCount_ = 0;
145   }
146  private:
147   // Keep these two static for test access
148   // Keep them thread_local in case of tests are run in parallel within one
149   // process
150   static FOLLY_TLS int lockCount_;
151   static FOLLY_TLS int unlockCount_;
152 };
153 FOLLY_TLS int FakeMutex::lockCount_{0};
154 FOLLY_TLS int FakeMutex::unlockCount_{0};
155
156 // SynchronizedLockTest is used to verify the correct lock unlock behavior
157 // happens per design
158 class SynchronizedLockTest : public testing::Test {
159  public:
160   void SetUp() override {
161     FakeMutex::resetLockUnlockCount();
162   }
163 };
164
165 // Single level of SYNCHRONIZED and UNSYNCHRONIZED, although nested test are
166 // super set of it, it is possible single level test passes while nested tests
167 // fail
168 TEST_F(SynchronizedLockTest, SyncUnSync) {
169   folly::Synchronized<std::vector<int>, FakeMutex> obj;
170   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
171   SYNCHRONIZED(obj) {
172     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
173     UNSYNCHRONIZED(obj) {
174       EXPECT_EQ((CountPair{1, 1}), FakeMutex::getLockUnlockCount());
175     }
176     EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
177   }
178   EXPECT_EQ((CountPair{2, 2}), FakeMutex::getLockUnlockCount());
179 }
180
181 // Nested SYNCHRONIZED UNSYNCHRONIZED test, 2 levels for each are used here
182 TEST_F(SynchronizedLockTest, NestedSyncUnSync) {
183   folly::Synchronized<std::vector<int>, FakeMutex> obj;
184   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
185   SYNCHRONIZED(objCopy, obj) {
186     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
187     SYNCHRONIZED(obj) {
188       EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
189       UNSYNCHRONIZED(obj) {
190         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
191         UNSYNCHRONIZED(obj) {
192           EXPECT_EQ((CountPair{2, 2}),
193                     FakeMutex::getLockUnlockCount());
194         }
195         EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
196       }
197       EXPECT_EQ((CountPair{4, 2}), FakeMutex::getLockUnlockCount());
198     }
199     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
200   }
201   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
202 }
203
204 // Different nesting behavior, UNSYNCHRONIZED called on differen depth of
205 // SYNCHRONIZED
206 TEST_F(SynchronizedLockTest, NestedSyncUnSync2) {
207   folly::Synchronized<std::vector<int>, FakeMutex> obj;
208   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
209   SYNCHRONIZED(objCopy, obj) {
210     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
211     SYNCHRONIZED(obj) {
212       EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
213       UNSYNCHRONIZED(obj) {
214         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
215       }
216       EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
217     }
218     EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
219     UNSYNCHRONIZED(obj) {
220       EXPECT_EQ((CountPair{3, 3}), FakeMutex::getLockUnlockCount());
221     }
222     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
223   }
224   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
225 }
226 }