proxygen
RWSpinLockTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-present Facebook, Inc.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 //
17 // @author xliu (xliux@fb.com)
18 //
19 
21 
22 #include <stdlib.h>
23 #include <thread>
24 #include <vector>
25 
26 #include <glog/logging.h>
27 
31 
32 DEFINE_int32(num_threads, 8, "num threads");
33 
34 namespace {
35 
36 static const int kMaxReaders = 50;
37 static std::atomic<bool> stopThread;
38 using namespace folly;
39 
40 template <typename RWSpinLockT>
41 struct RWSpinLockTest : public testing::Test {
42  typedef RWSpinLockT RWSpinLockType;
43 };
44 
45 typedef testing::Types<
47 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
48  ,
49  RWTicketSpinLockT<32, true>,
50  RWTicketSpinLockT<32, false>,
51  RWTicketSpinLockT<64, true>,
52  RWTicketSpinLockT<64, false>
53 #endif
54  >
55  Implementations;
56 
57 TYPED_TEST_CASE(RWSpinLockTest, Implementations);
58 
59 template <typename RWSpinLockType>
60 static void run(RWSpinLockType* lock) {
61  int64_t reads = 0;
62  int64_t writes = 0;
63  while (!stopThread.load(std::memory_order_acquire)) {
64  if (rand() % 10 == 0) { // write
65  typename RWSpinLockType::WriteHolder guard(lock);
66  ++writes;
67  } else { // read
68  typename RWSpinLockType::ReadHolder guard(lock);
69  ++reads;
70  }
71  }
72  // VLOG(0) << "total reads: " << reads << "; total writes: " << writes;
73 }
74 
75 TYPED_TEST(RWSpinLockTest, Writer_Wait_Readers) {
76  typedef typename TestFixture::RWSpinLockType RWSpinLockType;
77  RWSpinLockType l;
78 
79  for (int i = 0; i < kMaxReaders; ++i) {
80  EXPECT_TRUE(l.try_lock_shared());
81  EXPECT_FALSE(l.try_lock());
82  }
83 
84  for (int i = 0; i < kMaxReaders; ++i) {
85  EXPECT_FALSE(l.try_lock());
86  l.unlock_shared();
87  }
88 
89  EXPECT_TRUE(l.try_lock());
90 }
91 
92 TYPED_TEST(RWSpinLockTest, Readers_Wait_Writer) {
93  typedef typename TestFixture::RWSpinLockType RWSpinLockType;
94  RWSpinLockType l;
95 
96  EXPECT_TRUE(l.try_lock());
97 
98  for (int i = 0; i < kMaxReaders; ++i) {
99  EXPECT_FALSE(l.try_lock_shared());
100  }
101 
102  l.unlock_and_lock_shared();
103  for (int i = 0; i < kMaxReaders - 1; ++i) {
104  EXPECT_TRUE(l.try_lock_shared());
105  }
106 }
107 
108 TYPED_TEST(RWSpinLockTest, Writer_Wait_Writer) {
109  typedef typename TestFixture::RWSpinLockType RWSpinLockType;
110  RWSpinLockType l;
111 
112  EXPECT_TRUE(l.try_lock());
113  EXPECT_FALSE(l.try_lock());
114  l.unlock();
115 
116  EXPECT_TRUE(l.try_lock());
117  EXPECT_FALSE(l.try_lock());
118 }
119 
120 TYPED_TEST(RWSpinLockTest, Read_Holders) {
121  typedef typename TestFixture::RWSpinLockType RWSpinLockType;
122  RWSpinLockType l;
123 
124  {
125  typename RWSpinLockType::ReadHolder guard(&l);
126  EXPECT_FALSE(l.try_lock());
127  EXPECT_TRUE(l.try_lock_shared());
128  l.unlock_shared();
129 
130  EXPECT_FALSE(l.try_lock());
131  }
132 
133  EXPECT_TRUE(l.try_lock());
134  l.unlock();
135 }
136 
137 TYPED_TEST(RWSpinLockTest, Write_Holders) {
138  typedef typename TestFixture::RWSpinLockType RWSpinLockType;
139  RWSpinLockType l;
140  {
141  typename RWSpinLockType::WriteHolder guard(&l);
142  EXPECT_FALSE(l.try_lock());
143  EXPECT_FALSE(l.try_lock_shared());
144  }
145 
146  EXPECT_TRUE(l.try_lock_shared());
147  EXPECT_FALSE(l.try_lock());
148  l.unlock_shared();
149  EXPECT_TRUE(l.try_lock());
150 }
151 
152 TYPED_TEST(RWSpinLockTest, ConcurrentTests) {
153  typedef typename TestFixture::RWSpinLockType RWSpinLockType;
154  RWSpinLockType l;
155  srand(time(nullptr));
156 
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));
160  }
161 
162  sleep(1);
163  stopThread.store(true, std::memory_order_release);
164 
165  for (auto& t : threads) {
166  t.join();
167  }
168 }
169 
170 // RWSpinLock specific tests
171 
172 TEST(RWSpinLock, lock_unlock_tests) {
176  EXPECT_FALSE(lock.try_lock());
178  lock.unlock_upgrade();
179  lock.lock_shared();
180  EXPECT_FALSE(lock.try_lock());
182  lock.unlock_upgrade();
183  lock.unlock_shared();
184  EXPECT_TRUE(lock.try_lock());
189  lock.unlock_shared();
190  EXPECT_EQ(0, lock.bits());
191 }
192 
193 TEST(RWSpinLock, concurrent_holder_test) {
194  srand(time(nullptr));
195 
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);
201 
202  auto go = [&] {
203  while (!stop.load(std::memory_order_acquire)) {
204  auto r = (uint32_t)(rand()) % 10;
205  if (r < 3) { // starts from write lock
208  writes.fetch_add(1, std::memory_order_acq_rel);
209  } else if (r < 6) { // starts from upgrade lock
210  RWSpinLock::UpgradedHolder ug(&lock);
211  if (r < 4) {
213  } else {
215  }
216  upgrades.fetch_add(1, std::memory_order_acq_rel);
217  } else {
218  RWSpinLock::ReadHolder rg{&lock};
219  reads.fetch_add(1, std::memory_order_acq_rel);
220  }
221  }
222  };
223 
224  std::vector<std::thread> threads;
225  for (int i = 0; i < FLAGS_num_threads; ++i) {
226  threads.push_back(std::thread(go));
227  }
228 
229  sleep(5);
230  stop.store(true, std::memory_order_release);
231 
232  for (auto& t : threads) {
233  t.join();
234  }
235 
236  LOG(INFO) << "reads: " << reads.load(std::memory_order_acquire)
237  << "; writes: " << writes.load(std::memory_order_acquire)
238  << "; upgrades: " << upgrades.load(std::memory_order_acquire);
239 }
240 
241 } // namespace
void unlock_upgrade_and_lock_shared()
Definition: RWSpinLock.h:251
void unlock_and_lock_upgrade()
Definition: RWSpinLock.h:256
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
Future< Unit > sleep(Duration dur, Timekeeper *tk)
Definition: Future.cpp:42
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
void unlock_shared()
Definition: RWSpinLock.h:216
TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
bool try_lock_shared()
Definition: RWSpinLock.h:276
std::vector< std::thread::id > threads
static void stop()
static void run(EventBaseManager *ebm, EventBase *eb, folly::Baton<> *stop, const StringPiece &name)
TYPED_TEST(SynchronizedTest, Basic)
GuardImpl guard(ErrorHandler &&handler)
Definition: Base.h:840
auto lock(Synchronized< D, M > &synchronized, Args &&...args)
DEFINE_int32(num_threads, 8,"num threads")
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
bool try_lock_upgrade()
Definition: RWSpinLock.h:295
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
void unlock_upgrade()
Definition: RWSpinLock.h:236
TEST(SequencedExecutor, CPUThreadPoolExecutor)
int32_t bits() const
Definition: RWSpinLock.h:306
std::chrono::nanoseconds time()