2 * Copyright 2014 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.
18 // @author xliu (xliux@fb.com)
26 #include <gtest/gtest.h>
27 #include <gflags/gflags.h>
28 #include <glog/logging.h>
29 #include <folly/RWSpinLock.h>
31 DEFINE_int32(num_threads, 8, "num threads");
35 static const int kMaxReaders = 50;
36 static std::atomic<bool> stopThread;
37 using namespace folly;
39 template<typename RWSpinLockT> struct RWSpinLockTest: public testing::Test {
40 typedef RWSpinLockT RWSpinLockType;
43 typedef testing::Types<RWSpinLock
44 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
45 , RWTicketSpinLockT<32, true>,
46 RWTicketSpinLockT<32, false>,
47 RWTicketSpinLockT<64, true>,
48 RWTicketSpinLockT<64, false>
52 TYPED_TEST_CASE(RWSpinLockTest, Implementations);
54 template<typename RWSpinLockType>
55 static void run(RWSpinLockType* lock) {
58 while (!stopThread.load(std::memory_order_acquire)) {
59 if (rand() % 10 == 0) { // write
60 typename RWSpinLockType::WriteHolder guard(lock);
63 typename RWSpinLockType::ReadHolder guard(lock);
67 // VLOG(0) << "total reads: " << reads << "; total writes: " << writes;
71 TYPED_TEST(RWSpinLockTest, Writer_Wait_Readers) {
72 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
75 for (int i = 0; i < kMaxReaders; ++i) {
76 EXPECT_TRUE(l.try_lock_shared());
77 EXPECT_FALSE(l.try_lock());
80 for (int i = 0; i < kMaxReaders; ++i) {
81 EXPECT_FALSE(l.try_lock());
85 EXPECT_TRUE(l.try_lock());
88 TYPED_TEST(RWSpinLockTest, Readers_Wait_Writer) {
89 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
92 EXPECT_TRUE(l.try_lock());
94 for (int i = 0; i < kMaxReaders; ++i) {
95 EXPECT_FALSE(l.try_lock_shared());
98 l.unlock_and_lock_shared();
99 for (int i = 0; i < kMaxReaders - 1; ++i) {
100 EXPECT_TRUE(l.try_lock_shared());
104 TYPED_TEST(RWSpinLockTest, Writer_Wait_Writer) {
105 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
108 EXPECT_TRUE(l.try_lock());
109 EXPECT_FALSE(l.try_lock());
112 EXPECT_TRUE(l.try_lock());
113 EXPECT_FALSE(l.try_lock());
116 TYPED_TEST(RWSpinLockTest, Read_Holders) {
117 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
121 typename RWSpinLockType::ReadHolder guard(&l);
122 EXPECT_FALSE(l.try_lock());
123 EXPECT_TRUE(l.try_lock_shared());
126 EXPECT_FALSE(l.try_lock());
129 EXPECT_TRUE(l.try_lock());
133 TYPED_TEST(RWSpinLockTest, Write_Holders) {
134 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
137 typename RWSpinLockType::WriteHolder guard(&l);
138 EXPECT_FALSE(l.try_lock());
139 EXPECT_FALSE(l.try_lock_shared());
142 EXPECT_TRUE(l.try_lock_shared());
143 EXPECT_FALSE(l.try_lock());
145 EXPECT_TRUE(l.try_lock());
148 TYPED_TEST(RWSpinLockTest, ConcurrentTests) {
149 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
151 srand(time(nullptr));
153 std::vector<std::thread> threads;
154 for (int i = 0; i < FLAGS_num_threads; ++i) {
155 threads.push_back(std::thread(&run<RWSpinLockType>, &l));
159 stopThread.store(true, std::memory_order_release);
161 for (auto& t : threads) {
166 // RWSpinLock specific tests
168 TEST(RWSpinLock, lock_unlock_tests) {
169 folly::RWSpinLock lock;
170 EXPECT_TRUE(lock.try_lock_upgrade());
171 EXPECT_FALSE(lock.try_lock_shared());
172 EXPECT_FALSE(lock.try_lock());
173 EXPECT_FALSE(lock.try_lock_upgrade());
174 lock.unlock_upgrade();
176 EXPECT_FALSE(lock.try_lock());
177 EXPECT_TRUE(lock.try_lock_upgrade());
178 lock.unlock_upgrade();
179 lock.unlock_shared();
180 EXPECT_TRUE(lock.try_lock());
181 EXPECT_FALSE(lock.try_lock_upgrade());
182 lock.unlock_and_lock_upgrade();
183 EXPECT_FALSE(lock.try_lock_shared());
184 lock.unlock_upgrade_and_lock_shared();
185 lock.unlock_shared();
186 EXPECT_EQ(0, lock.bits());
189 TEST(RWSpinLock, concurrent_holder_test) {
190 srand(time(nullptr));
192 folly::RWSpinLock lock;
193 std::atomic<int64_t> reads(0);
194 std::atomic<int64_t> writes(0);
195 std::atomic<int64_t> upgrades(0);
196 std::atomic<bool> stop(false);
199 while (!stop.load(std::memory_order_acquire)) {
200 auto r = (uint32_t)(rand()) % 10;
201 if (r < 3) { // starts from write lock
202 RWSpinLock::ReadHolder rg{
203 RWSpinLock::UpgradedHolder{
204 RWSpinLock::WriteHolder{&lock}}};
205 writes.fetch_add(1, std::memory_order_acq_rel);;
206 } else if (r < 6) { // starts from upgrade lock
207 RWSpinLock::UpgradedHolder ug(&lock);
209 RWSpinLock::WriteHolder wg(std::move(ug));
211 RWSpinLock::ReadHolder rg(std::move(ug));
213 upgrades.fetch_add(1, std::memory_order_acq_rel);;
215 RWSpinLock::ReadHolder rg{&lock};
216 reads.fetch_add(1, std::memory_order_acq_rel);
221 std::vector<std::thread> threads;
222 for (int i = 0; i < FLAGS_num_threads; ++i) {
223 threads.push_back(std::thread(go));
227 stop.store(true, std::memory_order_release);
229 for (auto& t : threads) t.join();
231 LOG(INFO) << "reads: " << reads.load(std::memory_order_acquire)
232 << "; writes: " << writes.load(std::memory_order_acquire)
233 << "; upgrades: " << upgrades.load(std::memory_order_acquire);
238 int main(int argc, char** argv) {
239 testing::InitGoogleTest(&argc, argv);
240 gflags::ParseCommandLineFlags(&argc, &argv, true);
241 return RUN_ALL_TESTS();