Split benchmarks and tests
[folly.git] / folly / test / RWSpinLockTest.cpp
1 /*
2  * Copyright 2016 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 <glog/logging.h>
28 #include <folly/RWSpinLock.h>
29
30 #if FOLLY_HAVE_LIBGFLAGS
31 #include <gflags/gflags.h>
32 DEFINE_int32(num_threads, 8, "num threads");
33 #else
34 constexpr int FLAGS_num_threads = 8;
35 #endif
36
37 namespace {
38
39 static const int kMaxReaders = 50;
40 static std::atomic<bool> stopThread;
41 using namespace folly;
42
43 template<typename RWSpinLockT> struct RWSpinLockTest: public testing::Test {
44   typedef RWSpinLockT RWSpinLockType;
45 };
46
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>
53 #endif
54 > Implementations;
55
56 TYPED_TEST_CASE(RWSpinLockTest, Implementations);
57
58 template<typename RWSpinLockType>
59 static void run(RWSpinLockType* lock) {
60   int64_t reads = 0;
61   int64_t writes = 0;
62   while (!stopThread.load(std::memory_order_acquire)) {
63     if (rand() % 10 == 0) { // write
64       typename RWSpinLockType::WriteHolder guard(lock);
65       ++writes;
66     } else { // read
67       typename RWSpinLockType::ReadHolder guard(lock);
68       ++reads;
69     }
70   }
71   // VLOG(0) << "total reads: " << reads << "; total writes: " << writes;
72 }
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) {
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();
179   lock.lock_shared();
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());
191 }
192
193 TEST(RWSpinLock, concurrent_holder_test) {
194   srand(time(nullptr));
195
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);
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
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);
212         if (r < 4) {
213           RWSpinLock::WriteHolder wg(std::move(ug));
214         } else {
215           RWSpinLock::ReadHolder rg(std::move(ug));
216         }
217         upgrades.fetch_add(1, std::memory_order_acq_rel);;
218       } else {
219         RWSpinLock::ReadHolder rg{&lock};
220         reads.fetch_add(1, std::memory_order_acq_rel);
221       }
222     }
223   };
224
225   std::vector<std::thread> threads;
226   for (int i = 0; i < FLAGS_num_threads; ++i) {
227     threads.push_back(std::thread(go));
228   }
229
230   sleep(5);
231   stop.store(true, std::memory_order_release);
232
233   for (auto& t : threads) t.join();
234
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);
238 }
239
240 }