2 * Copyright 2013 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 #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64__) || \
46 , RWTicketSpinLockT<32, true>,
47 RWTicketSpinLockT<32, false>,
48 RWTicketSpinLockT<64, true>,
49 RWTicketSpinLockT<64, false>
53 TYPED_TEST_CASE(RWSpinLockTest, Implementations);
55 template<typename RWSpinLockType>
56 static void run(RWSpinLockType* lock) {
59 while (!stopThread.load(std::memory_order_acquire)) {
60 if (rand() % 10 == 0) { // write
61 typename RWSpinLockType::WriteHolder guard(lock);
64 typename RWSpinLockType::ReadHolder guard(lock);
68 // VLOG(0) << "total reads: " << reads << "; total writes: " << writes;
72 TYPED_TEST(RWSpinLockTest, Writer_Wait_Readers) {
73 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
76 for (int i = 0; i < kMaxReaders; ++i) {
77 EXPECT_TRUE(l.try_lock_shared());
78 EXPECT_FALSE(l.try_lock());
81 for (int i = 0; i < kMaxReaders; ++i) {
82 EXPECT_FALSE(l.try_lock());
86 EXPECT_TRUE(l.try_lock());
89 TYPED_TEST(RWSpinLockTest, Readers_Wait_Writer) {
90 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
93 EXPECT_TRUE(l.try_lock());
95 for (int i = 0; i < kMaxReaders; ++i) {
96 EXPECT_FALSE(l.try_lock_shared());
99 l.unlock_and_lock_shared();
100 for (int i = 0; i < kMaxReaders - 1; ++i) {
101 EXPECT_TRUE(l.try_lock_shared());
105 TYPED_TEST(RWSpinLockTest, Writer_Wait_Writer) {
106 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
109 EXPECT_TRUE(l.try_lock());
110 EXPECT_FALSE(l.try_lock());
113 EXPECT_TRUE(l.try_lock());
114 EXPECT_FALSE(l.try_lock());
117 TYPED_TEST(RWSpinLockTest, Read_Holders) {
118 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
122 typename RWSpinLockType::ReadHolder guard(&l);
123 EXPECT_FALSE(l.try_lock());
124 EXPECT_TRUE(l.try_lock_shared());
127 EXPECT_FALSE(l.try_lock());
130 EXPECT_TRUE(l.try_lock());
134 TYPED_TEST(RWSpinLockTest, Write_Holders) {
135 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
138 typename RWSpinLockType::WriteHolder guard(&l);
139 EXPECT_FALSE(l.try_lock());
140 EXPECT_FALSE(l.try_lock_shared());
143 EXPECT_TRUE(l.try_lock_shared());
144 EXPECT_FALSE(l.try_lock());
146 EXPECT_TRUE(l.try_lock());
149 TYPED_TEST(RWSpinLockTest, ConcurrentTests) {
150 typedef typename TestFixture::RWSpinLockType RWSpinLockType;
152 srand(time(nullptr));
154 std::vector<std::thread> threads;
155 for (int i = 0; i < FLAGS_num_threads; ++i) {
156 threads.push_back(std::thread(&run<RWSpinLockType>, &l));
160 stopThread.store(true, std::memory_order_release);
162 for (auto& t : threads) {
167 // RWSpinLock specific tests
169 TEST(RWSpinLock, lock_unlock_tests) {
170 folly::RWSpinLock lock;
171 EXPECT_TRUE(lock.try_lock_upgrade());
172 EXPECT_FALSE(lock.try_lock_shared());
173 EXPECT_FALSE(lock.try_lock());
174 EXPECT_FALSE(lock.try_lock_upgrade());
175 lock.unlock_upgrade();
177 EXPECT_FALSE(lock.try_lock());
178 EXPECT_TRUE(lock.try_lock_upgrade());
179 lock.unlock_upgrade();
180 lock.unlock_shared();
181 EXPECT_TRUE(lock.try_lock());
182 EXPECT_FALSE(lock.try_lock_upgrade());
183 lock.unlock_and_lock_upgrade();
184 EXPECT_FALSE(lock.try_lock_shared());
185 lock.unlock_upgrade_and_lock_shared();
186 lock.unlock_shared();
187 EXPECT_EQ(0, lock.bits());
190 TEST(RWSpinLock, concurrent_holder_test) {
191 srand(time(nullptr));
193 folly::RWSpinLock lock;
194 std::atomic<int64_t> reads(0);
195 std::atomic<int64_t> writes(0);
196 std::atomic<int64_t> upgrades(0);
197 std::atomic<bool> stop(false);
200 while (!stop.load(std::memory_order_acquire)) {
201 auto r = (uint32_t)(rand()) % 10;
202 if (r < 3) { // starts from write lock
203 RWSpinLock::ReadHolder rg{
204 RWSpinLock::UpgradedHolder{
205 RWSpinLock::WriteHolder{&lock}}};
206 writes.fetch_add(1, std::memory_order_acq_rel);;
207 } else if (r < 6) { // starts from upgrade lock
208 RWSpinLock::UpgradedHolder ug(&lock);
210 RWSpinLock::WriteHolder wg(std::move(ug));
212 RWSpinLock::ReadHolder rg(std::move(ug));
214 upgrades.fetch_add(1, std::memory_order_acq_rel);;
216 RWSpinLock::ReadHolder rg{&lock};
217 reads.fetch_add(1, std::memory_order_acq_rel);
222 std::vector<std::thread> threads;
223 for (int i = 0; i < FLAGS_num_threads; ++i) {
224 threads.push_back(std::thread(go));
228 stop.store(true, std::memory_order_release);
230 for (auto& t : threads) t.join();
232 LOG(INFO) << "reads: " << reads.load(std::memory_order_acquire)
233 << "; writes: " << writes.load(std::memory_order_acquire)
234 << "; upgrades: " << upgrades.load(std::memory_order_acquire);
239 int main(int argc, char** argv) {
240 testing::InitGoogleTest(&argc, argv);
241 google::ParseCommandLineFlags(&argc, &argv, true);
242 return RUN_ALL_TESTS();