33 DEFINE_int64(num_ops, 1003,
"Number of ops or pairs of ops per rep");
97 template <
template <
typename>
class Atom = std::atomic>
104 : val_(
v), next_(n) {
117 return next_.load(std::memory_order_acquire);
126 template <
bool Mutable,
template <
typename>
class Atom = std::atomic>
133 : next_(n), val_(
v) {
138 this->acquire_link_safe();
140 this->acquire_ref_safe();
154 return next_.load(std::memory_order_acquire);
157 template <
typename S>
169 template <
typename T,
template <
typename>
class Atom = std::atomic>
171 Atom<T*> head_{
nullptr};
175 auto p = head_.load(std::memory_order_relaxed);
176 for (
int i = 0;
i < size - 1; ++
i) {
177 p =
new T(
i + 10000, p,
true);
179 p =
new T(size + 9999, p);
180 head_.store(p, std::memory_order_relaxed);
184 auto curr = head_.load(std::memory_order_relaxed);
186 auto next = curr->next();
198 auto curr = prev->load(std::memory_order_acquire);
206 auto next = curr->next();
207 if (prev->load(std::memory_order_acquire) != curr) {
210 if (curr->value() ==
val) {
213 prev = curr->ptr_next();
222 return hand_over_hand(val, &hptr[0], &hptr[1]);
228 auto next = curr->next();
229 if (curr->value() ==
val) {
239 return protect_all(val, hptr[0]);
244 template <
template <
typename>
class Atom = std::atomic>
246 Atom<NodeAuto<Atom>*> link_[2];
251 link_[0].store(l1, std::memory_order_relaxed);
252 link_[1].store(l2, std::memory_order_relaxed);
261 return link_[
i].load(std::memory_order_acquire);
264 template <
typename S>
267 for (
int i = 0;
i < 2; ++
i) {
279 template <
template <
typename>
class Atom = std::atomic>
303 obj->unlink_and_reclaim_unchecked();
312 hazptr_cleanup<Atom>();
316 template <
template <
typename>
class Atom = std::atomic>
323 auto p2 =
new Obj(*p1);
343 hazptr_cleanup<Atom>();
346 template <
template <
typename>
class Atom = std::atomic>
353 template <
template <
typename>
class Atom = std::atomic>
361 hazptr_cleanup<Atom>();
364 hazptr_cleanup<Atom>();
368 template <
template <
typename>
class Atom = std::atomic>
374 for (
int i = 0;
i < 100;
i++) {
375 auto bar =
new Thing;
383 hazptr_cleanup<Atom>();
386 template <
template <
typename>
class Atom = std::atomic>
393 :
next(n), domain(d),
val(v) {}
396 next->retire(*domain);
400 Thing* last{
nullptr};
401 for (
int i = 0;
i < 2000;
i++) {
402 last =
new Thing(
i, last, &domain);
404 last->retire(domain);
405 hazptr_cleanup<Atom>();
408 template <
template <
typename>
class Atom = std::atomic>
410 for (
int i = 0;
i < 100; ++
i) {
420 auto phptr1 = &hptr1;
432 hazptr_cleanup<Atom>();
435 template <
template <
typename>
class Atom = std::atomic>
437 for (
int i = 0;
i < 100; ++
i) {
452 hazptr_cleanup<Atom>();
455 template <
template <
typename>
class Atom = std::atomic>
457 #if FOLLY_HAZPTR_THR_LOCAL 484 template <
template <
typename>
class Atom = std::atomic>
486 for (
int i = 0;
i < 100; ++
i) {
496 hazptr_cleanup<Atom>();
499 template <
bool Mutable,
template <
typename>
class Atom = std::atomic>
504 for (
int i = 0;
i < num - 1; ++
i) {
511 for (
auto q = p->
next(); q; q = q->next()) {
516 for (
auto q = p; q; q = q->
next()) {
522 hazptr_cleanup<Atom>();
531 hazptr_cleanup<Atom>();
535 hazptr_cleanup<Atom>();
539 template <
bool Mutable,
template <
typename>
class Atom = std::atomic>
543 Atom<bool> ready(
false);
544 Atom<bool> done(
false);
545 Atom<int> setHazptrs(0);
548 int num = FLAGS_num_ops;
549 int nthr = FLAGS_num_threads;
551 std::vector<std::thread> thr(nthr);
552 for (
int i = 0;
i <
nthr; ++
i) {
554 while (!ready.load()) {
562 for (
auto q = p; q; q = q->next()) {
568 while (!done.load()) {
575 for (
int i = 0;
i < num - 1; ++
i) {
582 while (setHazptrs.load() <
nthr) {
587 head().store(
nullptr);
591 for (
auto q = p; q; q = q->
next()) {
598 for (
auto&
t : thr) {
602 hazptr_cleanup<Atom>();
606 template <
template <
typename>
class Atom = std::atomic>
614 c->acquire_link_safe();
615 b->acquire_link_safe();
620 a->acquire_link_safe();
628 hazptr_cleanup<Atom>();
653 hazptr_cleanup<Atom>();
680 hazptr_cleanup<Atom>();
696 template <
template <
typename>
class Atom = std::atomic>
699 hazptr_retire<Atom>(
foo);
701 hazptr_retire<Atom>(
foo2, [](
int* obj) {
delete obj; });
703 bool retired =
false;
708 explicit delret(
bool* retire) : retired_(retire) {}
713 auto foo3 =
new delret(&retired);
719 template <
template <
typename>
class Atom = std::atomic>
721 int threadOps = 1007;
724 Atom<int> threadsDone{0};
725 Atom<bool> mainDone{
false};
726 std::vector<std::thread>
threads(FLAGS_num_threads);
727 for (
int tid = 0; tid < FLAGS_num_threads; ++tid) {
729 for (
int j = tid; j < threadOps; j += FLAGS_num_threads) {
733 threadsDone.fetch_add(1);
734 while (!mainDone.load()) {
740 for (
int i = 0;
i < mainOps; ++
i) {
745 while (threadsDone.load() < FLAGS_num_threads) {
749 hazptr_cleanup<Atom>();
751 mainDone.store(
true);
752 for (
auto&
t : threads) {
768 hazptr_cleanup<Atom>();
784 hazptr_cleanup<Atom>();
789 template <
template <
typename>
class Atom = std::atomic>
794 y->acquire_link_safe();
808 hazptr_cleanup<Atom>();
812 template <
template <
typename>
class Atom = std::atomic>
814 for (
int i = 0;
i < FLAGS_num_reps; ++
i) {
817 std::vector<std::thread>
threads(FLAGS_num_threads);
818 for (
int tid = 0; tid < FLAGS_num_threads; ++tid) {
821 for (
int j = tid; j < FLAGS_num_ops; j += FLAGS_num_threads) {
827 sum.fetch_add(local);
830 for (
auto&
t : threads) {
833 hazptr_cleanup<Atom>();
834 int expected = FLAGS_num_ops * (FLAGS_num_ops - 1) / 2;
839 template <
template <
typename>
class Atom = std::atomic>
842 for (
int i = 0;
i < FLAGS_num_reps; ++
i) {
844 std::vector<std::thread>
threads(FLAGS_num_threads);
845 for (
int tid = 0; tid < FLAGS_num_threads; ++tid) {
847 for (
int j = tid; j < FLAGS_num_ops; j += FLAGS_num_threads) {
852 for (
int j = 0; j < 10; ++j) {
855 for (
int j = 0; j < 10; ++j) {
858 for (
auto&
t : threads) {
861 hazptr_cleanup<Atom>();
865 template <
template <
typename>
class Atom = std::atomic>
870 auto ret = s.
cas(u, v);
884 hazptr_cleanup<Atom>();
896 TEST(HazptrTest, basic_objects) {
902 basic_objects_test<DeterministicAtomic>();
905 TEST(HazptrTest, copy_and_move) {
911 copy_and_move_test<DeterministicAtomic>();
914 TEST(HazptrTest, basic_holders) {
920 basic_holders_test<DeterministicAtomic>();
923 TEST(HazptrTest, basic_protection) {
929 basic_protection_test<DeterministicAtomic>();
938 virtual_test<DeterministicAtomic>();
941 TEST(HazptrTest, destruction) {
953 destruction_test<DeterministicAtomic>(myDomain0);
955 destruction_test<DeterministicAtomic>(
956 default_hazptr_domain<DeterministicAtomic>());
965 move_test<DeterministicAtomic>();
974 array_test<DeterministicAtomic>();
977 TEST(HazptrTest, array_dtor_full_tc) {
983 array_dtor_full_tc_test<DeterministicAtomic>();
992 local_test<DeterministicAtomic>();
995 TEST(HazptrTest, linked_mutable) {
1001 linked_test<true, DeterministicAtomic>();
1004 TEST(HazptrTest, linked_immutable) {
1005 linked_test<false>();
1010 linked_test<false, DeterministicAtomic>();
1013 TEST(HazptrTest, mt_linked_mutable) {
1014 mt_linked_test<true>();
1019 mt_linked_test<true, DeterministicAtomic>();
1022 TEST(HazptrTest, mt_linked_immutable) {
1023 mt_linked_test<false>();
1028 mt_linked_test<false, DeterministicAtomic>();
1037 auto_retire_test<DeterministicAtomic>();
1040 TEST(HazptrTest, free_function_retire) {
1046 free_function_retire_test<DeterministicAtomic>();
1055 cleanup_test<DeterministicAtomic>();
1064 priv_dtor_test<DeterministicAtomic>();
1073 lifo_test<DeterministicAtomic>();
1082 swmr_test<DeterministicAtomic>();
1091 wide_cas_test<DeterministicAtomic>();
1094 TEST(HazptrTest, reclamation_without_calling_cleanup) {
1098 std::vector<std::thread> thr(nthr);
1099 for (
int tid = 0; tid <
nthr; ++tid) {
1100 thr[tid] = std::thread([&, tid] {
1101 for (
int i = tid;
i < objs;
i +=
nthr) {
1107 for (
auto&
t : thr) {
1115 template <
typename InitFunc,
typename Func,
typename EndFunc>
1118 const InitFunc&
init,
1120 const EndFunc& endFn) {
1121 std::atomic<bool>
start{
false};
1124 std::vector<std::thread>
threads(nthreads);
1125 for (
int tid = 0; tid <
nthreads; ++tid) {
1126 threads[tid] = std::thread([&, tid] {
1135 for (
auto&
t : threads) {
1142 return std::chrono::duration_cast<std::chrono::nanoseconds>(tend - tbegin)
1146 template <
typename RepFunc>
1154 for (
int r = 0; r < reps; ++r) {
1165 std::cout <<
" " << std::setw(4) << max / ops <<
unit;
1166 std::cout <<
" " << std::setw(4) << avg / ops <<
unit;
1167 std::cout <<
" " << std::setw(4) << res / ops <<
unit;
1168 std::cout << std::endl;
1181 auto fn = [&](
int tid) {
1196 auto fn = [&](
int tid) {
1211 auto fn = [&](
int tid) {
1226 auto fn = [&](
int tid) {
1242 bool provided =
false) {
1246 auto fn = [&](
int tid) {
1268 bool provided =
false) {
1272 auto fn = [&](
int tid) {
1293 auto fn = [&](int) {
1295 for (
int i = 0;
i < 1000;
i++) {
1309 for (
int i :
nthr) {
1310 std::cout <<
"================================ " << std::setw(2) <<
i 1312 <<
"================================" << std::endl;
1313 std::cout <<
"10x construct/destruct hazptr_holder ";
1315 std::cout <<
"10x construct/destruct hazptr_array<1> ";
1316 array_bench<1>(
"",
i);
1317 std::cout <<
"10x construct/destruct hazptr_array<2> ";
1318 array_bench<2>(
"",
i);
1319 std::cout <<
"10x construct/destruct hazptr_array<3> ";
1320 array_bench<3>(
"",
i);
1321 std::cout <<
"10x construct/destruct hazptr_local<1> ";
1322 local_bench<1>(
"",
i);
1323 std::cout <<
"10x construct/destruct hazptr_local<2> ";
1324 local_bench<2>(
"",
i);
1325 std::cout <<
"10x construct/destruct hazptr_local<3> ";
1326 local_bench<3>(
"",
i);
1327 std::cout <<
"allocate/retire/reclaim object ";
1329 for (
int j :
sizes) {
1330 std::cout << j <<
"-item list hand-over-hand - own hazptrs ";
1332 std::cout << j <<
"-item list hand-over-hand ";
1334 std::cout << j <<
"-item list protect all - own hazptr ";
1336 std::cout << j <<
"-item list protect all ";
1339 std::cout <<
"hazptr_cleanup ";
void push_links(bool m, S &s)
#define ASSERT_GT(val1, val2)
std::atomic< int > dtors_
uint64_t bench(std::string name, int ops, const RepFunc &repFn)
DEFINE_int64(num_reps, 10,"Number of test reps")
void array_dtor_full_tc_test()
bool hand_over_hand(int val)
DEFINE_bool(bench, false,"run benchmark")
Atom< Node< Atom > * > * ptr_next() noexcept
void destruction_test(hazptr_domain< Atom > &domain)
void hazptr_retire(T *obj, D reclaim={})
std::atomic< int64_t > sum(0)
uint64_t holder_bench(std::string name, int nthreads)
bool hand_over_hand(int val, hazptr_holder< Atom > *hptr_prev, hazptr_holder< Atom > *hptr_curr)
#define ASSERT_EQ(val1, val2)
bool protect_all(int val, hazptr_holder< Atom > &hptr)
FOLLY_ALWAYS_INLINE bool try_protect(T *&ptr, const Atom< T * > &src) noexcept
FOLLY_ALWAYS_INLINE T * get_protected(const Atom< T * > &src) noexcept
uint64_t cleanup_bench(std::string name, int nthreads)
int value() const noexcept
void basic_protection_test()
int retires() const noexcept
uint64_t run_once(int nthreads, const InitFunc &init, const Func &fn, const EndFunc &endFn)
hazptr_domain< Atom > & default_hazptr_domain()
std::chrono::steady_clock::time_point now()
constexpr detail::Map< Move > move
void inc_ctors() noexcept
void retire(D deleter={}, hazptr_domain< Atom > &domain=default_hazptr_domain< Atom >())
DeterministicAtomicImpl< T, DeterministicSchedule > DeterministicAtomic
uint64_t obj_bench(std::string name, int nthreads)
FOLLY_NOINLINE void foo2()
int value() const noexcept
void inc_dtors() noexcept
static std::thread thread(Func &&func, Args &&...args)
NodeRC< Mutable, Atom > * next() const noexcept
NodeAuto(NodeAuto *l1=nullptr, NodeAuto *l2=nullptr) noexcept
requires E e noexcept(noexcept(s.error(std::move(e))))
static FOLLY_ALWAYS_INLINE hazptr_domain< Atom > & get()
int ctors() const noexcept
void retire(T *obj, D reclaim={})
std::vector< std::thread::id > threads
std::atomic< int > ctors_
constexpr auto size(C const &c) -> decltype(c.size())
static std::function< size_t(size_t)> uniform(uint64_t seed)
Node< Atom > * next() const noexcept
void free_function_retire_test()
bool contains(const T &val) const
void inc_retires() noexcept
TEST(HazptrTest, basic_objects)
static map< string, int > m
constexpr int hazptr_domain_rcount_threshold()
void hazptr_cleanup(hazptr_domain< Atom > &domain=default_hazptr_domain< Atom >()) noexcept
Atom< Node< Atom > * > next_
DEFINE_int32(num_threads, 6,"Number of threads")
NodeRC(int v=0, NodeRC *n=nullptr, bool acq=false) noexcept
std::atomic< int > retires_
**Optimized Holders **The template hazptr_array< M > provides most of the functionality *of M hazptr_holder s but with faster construction destruction *for M
int dtors() const noexcept
uint64_t list_protect_all_bench(std::string name, int nthreads, int size, bool provided=false)
uint64_t local_bench(std::string name, int nthreads)
void basic_objects_test()
Node(int v=0, Node *n=nullptr, bool=false) noexcept
TEST_F(HazptrPreInitTest, dsched_basic_objects)
Atom< NodeRC< Mutable, Atom > * > next_
FOLLY_ALWAYS_INLINE void reset(const T *ptr) noexcept
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
#define ASSERT_FALSE(condition)
static void join(std::thread &child)
void acquire_link_safe() noexcept
NodeAuto< Atom > * link(size_t i)
uint64_t array_bench(std::string name, int nthreads)
void basic_holders_test()
void copy_and_move_test()
#define ASSERT_TRUE(condition)
bool protect_all(int val)
uint64_t list_hoh_bench(std::string name, int nthreads, int size, bool provided=false)
void push_links(bool m, S &s)