Pull from FB rev 63ce89e2f2301e6bba44a111cc7d4218022156f6
[folly.git] / folly / test / RWSpinLockTest.cpp
1 /*
2  * Copyright 2012 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
25 #include <boost/thread.hpp>
26
27 #include "gtest/gtest.h"
28 #include <gflags/gflags.h>
29 #include <glog/logging.h>
30 #include "folly/RWSpinLock.h"
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> struct RWSpinLockTest: public testing::Test {
41   typedef RWSpinLockT RWSpinLockType;
42 };
43
44 typedef testing::Types<RWSpinLock
45 #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64__) || \
46     defined(ARCH_K8))
47         , RWTicketSpinLockT<32, true>,
48         RWTicketSpinLockT<32, false>,
49         RWTicketSpinLockT<64, true>,
50         RWTicketSpinLockT<64, false>
51 #endif
52 > Implementations;
53
54 TYPED_TEST_CASE(RWSpinLockTest, Implementations);
55
56 template<typename RWSpinLockType>
57 static void run(RWSpinLockType* lock) {
58   int64_t reads = 0;
59   int64_t writes = 0;
60   while (!stopThread.load(std::memory_order_acquire)) {
61     if (rand() % 10 == 0) { // write
62       typename RWSpinLockType::WriteHolder guard(lock);
63       ++writes;
64     } else { // read
65       typename RWSpinLockType::ReadHolder guard(lock);
66       ++reads;
67     }
68   }
69   VLOG(0) << "total reads: " << reads << "; total writes: " << writes;
70 }
71
72
73 TYPED_TEST(RWSpinLockTest, Writer_Wait_Readers) {
74   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
75   RWSpinLockType l;
76
77   for (int i = 0; i < kMaxReaders; ++i) {
78     EXPECT_TRUE(l.try_lock_shared());
79     EXPECT_FALSE(l.try_lock());
80   }
81
82   for (int i = 0; i < kMaxReaders; ++i) {
83     EXPECT_FALSE(l.try_lock());
84     l.unlock_shared();
85   }
86
87   EXPECT_TRUE(l.try_lock());
88 }
89
90 TYPED_TEST(RWSpinLockTest, Readers_Wait_Writer) {
91   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
92   RWSpinLockType l;
93
94   EXPECT_TRUE(l.try_lock());
95
96   for (int i = 0; i < kMaxReaders; ++i) {
97     EXPECT_FALSE(l.try_lock_shared());
98   }
99
100   l.unlock_and_lock_shared();
101   for (int i = 0; i < kMaxReaders - 1; ++i) {
102     EXPECT_TRUE(l.try_lock_shared());
103   }
104 }
105
106 TYPED_TEST(RWSpinLockTest, Writer_Wait_Writer) {
107   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
108   RWSpinLockType l;
109
110   EXPECT_TRUE(l.try_lock());
111   EXPECT_FALSE(l.try_lock());
112   l.unlock();
113
114   EXPECT_TRUE(l.try_lock());
115   EXPECT_FALSE(l.try_lock());
116 }
117
118 TYPED_TEST(RWSpinLockTest, Read_Holders) {
119   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
120   RWSpinLockType l;
121
122   {
123     typename RWSpinLockType::ReadHolder guard(&l);
124     EXPECT_FALSE(l.try_lock());
125     EXPECT_TRUE(l.try_lock_shared());
126     l.unlock_shared();
127
128     EXPECT_FALSE(l.try_lock());
129   }
130
131   EXPECT_TRUE(l.try_lock());
132   l.unlock();
133 }
134
135 TYPED_TEST(RWSpinLockTest, Write_Holders) {
136   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
137   RWSpinLockType l;
138   {
139     typename RWSpinLockType::WriteHolder guard(&l);
140     EXPECT_FALSE(l.try_lock());
141     EXPECT_FALSE(l.try_lock_shared());
142   }
143
144   EXPECT_TRUE(l.try_lock_shared());
145   EXPECT_FALSE(l.try_lock());
146   l.unlock_shared();
147   EXPECT_TRUE(l.try_lock());
148 }
149
150 TYPED_TEST(RWSpinLockTest, ConcurrentTests) {
151   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
152   RWSpinLockType l;
153   srand(time(NULL));
154
155   std::vector<boost::thread> threads;
156   for (int i = 0; i < FLAGS_num_threads; ++i) {
157     threads.push_back(boost::thread(&run<RWSpinLockType>, &l));
158   }
159
160   sleep(1);
161   stopThread.store(true, std::memory_order_release);
162
163   for (auto& t : threads) {
164     t.join();
165   }
166 }
167
168 // RWSpinLock specific tests
169
170 TEST(RWSpinLock, lock_unlock_tests) {
171   folly::RWSpinLock lock;
172   EXPECT_TRUE(lock.try_lock_upgrade());
173   EXPECT_TRUE(lock.try_lock_shared());
174   EXPECT_FALSE(lock.try_lock());
175   EXPECT_FALSE(lock.try_lock_upgrade());
176   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_TRUE(lock.try_lock_shared());
185   lock.unlock_shared();
186   lock.unlock_upgrade_and_lock_shared();
187   lock.unlock_shared();
188   EXPECT_EQ(0, lock.bits());
189 }
190
191 TEST(RWSpinLock, concurrent_holder_test) {
192   srand(time(NULL));
193
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);
199
200   auto go = [&] {
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 ug(
206               RWSpinLock::WriteHolder(&lock)));
207         writes.fetch_add(1, std::memory_order_acq_rel);;
208
209       } else if (r < 6) {   // starts from upgrade lock
210         RWSpinLock::UpgradedHolder ug(&lock);
211         if (r < 4) {
212           RWSpinLock::WriteHolder wg(std::move(ug));
213         } else {
214           RWSpinLock::ReadHolder rg(std::move(ug));
215         }
216         upgrades.fetch_add(1, std::memory_order_acq_rel);;
217       } else {
218         RWSpinLock::UpgradedHolder ug(
219             RWSpinLock::WriteHolder(
220               RWSpinLock::ReadHolder(&lock)));
221         reads.fetch_add(1, std::memory_order_acq_rel);
222       }
223     }
224   };
225
226   std::vector<boost::thread> threads;
227   for (int i = 0; i < FLAGS_num_threads; ++i) {
228     threads.push_back(boost::thread(go));
229   }
230
231   sleep(5);
232   stop.store(true, std::memory_order_release);
233
234   for (auto& t : threads) t.join();
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 }
242
243 int main(int argc, char** argv) {
244   testing::InitGoogleTest(&argc, argv);
245   google::ParseCommandLineFlags(&argc, &argv, true);
246   return RUN_ALL_TESTS();
247 }