folly copyright 2015 -> copyright 2016
[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/Synchronized.h>
22 #include <folly/RWSpinLock.h>
23 #include <folly/SharedMutex.h>
24 #include <folly/test/SynchronizedTestLib.h>
25 #include <gtest/gtest.h>
26
27 namespace {
28
29 template <class Mutex>
30 class SynchronizedTest : public testing::Test {};
31
32 using SynchronizedTestTypes = testing::Types
33   < folly::SharedMutexReadPriority
34   , folly::SharedMutexWritePriority
35   , std::mutex
36   , std::recursive_mutex
37 #ifdef FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
38   , std::timed_mutex
39   , std::recursive_timed_mutex
40 #endif
41   , boost::mutex
42   , boost::recursive_mutex
43 #ifdef FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
44   , boost::timed_mutex
45   , boost::recursive_timed_mutex
46 #endif
47   , boost::shared_mutex
48 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
49   , folly::RWTicketSpinLock32
50   , folly::RWTicketSpinLock64
51 #endif
52   >;
53 TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes);
54
55 TYPED_TEST(SynchronizedTest, Basic) {
56   testBasic<TypeParam>();
57 }
58
59 TYPED_TEST(SynchronizedTest, Concurrency) {
60   testConcurrency<TypeParam>();
61 }
62
63 TYPED_TEST(SynchronizedTest, DualLocking) {
64   testDualLocking<TypeParam>();
65 }
66
67 TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
68   testDualLockingWithConst<TypeParam>();
69 }
70
71 TYPED_TEST(SynchronizedTest, ConstCopy) {
72   testConstCopy<TypeParam>();
73 }
74
75 template <class Mutex>
76 class SynchronizedTimedTest : public testing::Test {};
77
78 using SynchronizedTimedTestTypes = testing::Types
79   < folly::SharedMutexReadPriority
80   , folly::SharedMutexWritePriority
81 #ifdef FOLLY_SYNCHRONIZED_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   >;
93 TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
94
95 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
96   testTimedSynchronized<TypeParam>();
97 }
98
99 template <class Mutex>
100 class SynchronizedTimedWithConstTest : public testing::Test {};
101
102 using SynchronizedTimedWithConstTestTypes = testing::Types
103   < folly::SharedMutexReadPriority
104   , folly::SharedMutexWritePriority
105 #ifdef FOLLY_SYNCHRONIZED_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   >;
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 thread_local int lockCount_;
151   static thread_local int unlockCount_;
152
153   // Adapters for Synchronized<>
154   friend void acquireReadWrite(FakeMutex& lock) { lock.lock(); }
155   friend void releaseReadWrite(FakeMutex& lock) { lock.unlock(); }
156 };
157 thread_local int FakeMutex::lockCount_{0};
158 thread_local int FakeMutex::unlockCount_{0};
159
160 // SynchronizedLockTest is used to verify the correct lock unlock behavior
161 // happens per design
162 class SynchronizedLockTest : public testing::Test {
163  public:
164   void SetUp() override {
165     FakeMutex::resetLockUnlockCount();
166   }
167 };
168
169 // Single level of SYNCHRONIZED and UNSYNCHRONIZED, although nested test are
170 // super set of it, it is possible single level test passes while nested tests
171 // fail
172 TEST_F(SynchronizedLockTest, SyncUnSync) {
173   folly::Synchronized<std::vector<int>, FakeMutex> obj;
174   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
175   SYNCHRONIZED(obj) {
176     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
177     UNSYNCHRONIZED(obj) {
178       EXPECT_EQ((CountPair{1, 1}), FakeMutex::getLockUnlockCount());
179     }
180     EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
181   }
182   EXPECT_EQ((CountPair{2, 2}), FakeMutex::getLockUnlockCount());
183 }
184
185 // Nested SYNCHRONIZED UNSYNCHRONIZED test, 2 levels for each are used here
186 TEST_F(SynchronizedLockTest, NestedSyncUnSync) {
187   folly::Synchronized<std::vector<int>, FakeMutex> obj;
188   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
189   SYNCHRONIZED(objCopy, obj) {
190     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
191     SYNCHRONIZED(obj) {
192       EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
193       UNSYNCHRONIZED(obj) {
194         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
195         UNSYNCHRONIZED(obj) {
196           EXPECT_EQ((CountPair{2, 2}),
197                     FakeMutex::getLockUnlockCount());
198         }
199         EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
200       }
201       EXPECT_EQ((CountPair{4, 2}), FakeMutex::getLockUnlockCount());
202     }
203     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
204   }
205   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
206 }
207
208 // Different nesting behavior, UNSYNCHRONIZED called on differen depth of
209 // SYNCHRONIZED
210 TEST_F(SynchronizedLockTest, NestedSyncUnSync2) {
211   folly::Synchronized<std::vector<int>, FakeMutex> obj;
212   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
213   SYNCHRONIZED(objCopy, obj) {
214     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
215     SYNCHRONIZED(obj) {
216       EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
217       UNSYNCHRONIZED(obj) {
218         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
219       }
220       EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
221     }
222     EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
223     UNSYNCHRONIZED(obj) {
224       EXPECT_EQ((CountPair{3, 3}), FakeMutex::getLockUnlockCount());
225     }
226     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
227   }
228   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
229 }
230 }