1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/utils/common.h"
8 #include "td/utils/HazardPointers.h"
9 #include "td/utils/logging.h"
10 #include "td/utils/port/thread.h"
11 #include "td/utils/Random.h"
12 #include "td/utils/Slice.h"
13 #include "td/utils/tests.h"
14
15 #include <atomic>
16
17 #if !TD_THREAD_UNSUPPORTED
TEST(HazardPointers,stress)18 TEST(HazardPointers, stress) {
19 struct Node {
20 std::atomic<std::string *> name_{nullptr};
21 char pad[64];
22 };
23 int threads_n = 10;
24 std::vector<Node> nodes(threads_n);
25 td::HazardPointers<std::string> hazard_pointers(threads_n);
26 std::vector<td::thread> threads(threads_n);
27 int thread_id = 0;
28 for (auto &thread : threads) {
29 thread = td::thread([&, thread_id] {
30 std::remove_reference_t<decltype(hazard_pointers)>::Holder holder(hazard_pointers, thread_id, 0);
31 for (int i = 0; i < 1000000; i++) {
32 auto &node = nodes[td::Random::fast(0, threads_n - 1)];
33 auto *str = holder.protect(node.name_);
34 if (str) {
35 CHECK(*str == td::Slice("one") || *str == td::Slice("twotwo"));
36 }
37 holder.clear();
38 if (td::Random::fast(0, 5) == 0) {
39 auto *new_str = new td::string(td::Random::fast_bool() ? "one" : "twotwo");
40 if (node.name_.compare_exchange_strong(str, new_str, std::memory_order_acq_rel)) {
41 hazard_pointers.retire(thread_id, str);
42 } else {
43 delete new_str;
44 }
45 }
46 }
47 });
48 thread_id++;
49 }
50 for (auto &thread : threads) {
51 thread.join();
52 }
53 LOG(INFO) << "Undeleted pointers: " << hazard_pointers.to_delete_size_unsafe();
54 CHECK(static_cast<int>(hazard_pointers.to_delete_size_unsafe()) <= threads_n * threads_n);
55 for (int i = 0; i < threads_n; i++) {
56 hazard_pointers.retire(i);
57 }
58 CHECK(hazard_pointers.to_delete_size_unsafe() == 0);
59 }
60 #endif
61