add an unlock() method to Synchronized<T>::LockedPtr
[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, WithLock) {
63   testWithLock<TypeParam>();
64 }
65
66 TYPED_TEST(SynchronizedTest, Unlock) {
67   testUnlock<TypeParam>();
68 }
69
70 TYPED_TEST(SynchronizedTest, Deprecated) {
71   testDeprecated<TypeParam>();
72 }
73
74 TYPED_TEST(SynchronizedTest, Concurrency) {
75   testConcurrency<TypeParam>();
76 }
77
78 TYPED_TEST(SynchronizedTest, AcquireLocked) {
79   testAcquireLocked<TypeParam>();
80 }
81
82 TYPED_TEST(SynchronizedTest, AcquireLockedWithConst) {
83   testAcquireLockedWithConst<TypeParam>();
84 }
85
86 TYPED_TEST(SynchronizedTest, DualLocking) {
87   testDualLocking<TypeParam>();
88 }
89
90 TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
91   testDualLockingWithConst<TypeParam>();
92 }
93
94 TYPED_TEST(SynchronizedTest, ConstCopy) {
95   testConstCopy<TypeParam>();
96 }
97
98 template <class Mutex>
99 class SynchronizedTimedTest : public testing::Test {};
100
101 using SynchronizedTimedTestTypes = testing::Types<
102 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
103     std::timed_mutex,
104     std::recursive_timed_mutex,
105     boost::timed_mutex,
106     boost::recursive_timed_mutex,
107     boost::shared_mutex,
108 #endif
109 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
110     folly::RWTicketSpinLock32,
111     folly::RWTicketSpinLock64,
112 #endif
113     folly::SharedMutexReadPriority,
114     folly::SharedMutexWritePriority>;
115 TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
116
117 TYPED_TEST(SynchronizedTimedTest, Timed) {
118   testTimed<TypeParam>();
119 }
120
121 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
122   testTimedSynchronized<TypeParam>();
123 }
124
125 template <class Mutex>
126 class SynchronizedTimedWithConstTest : public testing::Test {};
127
128 using SynchronizedTimedWithConstTestTypes = testing::Types<
129 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
130     boost::shared_mutex,
131 #endif
132 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
133     folly::RWTicketSpinLock32,
134     folly::RWTicketSpinLock64,
135 #endif
136     folly::SharedMutexReadPriority,
137     folly::SharedMutexWritePriority>;
138 TYPED_TEST_CASE(
139     SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes);
140
141 TYPED_TEST(SynchronizedTimedWithConstTest, TimedShared) {
142   testTimedShared<TypeParam>();
143 }
144
145 TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
146   testTimedSynchronizedWithConst<TypeParam>();
147 }
148
149 TYPED_TEST(SynchronizedTest, InPlaceConstruction) {
150   testInPlaceConstruction<TypeParam>();
151 }
152
153 using CountPair = std::pair<int, int>;
154 // This class is specialized only to be uesed in SynchronizedLockTest
155 class FakeMutex {
156  public:
157   bool lock() {
158     ++lockCount_;
159     return true;
160   }
161
162   bool unlock() {
163     ++unlockCount_;
164     return true;
165   }
166
167   static CountPair getLockUnlockCount() {
168     return CountPair{lockCount_, unlockCount_};
169   }
170
171   static void resetLockUnlockCount() {
172     lockCount_ = 0;
173     unlockCount_ = 0;
174   }
175  private:
176   // Keep these two static for test access
177   // Keep them thread_local in case of tests are run in parallel within one
178   // process
179   static FOLLY_TLS int lockCount_;
180   static FOLLY_TLS int unlockCount_;
181 };
182 FOLLY_TLS int FakeMutex::lockCount_{0};
183 FOLLY_TLS int FakeMutex::unlockCount_{0};
184
185 // SynchronizedLockTest is used to verify the correct lock unlock behavior
186 // happens per design
187 class SynchronizedLockTest : public testing::Test {
188  public:
189   void SetUp() override {
190     FakeMutex::resetLockUnlockCount();
191   }
192 };
193
194 // Single level of SYNCHRONIZED and UNSYNCHRONIZED, although nested test are
195 // super set of it, it is possible single level test passes while nested tests
196 // fail
197 TEST_F(SynchronizedLockTest, SyncUnSync) {
198   folly::Synchronized<std::vector<int>, FakeMutex> obj;
199   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
200   SYNCHRONIZED(obj) {
201     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
202     UNSYNCHRONIZED(obj) {
203       EXPECT_EQ((CountPair{1, 1}), FakeMutex::getLockUnlockCount());
204     }
205     EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
206   }
207   EXPECT_EQ((CountPair{2, 2}), FakeMutex::getLockUnlockCount());
208 }
209
210 // Nested SYNCHRONIZED UNSYNCHRONIZED test, 2 levels of synchronization
211 TEST_F(SynchronizedLockTest, NestedSyncUnSync) {
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       // Note: UNSYNCHRONIZED has always been kind of broken here.
219       // The input parameter is ignored (other than to overwrite what the input
220       // variable name refers to), and it unlocks the most object acquired in
221       // the most recent SYNCHRONIZED scope.
222       UNSYNCHRONIZED(obj) {
223         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
224       }
225       EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
226       UNSYNCHRONIZED(obj) {
227         EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
228       }
229       EXPECT_EQ((CountPair{4, 2}), FakeMutex::getLockUnlockCount());
230     }
231     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
232   }
233   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
234 }
235
236 // Different nesting behavior, UNSYNCHRONIZED called on different depth of
237 // SYNCHRONIZED
238 TEST_F(SynchronizedLockTest, NestedSyncUnSync2) {
239   folly::Synchronized<std::vector<int>, FakeMutex> obj;
240   EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
241   SYNCHRONIZED(objCopy, obj) {
242     EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
243     SYNCHRONIZED(obj) {
244       EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
245       UNSYNCHRONIZED(obj) {
246         EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
247       }
248       EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
249     }
250     EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
251     UNSYNCHRONIZED(obj) {
252       EXPECT_EQ((CountPair{3, 3}), FakeMutex::getLockUnlockCount());
253     }
254     EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
255   }
256   EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
257 }