Move folly/RWSpinLock.h to folly/synchronization/
[folly.git] / folly / synchronization / test / RWSpinLockTest.cpp
1 /*
2  * Copyright 2017-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
20 #include <folly/synchronization/RWSpinLock.h>
21
22 #include <stdlib.h>
23 #include <thread>
24 #include <vector>
25
26 #include <glog/logging.h>
27
28 #include <folly/portability/GFlags.h>
29 #include <folly/portability/GTest.h>
30 #include <folly/portability/Unistd.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 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
46         , RWTicketSpinLockT<32, true>,
47         RWTicketSpinLockT<32, false>,
48         RWTicketSpinLockT<64, true>,
49         RWTicketSpinLockT<64, false>
50 #endif
51 > Implementations;
52
53 TYPED_TEST_CASE(RWSpinLockTest, Implementations);
54
55 template <typename RWSpinLockType>
56 static void run(RWSpinLockType* lock) {
57   int64_t reads = 0;
58   int64_t writes = 0;
59   while (!stopThread.load(std::memory_order_acquire)) {
60     if (rand() % 10 == 0) { // write
61       typename RWSpinLockType::WriteHolder guard(lock);
62       ++writes;
63     } else { // read
64       typename RWSpinLockType::ReadHolder guard(lock);
65       ++reads;
66     }
67   }
68   // VLOG(0) << "total reads: " << reads << "; total writes: " << writes;
69 }
70
71
72 TYPED_TEST(RWSpinLockTest, Writer_Wait_Readers) {
73   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
74   RWSpinLockType l;
75
76   for (int i = 0; i < kMaxReaders; ++i) {
77     EXPECT_TRUE(l.try_lock_shared());
78     EXPECT_FALSE(l.try_lock());
79   }
80
81   for (int i = 0; i < kMaxReaders; ++i) {
82     EXPECT_FALSE(l.try_lock());
83     l.unlock_shared();
84   }
85
86   EXPECT_TRUE(l.try_lock());
87 }
88
89 TYPED_TEST(RWSpinLockTest, Readers_Wait_Writer) {
90   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
91   RWSpinLockType l;
92
93   EXPECT_TRUE(l.try_lock());
94
95   for (int i = 0; i < kMaxReaders; ++i) {
96     EXPECT_FALSE(l.try_lock_shared());
97   }
98
99   l.unlock_and_lock_shared();
100   for (int i = 0; i < kMaxReaders - 1; ++i) {
101     EXPECT_TRUE(l.try_lock_shared());
102   }
103 }
104
105 TYPED_TEST(RWSpinLockTest, Writer_Wait_Writer) {
106   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
107   RWSpinLockType l;
108
109   EXPECT_TRUE(l.try_lock());
110   EXPECT_FALSE(l.try_lock());
111   l.unlock();
112
113   EXPECT_TRUE(l.try_lock());
114   EXPECT_FALSE(l.try_lock());
115 }
116
117 TYPED_TEST(RWSpinLockTest, Read_Holders) {
118   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
119   RWSpinLockType l;
120
121   {
122     typename RWSpinLockType::ReadHolder guard(&l);
123     EXPECT_FALSE(l.try_lock());
124     EXPECT_TRUE(l.try_lock_shared());
125     l.unlock_shared();
126
127     EXPECT_FALSE(l.try_lock());
128   }
129
130   EXPECT_TRUE(l.try_lock());
131   l.unlock();
132 }
133
134 TYPED_TEST(RWSpinLockTest, Write_Holders) {
135   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
136   RWSpinLockType l;
137   {
138     typename RWSpinLockType::WriteHolder guard(&l);
139     EXPECT_FALSE(l.try_lock());
140     EXPECT_FALSE(l.try_lock_shared());
141   }
142
143   EXPECT_TRUE(l.try_lock_shared());
144   EXPECT_FALSE(l.try_lock());
145   l.unlock_shared();
146   EXPECT_TRUE(l.try_lock());
147 }
148
149 TYPED_TEST(RWSpinLockTest, ConcurrentTests) {
150   typedef typename TestFixture::RWSpinLockType RWSpinLockType;
151   RWSpinLockType l;
152   srand(time(nullptr));
153
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));
157   }
158
159   sleep(1);
160   stopThread.store(true, std::memory_order_release);
161
162   for (auto& t : threads) {
163     t.join();
164   }
165 }
166
167 // RWSpinLock specific tests
168
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();
176   lock.lock_shared();
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());
188 }
189
190 TEST(RWSpinLock, concurrent_holder_test) {
191   srand(time(nullptr));
192
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);
198
199   auto go = [&] {
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);
209         if (r < 4) {
210           RWSpinLock::WriteHolder wg(std::move(ug));
211         } else {
212           RWSpinLock::ReadHolder rg(std::move(ug));
213         }
214         upgrades.fetch_add(1, std::memory_order_acq_rel);;
215       } else {
216         RWSpinLock::ReadHolder rg{&lock};
217         reads.fetch_add(1, std::memory_order_acq_rel);
218       }
219     }
220   };
221
222   std::vector<std::thread> threads;
223   for (int i = 0; i < FLAGS_num_threads; ++i) {
224     threads.push_back(std::thread(go));
225   }
226
227   sleep(5);
228   stop.store(true, std::memory_order_release);
229
230   for (auto& t : threads) {
231     t.join();
232   }
233
234   LOG(INFO) << "reads: " << reads.load(std::memory_order_acquire)
235     << "; writes: " << writes.load(std::memory_order_acquire)
236     << "; upgrades: " << upgrades.load(std::memory_order_acquire);
237 }
238
239 } // namespace