RCU
[folly.git] / folly / synchronization / test / RcuTest.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 #include <folly/synchronization/Rcu.h>
17
18 #include <thread>
19 #include <vector>
20
21 #include <glog/logging.h>
22
23 #include <folly/Benchmark.h>
24 #include <folly/Random.h>
25 #include <folly/portability/GFlags.h>
26 #include <folly/portability/GTest.h>
27 #include <folly/synchronization/Baton.h>
28
29 using namespace folly;
30
31 DEFINE_int64(iters, 100000, "Number of iterations");
32 DEFINE_int64(threads, 32, "Number of threads");
33
34 TEST(RcuTest, Basic) {
35   auto foo = new int(2);
36   rcu_retire(foo);
37 }
38
39 class des {
40   bool* d_;
41
42  public:
43   des(bool* d) : d_(d) {}
44   ~des() {
45     *d_ = true;
46   }
47 };
48
49 TEST(RcuTest, Guard) {
50   bool del = false;
51   auto foo = new des(&del);
52   { rcu_reader g; }
53   rcu_retire(foo);
54   synchronize_rcu();
55   EXPECT_TRUE(del);
56 }
57
58 TEST(RcuTest, Perf) {
59   long i = FLAGS_iters;
60   auto start = std::chrono::steady_clock::now();
61   while (i-- > 0) {
62     rcu_reader g;
63   }
64   auto diff = std::chrono::steady_clock::now() - start;
65   printf(
66       "Total time %li ns \n",
67       std::chrono::duration_cast<std::chrono::nanoseconds>(diff).count() /
68           FLAGS_iters);
69 }
70
71 TEST(RcuTest, ResetPerf) {
72   long i = FLAGS_iters;
73   auto start = std::chrono::steady_clock::now();
74   while (i-- > 0) {
75     rcu_retire<int>(nullptr, [](int*) {});
76   }
77   auto diff = std::chrono::steady_clock::now() - start;
78   printf(
79       "Total time %li ns \n",
80       std::chrono::duration_cast<std::chrono::nanoseconds>(diff).count() /
81           FLAGS_iters);
82 }
83
84 TEST(RcuTest, SlowReader) {
85   std::thread t;
86   {
87     rcu_reader g;
88
89     t = std::thread([&]() { synchronize_rcu(); });
90     usleep(100); // Wait for synchronize to start
91   }
92   t.join();
93 }
94
95 rcu_reader tryretire(des* obj) {
96   rcu_reader g;
97   rcu_retire(obj);
98   return g;
99 }
100
101 TEST(RcuTest, CopyGuard) {
102   bool del = false;
103   auto foo = new des(&del);
104   {
105     auto res = tryretire(foo);
106     EXPECT_FALSE(del);
107   }
108   rcu_barrier();
109   EXPECT_TRUE(del);
110 }
111
112 TEST(RcuTest, Stress) {
113   std::vector<std::thread> threads;
114   constexpr uint32_t sz = 1000;
115   std::atomic<int*> ints[sz];
116   for (uint i = 0; i < sz; i++) {
117     ints[i].store(new int(0));
118   }
119   for (int th = 0; th < FLAGS_threads; th++) {
120     threads.push_back(std::thread([&]() {
121       for (int i = 0; i < FLAGS_iters / 100; i++) {
122         rcu_reader g;
123         int sum = 0;
124         int* ptrs[sz];
125         for (uint j = 0; j < sz; j++) {
126           ptrs[j] = ints[j].load(std::memory_order_acquire);
127         }
128         for (uint j = 0; j < sz; j++) {
129           sum += *ptrs[j];
130         }
131         EXPECT_EQ(sum, 0);
132       }
133     }));
134   }
135   std::atomic<bool> done{false};
136   std::thread updater([&]() {
137     while (!done.load()) {
138       auto newint = new int(0);
139       auto oldint = ints[folly::Random::rand32() % sz].exchange(newint);
140       rcu_retire<int>(oldint, [](int* obj) {
141         *obj = folly::Random::rand32();
142         delete obj;
143       });
144     }
145   });
146   for (auto& t : threads) {
147     t.join();
148   }
149   done = true;
150   updater.join();
151 }
152
153 TEST(RcuTest, Synchronize) {
154   std::vector<std::thread> threads;
155   for (int th = 0; th < FLAGS_threads; th++) {
156     threads.push_back(std::thread([&]() {
157       for (int i = 0; i < 10; i++) {
158         synchronize_rcu();
159       }
160     }));
161   }
162   for (auto& t : threads) {
163     t.join();
164   }
165 }
166
167 TEST(RcuTest, NewDomainTest) {
168   struct UniqueTag;
169   rcu_domain<UniqueTag> newdomain(nullptr);
170   synchronize_rcu();
171 }
172
173 TEST(RcuTest, MovableReader) {
174   {
175     rcu_reader g;
176     rcu_reader f(std::move(g));
177   }
178   synchronize_rcu();
179   {
180     rcu_reader g(std::defer_lock);
181     rcu_reader f;
182     g = std::move(f);
183   }
184   synchronize_rcu();
185 }
186
187 TEST(RcuTest, SynchronizeInCall) {
188   rcu_default_domain()->call([]() { synchronize_rcu(); });
189   synchronize_rcu();
190 }
191
192 TEST(RcuTest, MoveReaderBetweenThreads) {
193   rcu_reader g;
194   std::thread t([f = std::move(g)] {});
195   t.join();
196   synchronize_rcu();
197 }
198
199 TEST(RcuTest, ForkTest) {
200   folly::Baton<> b;
201   rcu_token epoch;
202   std::thread t([&]() {
203     epoch = rcu_default_domain()->lock_shared();
204     b.post();
205   });
206   t.detach();
207   b.wait();
208   auto pid = fork();
209   if (pid) {
210     // parent
211     rcu_default_domain()->unlock_shared(std::move(epoch));
212     synchronize_rcu();
213     int status;
214     auto pid2 = wait(&status);
215     EXPECT_EQ(status, 0);
216     EXPECT_EQ(pid, pid2);
217   } else {
218     // child
219     synchronize_rcu();
220     exit(0); // Do not print gtest results
221   }
222 }
223
224 TEST(RcuTest, CoreLocalList) {
225   struct TTag;
226   folly::detail::ThreadCachedLists<TTag> lists;
227   int numthreads = 32;
228   std::vector<std::thread> threads;
229   std::atomic<int> done{0};
230   for (int tr = 0; tr < numthreads; tr++) {
231     threads.push_back(std::thread([&]() {
232       for (int i = 0; i < FLAGS_iters; i++) {
233         auto node = new folly::detail::ThreadCachedListsBase::Node;
234         lists.push(node);
235       }
236       done++;
237     }));
238   }
239   while (done.load() != numthreads) {
240     folly::detail::ThreadCachedLists<TTag>::ListHead list{};
241     lists.collect(list);
242     list.forEach([](folly::detail::ThreadCachedLists<TTag>::Node* node) {
243       delete node;
244     });
245   }
246   for (auto& thread : threads) {
247     thread.join();
248   }
249 }
250
251 TEST(RcuTest, ThreadDeath) {
252   bool del = false;
253   std::thread t([&] {
254     auto foo = new des(&del);
255     rcu_retire(foo);
256   });
257   t.join();
258   synchronize_rcu();
259   EXPECT_TRUE(del);
260 }
261
262 TEST(RcuTest, RcuObjBase) {
263   bool retired = false;
264   struct base_test : rcu_obj_base<base_test> {
265     bool* ret_;
266     base_test(bool* ret) : ret_(ret) {}
267     ~base_test() {
268       (*ret_) = true;
269     }
270   };
271
272   auto foo = new base_test(&retired);
273   foo->retire();
274   synchronize_rcu();
275   EXPECT_TRUE(retired);
276 }