2369eef09e2370bc5e7522298459de8dc2343872
[folly.git] / folly / test / RWSpinLockTest.cpp
1 /*
2  * Copyright 2014 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 //
18 // @author xliu (xliux@fb.com)
19 //
20
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <vector>
24 #include <thread>
25
26 #include <gtest/gtest.h>
27 #include <gflags/gflags.h>
28 #include <glog/logging.h>
29 #include "folly/RWSpinLock.h"
30
31 DEFINE_int32(num_threads, 8, "num threads");
32
33 namespace {
34
35 static const int kMaxReaders = 50;
36 static std::atomic<bool> stopThread;
37 using namespace folly;
38
39 template<typename RWSpinLockT> struct RWSpinLockTest: public testing::Test {
40   typedef RWSpinLockT RWSpinLockType;
41 };
42
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>
49 #endif
50 > Implementations;
51
52 TYPED_TEST_CASE(RWSpinLockTest, Implementations);
53
54 template<typename RWSpinLockType>
55 static void run(RWSpinLockType* lock) {
56   int64_t reads = 0;
57   int64_t writes = 0;
58   while (!stopThread.load(std::memory_order_acquire)) {
59     if (rand() % 10 == 0) { // write
60       typename RWSpinLockType::WriteHolder guard(lock);
61       ++writes;
62     } else { // read
63       typename RWSpinLockType::ReadHolder guard(lock);
64       ++reads;
65     }
66   }
67   // VLOG(0) << "total reads: " << reads << "; total writes: " << writes;
68 }
69
70
71 TYPED_TEST(RWSpinLockTest, Writer_Wait_Readers) {
72   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
73   RWSpinLockType l;
74
75   for (int i = 0; i < kMaxReaders; ++i) {
76     EXPECT_TRUE(l.try_lock_shared());
77     EXPECT_FALSE(l.try_lock());
78   }
79
80   for (int i = 0; i < kMaxReaders; ++i) {
81     EXPECT_FALSE(l.try_lock());
82     l.unlock_shared();
83   }
84
85   EXPECT_TRUE(l.try_lock());
86 }
87
88 TYPED_TEST(RWSpinLockTest, Readers_Wait_Writer) {
89   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
90   RWSpinLockType l;
91
92   EXPECT_TRUE(l.try_lock());
93
94   for (int i = 0; i < kMaxReaders; ++i) {
95     EXPECT_FALSE(l.try_lock_shared());
96   }
97
98   l.unlock_and_lock_shared();
99   for (int i = 0; i < kMaxReaders - 1; ++i) {
100     EXPECT_TRUE(l.try_lock_shared());
101   }
102 }
103
104 TYPED_TEST(RWSpinLockTest, Writer_Wait_Writer) {
105   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
106   RWSpinLockType l;
107
108   EXPECT_TRUE(l.try_lock());
109   EXPECT_FALSE(l.try_lock());
110   l.unlock();
111
112   EXPECT_TRUE(l.try_lock());
113   EXPECT_FALSE(l.try_lock());
114 }
115
116 TYPED_TEST(RWSpinLockTest, Read_Holders) {
117   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
118   RWSpinLockType l;
119
120   {
121     typename RWSpinLockType::ReadHolder guard(&l);
122     EXPECT_FALSE(l.try_lock());
123     EXPECT_TRUE(l.try_lock_shared());
124     l.unlock_shared();
125
126     EXPECT_FALSE(l.try_lock());
127   }
128
129   EXPECT_TRUE(l.try_lock());
130   l.unlock();
131 }
132
133 TYPED_TEST(RWSpinLockTest, Write_Holders) {
134   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
135   RWSpinLockType l;
136   {
137     typename RWSpinLockType::WriteHolder guard(&l);
138     EXPECT_FALSE(l.try_lock());
139     EXPECT_FALSE(l.try_lock_shared());
140   }
141
142   EXPECT_TRUE(l.try_lock_shared());
143   EXPECT_FALSE(l.try_lock());
144   l.unlock_shared();
145   EXPECT_TRUE(l.try_lock());
146 }
147
148 TYPED_TEST(RWSpinLockTest, ConcurrentTests) {
149   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
150   RWSpinLockType l;
151   srand(time(nullptr));
152
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));
156   }
157
158   sleep(1);
159   stopThread.store(true, std::memory_order_release);
160
161   for (auto& t : threads) {
162     t.join();
163   }
164 }
165
166 // RWSpinLock specific tests
167
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();
175   lock.lock_shared();
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());
187 }
188
189 TEST(RWSpinLock, concurrent_holder_test) {
190   srand(time(nullptr));
191
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);
197
198   auto go = [&] {
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);
208         if (r < 4) {
209           RWSpinLock::WriteHolder wg(std::move(ug));
210         } else {
211           RWSpinLock::ReadHolder rg(std::move(ug));
212         }
213         upgrades.fetch_add(1, std::memory_order_acq_rel);;
214       } else {
215         RWSpinLock::ReadHolder rg{&lock};
216         reads.fetch_add(1, std::memory_order_acq_rel);
217       }
218     }
219   };
220
221   std::vector<std::thread> threads;
222   for (int i = 0; i < FLAGS_num_threads; ++i) {
223     threads.push_back(std::thread(go));
224   }
225
226   sleep(5);
227   stop.store(true, std::memory_order_release);
228
229   for (auto& t : threads) t.join();
230
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);
234 }
235
236 }
237
238 int main(int argc, char** argv) {
239   testing::InitGoogleTest(&argc, argv);
240   google::ParseCommandLineFlags(&argc, &argv, true);
241   return RUN_ALL_TESTS();
242 }