2 * Copyright 2016 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 // @author: Andrei Alexandrescu (aalexandre)
19 // Test bed for folly/Synchronized.h
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>
30 using namespace folly::sync_tests;
32 template <class Mutex>
33 class SynchronizedTest : public testing::Test {};
35 using SynchronizedTestTypes = testing::Types<
36 folly::SharedMutexReadPriority,
37 folly::SharedMutexWritePriority,
40 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
42 std::recursive_timed_mutex,
45 boost::recursive_mutex,
46 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
48 boost::recursive_timed_mutex,
50 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
51 folly::RWTicketSpinLock32,
52 folly::RWTicketSpinLock64,
56 TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes);
58 TYPED_TEST(SynchronizedTest, Basic) {
59 testBasic<TypeParam>();
62 TYPED_TEST(SynchronizedTest, WithLock) {
63 testWithLock<TypeParam>();
66 TYPED_TEST(SynchronizedTest, Unlock) {
67 testUnlock<TypeParam>();
70 TYPED_TEST(SynchronizedTest, Deprecated) {
71 testDeprecated<TypeParam>();
74 TYPED_TEST(SynchronizedTest, Concurrency) {
75 testConcurrency<TypeParam>();
78 TYPED_TEST(SynchronizedTest, AcquireLocked) {
79 testAcquireLocked<TypeParam>();
82 TYPED_TEST(SynchronizedTest, AcquireLockedWithConst) {
83 testAcquireLockedWithConst<TypeParam>();
86 TYPED_TEST(SynchronizedTest, DualLocking) {
87 testDualLocking<TypeParam>();
90 TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
91 testDualLockingWithConst<TypeParam>();
94 TYPED_TEST(SynchronizedTest, ConstCopy) {
95 testConstCopy<TypeParam>();
98 template <class Mutex>
99 class SynchronizedTimedTest : public testing::Test {};
101 using SynchronizedTimedTestTypes = testing::Types<
102 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
104 std::recursive_timed_mutex,
106 boost::recursive_timed_mutex,
109 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
110 folly::RWTicketSpinLock32,
111 folly::RWTicketSpinLock64,
113 folly::SharedMutexReadPriority,
114 folly::SharedMutexWritePriority>;
115 TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
117 TYPED_TEST(SynchronizedTimedTest, Timed) {
118 testTimed<TypeParam>();
121 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
122 testTimedSynchronized<TypeParam>();
125 template <class Mutex>
126 class SynchronizedTimedWithConstTest : public testing::Test {};
128 using SynchronizedTimedWithConstTestTypes = testing::Types<
129 #if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
132 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
133 folly::RWTicketSpinLock32,
134 folly::RWTicketSpinLock64,
136 folly::SharedMutexReadPriority,
137 folly::SharedMutexWritePriority>;
139 SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes);
141 TYPED_TEST(SynchronizedTimedWithConstTest, TimedShared) {
142 testTimedShared<TypeParam>();
145 TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
146 testTimedSynchronizedWithConst<TypeParam>();
149 TYPED_TEST(SynchronizedTest, InPlaceConstruction) {
150 testInPlaceConstruction<TypeParam>();
153 using CountPair = std::pair<int, int>;
154 // This class is specialized only to be uesed in SynchronizedLockTest
167 static CountPair getLockUnlockCount() {
168 return CountPair{lockCount_, unlockCount_};
171 static void resetLockUnlockCount() {
176 // Keep these two static for test access
177 // Keep them thread_local in case of tests are run in parallel within one
179 static FOLLY_TLS int lockCount_;
180 static FOLLY_TLS int unlockCount_;
182 FOLLY_TLS int FakeMutex::lockCount_{0};
183 FOLLY_TLS int FakeMutex::unlockCount_{0};
185 // SynchronizedLockTest is used to verify the correct lock unlock behavior
186 // happens per design
187 class SynchronizedLockTest : public testing::Test {
189 void SetUp() override {
190 FakeMutex::resetLockUnlockCount();
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
197 TEST_F(SynchronizedLockTest, SyncUnSync) {
198 folly::Synchronized<std::vector<int>, FakeMutex> obj;
199 EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
201 EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
202 UNSYNCHRONIZED(obj) {
203 EXPECT_EQ((CountPair{1, 1}), FakeMutex::getLockUnlockCount());
205 EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
207 EXPECT_EQ((CountPair{2, 2}), FakeMutex::getLockUnlockCount());
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());
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());
225 EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
226 UNSYNCHRONIZED(obj) {
227 EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
229 EXPECT_EQ((CountPair{4, 2}), FakeMutex::getLockUnlockCount());
231 EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
233 EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
236 // Different nesting behavior, UNSYNCHRONIZED called on different depth of
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());
244 EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
245 UNSYNCHRONIZED(obj) {
246 EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
248 EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
250 EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
251 UNSYNCHRONIZED(obj) {
252 EXPECT_EQ((CountPair{3, 3}), FakeMutex::getLockUnlockCount());
254 EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
256 EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());