2 * Copyright 2017-present Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <folly/synchronization/Rcu.h>
21 #include <glog/logging.h>
23 #include <folly/Benchmark.h>
24 #include <folly/Random.h>
25 #include <folly/portability/GFlags.h>
26 #include <folly/portability/GTest.h>
28 using namespace folly;
30 DEFINE_int64(iters, 100000, "Number of iterations");
31 DEFINE_uint64(threads, 32, "Number of threads");
33 TEST(RcuTest, Basic) {
34 auto foo = new int(2);
42 des(bool* d) : d_(d) {}
48 TEST(RcuTest, Guard) {
50 auto foo = new des(&del);
59 auto start = std::chrono::steady_clock::now();
63 auto diff = std::chrono::steady_clock::now() - start;
65 "Total time %li ns \n",
66 std::chrono::duration_cast<std::chrono::nanoseconds>(diff).count() /
70 TEST(RcuTest, ResetPerf) {
72 auto start = std::chrono::steady_clock::now();
74 rcu_retire<int>(nullptr, [](int*) {});
76 auto diff = std::chrono::steady_clock::now() - start;
78 "Total time %li ns \n",
79 std::chrono::duration_cast<std::chrono::nanoseconds>(diff).count() /
83 TEST(RcuTest, SlowReader) {
88 t = std::thread([&]() { synchronize_rcu(); });
89 usleep(100); // Wait for synchronize to start
94 rcu_reader tryretire(des* obj) {
100 TEST(RcuTest, CopyGuard) {
102 auto foo = new des(&del);
104 auto res = tryretire(foo);
111 TEST(RcuTest, Stress) {
112 std::vector<std::thread> threads;
113 constexpr uint32_t sz = 1000;
114 std::atomic<int*> ints[sz];
115 for (uint i = 0; i < sz; i++) {
116 ints[i].store(new int(0));
118 for (unsigned th = 0; th < FLAGS_threads; th++) {
119 threads.push_back(std::thread([&]() {
120 for (int i = 0; i < FLAGS_iters / 100; i++) {
124 for (uint j = 0; j < sz; j++) {
125 ptrs[j] = ints[j].load(std::memory_order_acquire);
127 for (uint j = 0; j < sz; j++) {
134 std::atomic<bool> done{false};
135 std::thread updater([&]() {
136 while (!done.load()) {
137 auto newint = new int(0);
138 auto oldint = ints[folly::Random::rand32() % sz].exchange(newint);
139 rcu_retire<int>(oldint, [](int* obj) {
140 *obj = folly::Random::rand32();
145 for (auto& t : threads) {
152 for (uint i = 0; i < sz; i++) {
153 delete ints[i].exchange(nullptr);
157 TEST(RcuTest, Synchronize) {
158 std::vector<std::thread> threads;
159 for (unsigned th = 0; th < FLAGS_threads; th++) {
160 threads.push_back(std::thread([&]() {
161 for (int i = 0; i < 10; i++) {
166 for (auto& t : threads) {
171 TEST(RcuTest, NewDomainTest) {
173 rcu_domain<UniqueTag> newdomain(nullptr);
177 TEST(RcuTest, MovableReader) {
180 rcu_reader f(std::move(g));
184 rcu_reader g(std::defer_lock);
191 TEST(RcuTest, SynchronizeInCall) {
192 rcu_default_domain()->call([]() { synchronize_rcu(); });
196 TEST(RcuTest, MoveReaderBetweenThreads) {
198 std::thread t([f = std::move(g)] {});
203 TEST(RcuTest, ForkTest) {
205 std::thread t([&]() {
206 epoch = rcu_default_domain()->lock_shared();
212 rcu_default_domain()->unlock_shared(std::move(epoch));
215 auto pid2 = wait(&status);
216 EXPECT_EQ(status, 0);
217 EXPECT_EQ(pid, pid2);
221 exit(0); // Do not print gtest results
225 TEST(RcuTest, ThreadLocalList) {
227 folly::detail::ThreadCachedLists<TTag> lists;
228 std::vector<std::thread> threads{FLAGS_threads};
229 std::atomic<unsigned long> done{FLAGS_threads};
230 for (auto& tr : threads) {
231 tr = std::thread([&]() {
232 for (int i = 0; i < FLAGS_iters; i++) {
233 auto node = new folly::detail::ThreadCachedListsBase::Node;
239 while (done.load() > 0) {
240 folly::detail::ThreadCachedLists<TTag>::ListHead list{};
242 list.forEach([](folly::detail::ThreadCachedLists<TTag>::Node* node) {
246 for (auto& thread : threads) {
249 // Run cleanup pass one more time to make ASAN happy
250 folly::detail::ThreadCachedLists<TTag>::ListHead list{};
253 [](folly::detail::ThreadCachedLists<TTag>::Node* node) { delete node; });
256 TEST(RcuTest, ThreadDeath) {
259 auto foo = new des(&del);
267 TEST(RcuTest, RcuObjBase) {
268 bool retired = false;
269 struct base_test : rcu_obj_base<base_test> {
271 base_test(bool* ret) : ret_(ret) {}
277 auto foo = new base_test(&retired);
280 EXPECT_TRUE(retired);