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.
18 // @author xliu (xliux@fb.com)
21 #include <folly/RWSpinLock.h>
27 #include <glog/logging.h>
29 #include <folly/portability/GFlags.h>
30 #include <folly/portability/GTest.h>
31 #include <folly/portability/Unistd.h>
33 DEFINE_int32(num_threads, 8, "num threads");
37 static const int kMaxReaders = 50;
38 static std::atomic<bool> stopThread;
39 using namespace folly;
41 template<typename RWSpinLockT> struct RWSpinLockTest: public testing::Test {
42 typedef RWSpinLockT RWSpinLockType;
45 typedef testing::Types<RWSpinLock
46 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
47 , RWTicketSpinLockT<32, true>,
48 RWTicketSpinLockT<32, false>,
49 RWTicketSpinLockT<64, true>,
50 RWTicketSpinLockT<64, false>
54 TYPED_TEST_CASE(RWSpinLockTest, Implementations);
56 template<typename RWSpinLockType>
57 static void run(RWSpinLockType* lock) {
60 while (!stopThread.load(std::memory_order_acquire)) {
61 if (rand() % 10 == 0) { // write
62 typename RWSpinLockType::WriteHolder guard(lock);
65 typename RWSpinLockType::ReadHolder guard(lock);
69 // VLOG(0) << "total reads: " << reads << "; total writes: " << writes;
73 TYPED_TEST(RWSpinLockTest, Writer_Wait_Readers) {
74 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
77 for (int i = 0; i < kMaxReaders; ++i) {
78 EXPECT_TRUE(l.try_lock_shared());
79 EXPECT_FALSE(l.try_lock());
82 for (int i = 0; i < kMaxReaders; ++i) {
83 EXPECT_FALSE(l.try_lock());
87 EXPECT_TRUE(l.try_lock());
90 TYPED_TEST(RWSpinLockTest, Readers_Wait_Writer) {
91 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
94 EXPECT_TRUE(l.try_lock());
96 for (int i = 0; i < kMaxReaders; ++i) {
97 EXPECT_FALSE(l.try_lock_shared());
100 l.unlock_and_lock_shared();
101 for (int i = 0; i < kMaxReaders - 1; ++i) {
102 EXPECT_TRUE(l.try_lock_shared());
106 TYPED_TEST(RWSpinLockTest, Writer_Wait_Writer) {
107 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
110 EXPECT_TRUE(l.try_lock());
111 EXPECT_FALSE(l.try_lock());
114 EXPECT_TRUE(l.try_lock());
115 EXPECT_FALSE(l.try_lock());
118 TYPED_TEST(RWSpinLockTest, Read_Holders) {
119 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
123 typename RWSpinLockType::ReadHolder guard(&l);
124 EXPECT_FALSE(l.try_lock());
125 EXPECT_TRUE(l.try_lock_shared());
128 EXPECT_FALSE(l.try_lock());
131 EXPECT_TRUE(l.try_lock());
135 TYPED_TEST(RWSpinLockTest, Write_Holders) {
136 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
139 typename RWSpinLockType::WriteHolder guard(&l);
140 EXPECT_FALSE(l.try_lock());
141 EXPECT_FALSE(l.try_lock_shared());
144 EXPECT_TRUE(l.try_lock_shared());
145 EXPECT_FALSE(l.try_lock());
147 EXPECT_TRUE(l.try_lock());
150 TYPED_TEST(RWSpinLockTest, ConcurrentTests) {
151 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
153 srand(time(nullptr));
155 std::vector<std::thread> threads;
156 for (int i = 0; i < FLAGS_num_threads; ++i) {
157 threads.push_back(std::thread(&run<RWSpinLockType>, &l));
161 stopThread.store(true, std::memory_order_release);
163 for (auto& t : threads) {
168 // RWSpinLock specific tests
170 TEST(RWSpinLock, lock_unlock_tests) {
171 folly::RWSpinLock lock;
172 EXPECT_TRUE(lock.try_lock_upgrade());
173 EXPECT_FALSE(lock.try_lock_shared());
174 EXPECT_FALSE(lock.try_lock());
175 EXPECT_FALSE(lock.try_lock_upgrade());
176 lock.unlock_upgrade();
178 EXPECT_FALSE(lock.try_lock());
179 EXPECT_TRUE(lock.try_lock_upgrade());
180 lock.unlock_upgrade();
181 lock.unlock_shared();
182 EXPECT_TRUE(lock.try_lock());
183 EXPECT_FALSE(lock.try_lock_upgrade());
184 lock.unlock_and_lock_upgrade();
185 EXPECT_FALSE(lock.try_lock_shared());
186 lock.unlock_upgrade_and_lock_shared();
187 lock.unlock_shared();
188 EXPECT_EQ(0, lock.bits());
191 TEST(RWSpinLock, concurrent_holder_test) {
192 srand(time(nullptr));
194 folly::RWSpinLock lock;
195 std::atomic<int64_t> reads(0);
196 std::atomic<int64_t> writes(0);
197 std::atomic<int64_t> upgrades(0);
198 std::atomic<bool> stop(false);
201 while (!stop.load(std::memory_order_acquire)) {
202 auto r = (uint32_t)(rand()) % 10;
203 if (r < 3) { // starts from write lock
204 RWSpinLock::ReadHolder rg{
205 RWSpinLock::UpgradedHolder{
206 RWSpinLock::WriteHolder{&lock}}};
207 writes.fetch_add(1, std::memory_order_acq_rel);;
208 } else if (r < 6) { // starts from upgrade lock
209 RWSpinLock::UpgradedHolder ug(&lock);
211 RWSpinLock::WriteHolder wg(std::move(ug));
213 RWSpinLock::ReadHolder rg(std::move(ug));
215 upgrades.fetch_add(1, std::memory_order_acq_rel);;
217 RWSpinLock::ReadHolder rg{&lock};
218 reads.fetch_add(1, std::memory_order_acq_rel);
223 std::vector<std::thread> threads;
224 for (int i = 0; i < FLAGS_num_threads; ++i) {
225 threads.push_back(std::thread(go));
229 stop.store(true, std::memory_order_release);
231 for (auto& t : threads) t.join();
233 LOG(INFO) << "reads: " << reads.load(std::memory_order_acquire)
234 << "; writes: " << writes.load(std::memory_order_acquire)
235 << "; upgrades: " << upgrades.load(std::memory_order_acquire);