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)
26 #include <gtest/gtest.h>
27 #include <glog/logging.h>
28 #include <folly/RWSpinLock.h>
30 #if FOLLY_HAVE_LIBGFLAGS
31 #include <gflags/gflags.h>
32 DEFINE_int32(num_threads, 8, "num threads");
34 constexpr int FLAGS_num_threads = 8;
39 static const int kMaxReaders = 50;
40 static std::atomic<bool> stopThread;
41 using namespace folly;
43 template<typename RWSpinLockT> struct RWSpinLockTest: public testing::Test {
44 typedef RWSpinLockT RWSpinLockType;
47 typedef testing::Types<RWSpinLock
48 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
49 , RWTicketSpinLockT<32, true>,
50 RWTicketSpinLockT<32, false>,
51 RWTicketSpinLockT<64, true>,
52 RWTicketSpinLockT<64, false>
56 TYPED_TEST_CASE(RWSpinLockTest, Implementations);
58 template<typename RWSpinLockType>
59 static void run(RWSpinLockType* lock) {
62 while (!stopThread.load(std::memory_order_acquire)) {
63 if (rand() % 10 == 0) { // write
64 typename RWSpinLockType::WriteHolder guard(lock);
67 typename RWSpinLockType::ReadHolder guard(lock);
71 // VLOG(0) << "total reads: " << reads << "; total writes: " << writes;
75 TYPED_TEST(RWSpinLockTest, Writer_Wait_Readers) {
76 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
79 for (int i = 0; i < kMaxReaders; ++i) {
80 EXPECT_TRUE(l.try_lock_shared());
81 EXPECT_FALSE(l.try_lock());
84 for (int i = 0; i < kMaxReaders; ++i) {
85 EXPECT_FALSE(l.try_lock());
89 EXPECT_TRUE(l.try_lock());
92 TYPED_TEST(RWSpinLockTest, Readers_Wait_Writer) {
93 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
96 EXPECT_TRUE(l.try_lock());
98 for (int i = 0; i < kMaxReaders; ++i) {
99 EXPECT_FALSE(l.try_lock_shared());
102 l.unlock_and_lock_shared();
103 for (int i = 0; i < kMaxReaders - 1; ++i) {
104 EXPECT_TRUE(l.try_lock_shared());
108 TYPED_TEST(RWSpinLockTest, Writer_Wait_Writer) {
109 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
112 EXPECT_TRUE(l.try_lock());
113 EXPECT_FALSE(l.try_lock());
116 EXPECT_TRUE(l.try_lock());
117 EXPECT_FALSE(l.try_lock());
120 TYPED_TEST(RWSpinLockTest, Read_Holders) {
121 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
125 typename RWSpinLockType::ReadHolder guard(&l);
126 EXPECT_FALSE(l.try_lock());
127 EXPECT_TRUE(l.try_lock_shared());
130 EXPECT_FALSE(l.try_lock());
133 EXPECT_TRUE(l.try_lock());
137 TYPED_TEST(RWSpinLockTest, Write_Holders) {
138 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
141 typename RWSpinLockType::WriteHolder guard(&l);
142 EXPECT_FALSE(l.try_lock());
143 EXPECT_FALSE(l.try_lock_shared());
146 EXPECT_TRUE(l.try_lock_shared());
147 EXPECT_FALSE(l.try_lock());
149 EXPECT_TRUE(l.try_lock());
152 TYPED_TEST(RWSpinLockTest, ConcurrentTests) {
153 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
155 srand(time(nullptr));
157 std::vector<std::thread> threads;
158 for (int i = 0; i < FLAGS_num_threads; ++i) {
159 threads.push_back(std::thread(&run<RWSpinLockType>, &l));
163 stopThread.store(true, std::memory_order_release);
165 for (auto& t : threads) {
170 // RWSpinLock specific tests
172 TEST(RWSpinLock, lock_unlock_tests) {
173 folly::RWSpinLock lock;
174 EXPECT_TRUE(lock.try_lock_upgrade());
175 EXPECT_FALSE(lock.try_lock_shared());
176 EXPECT_FALSE(lock.try_lock());
177 EXPECT_FALSE(lock.try_lock_upgrade());
178 lock.unlock_upgrade();
180 EXPECT_FALSE(lock.try_lock());
181 EXPECT_TRUE(lock.try_lock_upgrade());
182 lock.unlock_upgrade();
183 lock.unlock_shared();
184 EXPECT_TRUE(lock.try_lock());
185 EXPECT_FALSE(lock.try_lock_upgrade());
186 lock.unlock_and_lock_upgrade();
187 EXPECT_FALSE(lock.try_lock_shared());
188 lock.unlock_upgrade_and_lock_shared();
189 lock.unlock_shared();
190 EXPECT_EQ(0, lock.bits());
193 TEST(RWSpinLock, concurrent_holder_test) {
194 srand(time(nullptr));
196 folly::RWSpinLock lock;
197 std::atomic<int64_t> reads(0);
198 std::atomic<int64_t> writes(0);
199 std::atomic<int64_t> upgrades(0);
200 std::atomic<bool> stop(false);
203 while (!stop.load(std::memory_order_acquire)) {
204 auto r = (uint32_t)(rand()) % 10;
205 if (r < 3) { // starts from write lock
206 RWSpinLock::ReadHolder rg{
207 RWSpinLock::UpgradedHolder{
208 RWSpinLock::WriteHolder{&lock}}};
209 writes.fetch_add(1, std::memory_order_acq_rel);;
210 } else if (r < 6) { // starts from upgrade lock
211 RWSpinLock::UpgradedHolder ug(&lock);
213 RWSpinLock::WriteHolder wg(std::move(ug));
215 RWSpinLock::ReadHolder rg(std::move(ug));
217 upgrades.fetch_add(1, std::memory_order_acq_rel);;
219 RWSpinLock::ReadHolder rg{&lock};
220 reads.fetch_add(1, std::memory_order_acq_rel);
225 std::vector<std::thread> threads;
226 for (int i = 0; i < FLAGS_num_threads; ++i) {
227 threads.push_back(std::thread(go));
231 stop.store(true, std::memory_order_release);
233 for (auto& t : threads) t.join();
235 LOG(INFO) << "reads: " << reads.load(std::memory_order_acquire)
236 << "; writes: " << writes.load(std::memory_order_acquire)
237 << "; upgrades: " << upgrades.load(std::memory_order_acquire);