proxygen
HazptrTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2018-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 
22 
26 
27 #include <atomic>
28 #include <thread>
29 
30 DEFINE_bool(bench, false, "run benchmark");
31 DEFINE_int64(num_reps, 10, "Number of test reps");
32 DEFINE_int32(num_threads, 6, "Number of threads");
33 DEFINE_int64(num_ops, 1003, "Number of ops or pairs of ops per rep");
34 
44 using folly::hazptr_root;
45 using folly::hazptr_tc;
51 
53 
54 // Structures
55 
57 class Count {
58  std::atomic<int> ctors_{0};
59  std::atomic<int> dtors_{0};
60  std::atomic<int> retires_{0};
61 
62  public:
63  void clear() noexcept {
64  ctors_.store(0);
65  dtors_.store(0);
66  retires_.store(0);
67  }
68 
70  return ctors_.load();
71  }
72 
74  return dtors_.load();
75  }
76 
78  return retires_.load();
79  }
80 
82  ctors_.fetch_add(1);
83  }
84 
86  dtors_.fetch_add(1);
87  }
88 
90  retires_.fetch_add(1);
91  }
92 }; // Count
93 
94 static Count c_;
95 
97 template <template <typename> class Atom = std::atomic>
98 class Node : public hazptr_obj_base<Node<Atom>, Atom> {
99  int val_;
100  Atom<Node<Atom>*> next_;
101 
102  public:
103  explicit Node(int v = 0, Node* n = nullptr, bool = false) noexcept
104  : val_(v), next_(n) {
105  c_.inc_ctors();
106  }
107 
108  ~Node() {
109  c_.inc_dtors();
110  }
111 
113  return val_;
114  }
115 
117  return next_.load(std::memory_order_acquire);
118  }
119 
120  Atom<Node<Atom>*>* ptr_next() noexcept {
121  return &next_;
122  }
123 }; // Node
124 
126 template <bool Mutable, template <typename> class Atom = std::atomic>
127 class NodeRC : public hazptr_obj_base_linked<NodeRC<Mutable, Atom>, Atom> {
128  Atom<NodeRC<Mutable, Atom>*> next_;
129  int val_;
130 
131  public:
132  explicit NodeRC(int v = 0, NodeRC* n = nullptr, bool acq = false) noexcept
133  : next_(n), val_(v) {
134  this->set_deleter();
135  c_.inc_ctors();
136  if (acq) {
137  if (Mutable) {
138  this->acquire_link_safe();
139  } else {
140  this->acquire_ref_safe();
141  }
142  }
143  }
144 
146  c_.inc_dtors();
147  }
148 
150  return val_;
151  }
152 
154  return next_.load(std::memory_order_acquire);
155  }
156 
157  template <typename S>
158  void push_links(bool m, S& s) {
159  if (Mutable == m) {
160  auto p = next();
161  if (p) {
162  s.push(p);
163  }
164  }
165  }
166 }; // NodeRC
167 
169 template <typename T, template <typename> class Atom = std::atomic>
170 struct List {
171  Atom<T*> head_{nullptr};
172 
173  public:
174  explicit List(int size) {
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);
178  }
179  p = new T(size + 9999, p);
180  head_.store(p, std::memory_order_relaxed);
181  }
182 
183  ~List() {
184  auto curr = head_.load(std::memory_order_relaxed);
185  while (curr) {
186  auto next = curr->next();
187  curr->retire();
188  curr = next;
189  }
190  }
191 
193  int val,
194  hazptr_holder<Atom>* hptr_prev,
195  hazptr_holder<Atom>* hptr_curr) {
196  while (true) {
197  auto prev = &head_;
198  auto curr = prev->load(std::memory_order_acquire);
199  while (true) {
200  if (!curr) {
201  return false;
202  }
203  if (!hptr_curr->try_protect(curr, *prev)) {
204  break;
205  }
206  auto next = curr->next();
207  if (prev->load(std::memory_order_acquire) != curr) {
208  break;
209  }
210  if (curr->value() == val) {
211  return true;
212  }
213  prev = curr->ptr_next();
214  curr = next;
215  std::swap(hptr_curr, hptr_prev);
216  }
217  }
218  }
219 
220  bool hand_over_hand(int val) {
222  return hand_over_hand(val, &hptr[0], &hptr[1]);
223  }
224 
226  auto curr = hptr.get_protected(head_);
227  while (curr) {
228  auto next = curr->next();
229  if (curr->value() == val) {
230  return true;
231  }
232  curr = next;
233  }
234  return false;
235  }
236 
237  bool protect_all(int val) {
239  return protect_all(val, hptr[0]);
240  }
241 }; // NodeRC
242 
244 template <template <typename> class Atom = std::atomic>
245 class NodeAuto : public hazptr_obj_base_linked<NodeAuto<Atom>, Atom> {
246  Atom<NodeAuto<Atom>*> link_[2];
247 
248  public:
249  explicit NodeAuto(NodeAuto* l1 = nullptr, NodeAuto* l2 = nullptr) noexcept {
250  this->set_deleter();
251  link_[0].store(l1, std::memory_order_relaxed);
252  link_[1].store(l2, std::memory_order_relaxed);
253  c_.inc_ctors();
254  }
255 
257  c_.inc_dtors();
258  }
259 
260  NodeAuto<Atom>* link(size_t i) {
261  return link_[i].load(std::memory_order_acquire);
262  }
263 
264  template <typename S>
265  void push_links(bool m, S& s) {
266  if (m == false) { // Immutable
267  for (int i = 0; i < 2; ++i) {
268  auto p = link(i);
269  if (p) {
270  s.push(p);
271  }
272  }
273  }
274  }
275 }; // NodeAuto
276 
277 // Test Functions
278 
279 template <template <typename> class Atom = std::atomic>
281  c_.clear();
282  int num = 0;
283  {
284  ++num;
285  auto obj = new Node<Atom>;
286  obj->retire();
287  }
288  {
289  ++num;
290  auto obj = new NodeRC<false, Atom>(0, nullptr);
291  obj->retire();
292  }
293  {
294  ++num;
295  auto obj = new NodeRC<false, Atom>(0, nullptr);
296  obj->acquire_link_safe();
297  obj->unlink();
298  }
299  {
300  ++num;
301  auto obj = new NodeRC<false, Atom>(0, nullptr);
302  obj->acquire_link_safe();
303  obj->unlink_and_reclaim_unchecked();
304  }
305  {
306  ++num;
307  auto obj = new NodeRC<false, Atom>(0, nullptr);
308  obj->acquire_link_safe();
310  }
311  ASSERT_EQ(c_.ctors(), num);
312  hazptr_cleanup<Atom>();
313  ASSERT_EQ(c_.dtors(), num);
314 }
315 
316 template <template <typename> class Atom = std::atomic>
318  struct Obj : hazptr_obj_base<Obj, Atom> {
319  int a;
320  };
321 
322  auto p1 = new Obj();
323  auto p2 = new Obj(*p1);
324  p1->retire();
325  p2->retire();
326 
327  p1 = new Obj();
328  p2 = new Obj(std::move(*p1));
329  p1->retire();
330  p2->retire();
331 
332  p1 = new Obj();
333  p2 = new Obj();
334  *p2 = *p1;
335  p1->retire();
336  p2->retire();
337 
338  p1 = new Obj();
339  p2 = new Obj();
340  *p2 = std::move(*p1);
341  p1->retire();
342  p2->retire();
343  hazptr_cleanup<Atom>();
344 }
345 
346 template <template <typename> class Atom = std::atomic>
351 }
352 
353 template <template <typename> class Atom = std::atomic>
355  c_.clear();
356  auto obj = new Node<Atom>;
358  h.reset(obj);
359  obj->retire();
360  ASSERT_EQ(c_.ctors(), 1);
361  hazptr_cleanup<Atom>();
362  ASSERT_EQ(c_.dtors(), 0);
363  h.reset();
364  hazptr_cleanup<Atom>();
365  ASSERT_EQ(c_.dtors(), 1);
366 }
367 
368 template <template <typename> class Atom = std::atomic>
369 void virtual_test() {
370  struct Thing : public hazptr_obj_base<Thing, Atom> {
371  virtual ~Thing() {}
372  int a;
373  };
374  for (int i = 0; i < 100; i++) {
375  auto bar = new Thing;
376  bar->a = i;
377 
378  hazptr_holder<Atom> hptr;
379  hptr.reset(bar);
380  bar->retire();
381  ASSERT_EQ(bar->a, i);
382  }
383  hazptr_cleanup<Atom>();
384 }
385 
386 template <template <typename> class Atom = std::atomic>
388  struct Thing : public hazptr_obj_base<Thing, Atom> {
389  Thing* next;
390  hazptr_domain<Atom>* domain;
391  int val;
392  Thing(int v, Thing* n, hazptr_domain<Atom>* d)
393  : next(n), domain(d), val(v) {}
394  ~Thing() {
395  if (next) {
396  next->retire(*domain);
397  }
398  }
399  };
400  Thing* last{nullptr};
401  for (int i = 0; i < 2000; i++) {
402  last = new Thing(i, last, &domain);
403  }
404  last->retire(domain);
405  hazptr_cleanup<Atom>();
406 }
407 
408 template <template <typename> class Atom = std::atomic>
409 void move_test() {
410  for (int i = 0; i < 100; ++i) {
411  auto x = new Node<Atom>(i);
412  hazptr_holder<Atom> hptr0;
413  // Protect object
414  hptr0.reset(x);
415  // Retire object
416  x->retire();
417  // Move constructor - still protected
418  hazptr_holder<Atom> hptr1(std::move(hptr0));
419  // Self move is no-op - still protected
420  auto phptr1 = &hptr1;
421  ASSERT_EQ(phptr1, &hptr1);
422  hptr1 = std::move(*phptr1);
423  // Empty constructor
424  hazptr_holder<Atom> hptr2(nullptr);
425  // Move assignment - still protected
426  hptr2 = std::move(hptr1);
427  // Access object
428  ASSERT_EQ(x->value(), i);
429  // Unprotect object - hptr2 is nonempty
430  hptr2.reset();
431  }
432  hazptr_cleanup<Atom>();
433 }
434 
435 template <template <typename> class Atom = std::atomic>
436 void array_test() {
437  for (int i = 0; i < 100; ++i) {
438  auto x = new Node<Atom>(i);
440  // Protect object
441  hptr[2].reset(x);
442  // Empty array
443  hazptr_array<3, Atom> h(nullptr);
444  // Move assignment
445  h = std::move(hptr);
446  // Retire object
447  x->retire();
448  ASSERT_EQ(x->value(), i);
449  // Unprotect object - hptr2 is nonempty
450  h[2].reset();
451  }
452  hazptr_cleanup<Atom>();
453 }
454 
455 template <template <typename> class Atom = std::atomic>
457 #if FOLLY_HAZPTR_THR_LOCAL
459 #else
460  const uint8_t M = 3;
461 #endif
462  {
463  // Fill the thread cache
465  }
466  {
467  // Empty array x
468  hazptr_array<M, Atom> x(nullptr);
469  {
470  // y ctor gets elements from the thread cache filled by w dtor.
472  // z ctor gets elements from the default domain.
474  // Elements of y are moved to x.
475  x = std::move(y);
476  // z dtor fills the thread cache.
477  }
478  // x dtor finds the thread cache full. It has to call
479  // ~hazptr_holder() for each of its elements, which were
480  // previously taken from the thread cache by y ctor.
481  }
482 }
483 
484 template <template <typename> class Atom = std::atomic>
485 void local_test() {
486  for (int i = 0; i < 100; ++i) {
487  auto x = new Node<Atom>(i);
489  // Protect object
490  hptr[2].reset(x);
491  // Retire object
492  x->retire();
493  // Unprotect object - hptr2 is nonempty
494  hptr[2].reset();
495  }
496  hazptr_cleanup<Atom>();
497 }
498 
499 template <bool Mutable, template <typename> class Atom = std::atomic>
500 void linked_test() {
501  c_.clear();
502  NodeRC<Mutable, Atom>* p = nullptr;
503  int num = 193;
504  for (int i = 0; i < num - 1; ++i) {
505  p = new NodeRC<Mutable, Atom>(i, p, true);
506  }
507  p = new NodeRC<Mutable, Atom>(num - 1, p, Mutable);
508  hazptr_holder<Atom> hptr;
509  hptr.reset(p);
510  if (!Mutable) {
511  for (auto q = p->next(); q; q = q->next()) {
512  q->retire();
513  }
514  }
515  int v = num;
516  for (auto q = p; q; q = q->next()) {
517  ASSERT_GT(v, 0);
518  --v;
519  ASSERT_EQ(q->value(), v);
520  }
521 
522  hazptr_cleanup<Atom>();
523  ASSERT_EQ(c_.ctors(), num);
524  ASSERT_EQ(c_.dtors(), 0);
525 
526  if (Mutable) {
528  } else {
529  p->retire();
530  }
531  hazptr_cleanup<Atom>();
532  ASSERT_EQ(c_.dtors(), 0);
533 
534  hptr.reset();
535  hazptr_cleanup<Atom>();
536  ASSERT_EQ(c_.dtors(), num);
537 }
538 
539 template <bool Mutable, template <typename> class Atom = std::atomic>
541  c_.clear();
542 
543  Atom<bool> ready(false);
544  Atom<bool> done(false);
545  Atom<int> setHazptrs(0);
547 
548  int num = FLAGS_num_ops;
549  int nthr = FLAGS_num_threads;
550  ASSERT_GT(FLAGS_num_threads, 0);
551  std::vector<std::thread> thr(nthr);
552  for (int i = 0; i < nthr; ++i) {
553  thr[i] = DSched::thread([&] {
554  while (!ready.load()) {
555  /* spin */
556  }
557  hazptr_holder<Atom> hptr;
558  auto p = hptr.get_protected(head());
559  ++setHazptrs;
560  /* Concurrent with removal */
561  int v = num;
562  for (auto q = p; q; q = q->next()) {
563  ASSERT_GT(v, 0);
564  --v;
565  ASSERT_EQ(q->value(), v);
566  }
567  ASSERT_EQ(v, 0);
568  while (!done.load()) {
569  /* spin */
570  }
571  });
572  }
573 
574  NodeRC<Mutable, Atom>* p = nullptr;
575  for (int i = 0; i < num - 1; ++i) {
576  p = new NodeRC<Mutable, Atom>(i, p, true);
577  }
578  p = new NodeRC<Mutable, Atom>(num - 1, p, Mutable);
579  ASSERT_EQ(c_.ctors(), num);
580  head().store(p);
581  ready.store(true);
582  while (setHazptrs.load() < nthr) {
583  /* spin */
584  }
585 
586  /* this is concurrent with traversal by readers */
587  head().store(nullptr);
588  if (Mutable) {
589  p->unlink();
590  } else {
591  for (auto q = p; q; q = q->next()) {
592  q->retire();
593  }
594  }
595  ASSERT_EQ(c_.dtors(), 0);
596  done.store(true);
597 
598  for (auto& t : thr) {
599  DSched::join(t);
600  }
601 
602  hazptr_cleanup<Atom>();
603  ASSERT_EQ(c_.dtors(), num);
604 }
605 
606 template <template <typename> class Atom = std::atomic>
608  c_.clear();
609  auto d = new NodeAuto<Atom>;
610  d->acquire_link_safe();
611  auto c = new NodeAuto<Atom>(d);
612  d->acquire_link_safe();
613  auto b = new NodeAuto<Atom>(d);
614  c->acquire_link_safe();
615  b->acquire_link_safe();
616  auto a = new NodeAuto<Atom>(b, c);
618  {
620  a->acquire_link_safe();
621  root().store(a);
622  ASSERT_EQ(c_.ctors(), 4);
623  /* So far the links and link counts are:
624  root-->a a-->b a-->c b-->d c-->d
625  a(1,0) b(1,0) c(1,0) d(2,0)
626  */
627  h.reset(c); /* h protects c */
628  hazptr_cleanup<Atom>();
629  ASSERT_EQ(c_.dtors(), 0);
630  /* Nothing is retired or reclaimed yet */
631  }
632  /* root dtor calls a->unlink, which calls a->release_link, which
633  changes a's link counts from (1,0) to (0,0), which triggers calls
634  to c->downgrade_link, b->downgrade_link, and a->retire.
635 
636  c->downgrade_link changes c's link counts from (1,0) to (0,1),
637  which triggers calls to d->downgrade_link and c->retire.
638 
639  d->downgrade_link changes d's link counts from (2,0) to (1,1).
640 
641  b->downgrade_link changes b's link counts from (1,0) to (0,1),
642  which triggers calls to d->downgrade_link and b->retire.
643 
644  d->downgrade_link changes d's link counts from (1,1) to (0,2),
645  which triggers a call to d->retire.
646 
647  So far (assuming retire-s did not trigger bulk_reclaim):
648  a-->b a-->c b-->d c-->d
649  a(0,0) b(0,1) c(0,1) d(0,2)
650  Retired: a b c d
651  Protected: c
652  */
653  hazptr_cleanup<Atom>();
654  /* hazptr_cleanup calls bulk_reclaim which finds a, b, and d
655  unprotected, which triggers calls to a->release_ref,
656  b->release_ref, and d->release_ref (not necessarily in that
657  order).
658 
659  a->release_ref finds a's link counts to be (0,0), which triggers
660  calls to c->release_ref, b->release_ref and delete a.
661 
662  The call to c->release_ref changes its link counts from (0,1) to
663  (0,0).
664 
665  The first call to b->release_ref changes b's link counts to
666  (0,0). The second call finds the link counts to be (0,0), which
667  triggers a call to d->release_ref and delete b.
668 
669  The first call to d->release_ref changes its link counts to
670  (0,1), and the second call changes them to (0,0);
671 
672  So far:
673  c-->d
674  a(deleted) b(deleted) c(0,0) d(0,0)
675  Retired and protected: c
676  bulk_reclamed-ed (i.e, found not protected): d
677  */
678  ASSERT_EQ(c_.dtors(), 2);
679  h.reset(); /* c is now no longer protected */
680  hazptr_cleanup<Atom>();
681  /* hazptr_cleanup calls bulk_reclaim which finds c unprotected,
682  which triggers a call to c->release_ref.
683 
684  c->release_ref finds c's link counts to be (0,0), which
685  triggers calls to d->release_ref and delete c.
686 
687  d->release_ref finds d's link counts to be (0,0), which triggers
688  a call to delete d.
689 
690  Finally:
691  a(deleted) b(deleted) c(deleted) d(deleted)
692  */
693  ASSERT_EQ(c_.dtors(), 4);
694 }
695 
696 template <template <typename> class Atom = std::atomic>
698  auto foo = new int;
699  hazptr_retire<Atom>(foo);
700  auto foo2 = new int;
701  hazptr_retire<Atom>(foo2, [](int* obj) { delete obj; });
702 
703  bool retired = false;
704  {
705  hazptr_domain<Atom> myDomain0;
706  struct delret {
707  bool* retired_;
708  explicit delret(bool* retire) : retired_(retire) {}
709  ~delret() {
710  *retired_ = true;
711  }
712  };
713  auto foo3 = new delret(&retired);
714  myDomain0.retire(foo3);
715  }
716  ASSERT_TRUE(retired);
717 }
718 
719 template <template <typename> class Atom = std::atomic>
720 void cleanup_test() {
721  int threadOps = 1007;
722  int mainOps = 19;
723  c_.clear();
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) {
728  threads[tid] = DSched::thread([&, tid]() {
729  for (int j = tid; j < threadOps; j += FLAGS_num_threads) {
730  auto p = new Node<Atom>;
731  p->retire();
732  }
733  threadsDone.fetch_add(1);
734  while (!mainDone.load()) {
735  /* spin */;
736  }
737  });
738  }
739  { // include the main thread in the test
740  for (int i = 0; i < mainOps; ++i) {
741  auto p = new Node<Atom>;
742  p->retire();
743  }
744  }
745  while (threadsDone.load() < FLAGS_num_threads) {
746  /* spin */;
747  }
748  ASSERT_EQ(c_.ctors(), threadOps + mainOps);
749  hazptr_cleanup<Atom>();
750  ASSERT_EQ(c_.dtors(), threadOps + mainOps);
751  mainDone.store(true);
752  for (auto& t : threads) {
753  DSched::join(t);
754  }
755  { // Cleanup after using array
756  c_.clear();
758  {
760  auto p0 = new Node<Atom>;
761  auto p1 = new Node<Atom>;
762  h[0].reset(p0);
763  h[1].reset(p1);
764  p0->retire();
765  p1->retire();
766  }
767  ASSERT_EQ(c_.ctors(), 2);
768  hazptr_cleanup<Atom>();
769  ASSERT_EQ(c_.dtors(), 2);
770  }
771  { // Cleanup after using local
772  c_.clear();
774  {
776  auto p0 = new Node<Atom>;
777  auto p1 = new Node<Atom>;
778  h[0].reset(p0);
779  h[1].reset(p1);
780  p0->retire();
781  p1->retire();
782  }
783  ASSERT_EQ(c_.ctors(), 2);
784  hazptr_cleanup<Atom>();
785  ASSERT_EQ(c_.dtors(), 2);
786  }
787 }
788 
789 template <template <typename> class Atom = std::atomic>
791  c_.clear();
792  using NodeT = NodeRC<true, Atom>;
793  auto y = new NodeT;
794  y->acquire_link_safe();
795  struct Foo : hazptr_obj_base<Foo, Atom> {
797  };
798  auto x = new Foo;
799  x->r_().store(y);
800  /* Thread retires x. Dtor of TLS priv list pushes x to domain, which
801  triggers bulk reclaim due to timed cleanup (when the test is run
802  by itself). Reclamation of x unlinks and retires y. y should
803  not be pushed into the thread's priv list. It should be pushed to
804  domain instead. */
805  auto thr = DSched::thread([&]() { x->retire(); });
806  DSched::join(thr);
807  ASSERT_EQ(c_.ctors(), 1);
808  hazptr_cleanup<Atom>();
809  ASSERT_EQ(c_.dtors(), 1);
810 }
811 
812 template <template <typename> class Atom = std::atomic>
813 void lifo_test() {
814  for (int i = 0; i < FLAGS_num_reps; ++i) {
815  Atom<int> sum{0};
817  std::vector<std::thread> threads(FLAGS_num_threads);
818  for (int tid = 0; tid < FLAGS_num_threads; ++tid) {
819  threads[tid] = DSched::thread([&, tid]() {
820  int local = 0;
821  for (int j = tid; j < FLAGS_num_ops; j += FLAGS_num_threads) {
822  s.push(j);
823  int v;
824  ASSERT_TRUE(s.pop(v));
825  local += v;
826  }
827  sum.fetch_add(local);
828  });
829  }
830  for (auto& t : threads) {
831  DSched::join(t);
832  }
833  hazptr_cleanup<Atom>();
834  int expected = FLAGS_num_ops * (FLAGS_num_ops - 1) / 2;
835  ASSERT_EQ(sum.load(), expected);
836  }
837 }
838 
839 template <template <typename> class Atom = std::atomic>
840 void swmr_test() {
841  using T = uint64_t;
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) {
846  threads[tid] = DSched::thread([&s, tid]() {
847  for (int j = tid; j < FLAGS_num_ops; j += FLAGS_num_threads) {
848  s.contains(j);
849  }
850  });
851  }
852  for (int j = 0; j < 10; ++j) {
853  s.add(j);
854  }
855  for (int j = 0; j < 10; ++j) {
856  s.remove(j);
857  }
858  for (auto& t : threads) {
859  DSched::join(t);
860  }
861  hazptr_cleanup<Atom>();
862  }
863 }
864 
865 template <template <typename> class Atom = std::atomic>
868  std::string u = "";
869  std::string v = "11112222";
870  auto ret = s.cas(u, v);
871  ASSERT_TRUE(ret);
872  u = "";
873  v = "11112222";
874  ret = s.cas(u, v);
875  ASSERT_FALSE(ret);
876  u = "11112222";
877  v = "22223333";
878  ret = s.cas(u, v);
879  ASSERT_TRUE(ret);
880  u = "22223333";
881  v = "333344445555";
882  ret = s.cas(u, v);
883  ASSERT_TRUE(ret);
884  hazptr_cleanup<Atom>();
885 }
886 
888  private:
889  // pre-init to avoid deadlock when using DeterministicAtomic
890  hazptr_domain<DeterministicAtomic>& defaultDomainHelper_{
892 };
893 
894 // Tests
895 
896 TEST(HazptrTest, basic_objects) {
898 }
899 
900 TEST_F(HazptrPreInitTest, dsched_basic_objects) {
901  DSched sched(DSched::uniform(0));
902  basic_objects_test<DeterministicAtomic>();
903 }
904 
905 TEST(HazptrTest, copy_and_move) {
907 }
908 
909 TEST_F(HazptrPreInitTest, dsched_copy_and_move) {
910  DSched sched(DSched::uniform(0));
911  copy_and_move_test<DeterministicAtomic>();
912 }
913 
914 TEST(HazptrTest, basic_holders) {
916 }
917 
918 TEST_F(HazptrPreInitTest, dsched_basic_holders) {
919  DSched sched(DSched::uniform(0));
920  basic_holders_test<DeterministicAtomic>();
921 }
922 
923 TEST(HazptrTest, basic_protection) {
925 }
926 
927 TEST_F(HazptrPreInitTest, dsched_basic_protection) {
928  DSched sched(DSched::uniform(0));
929  basic_protection_test<DeterministicAtomic>();
930 }
931 
932 TEST(HazptrTest, virtual) {
933  virtual_test();
934 }
935 
936 TEST_F(HazptrPreInitTest, dsched_virtual) {
937  DSched sched(DSched::uniform(0));
938  virtual_test<DeterministicAtomic>();
939 }
940 
941 TEST(HazptrTest, destruction) {
942  {
943  hazptr_domain<> myDomain0;
944  destruction_test(myDomain0);
945  }
946  destruction_test(default_hazptr_domain<std::atomic>());
947 }
948 
949 TEST_F(HazptrPreInitTest, dsched_destruction) {
950  DSched sched(DSched::uniform(0));
951  {
953  destruction_test<DeterministicAtomic>(myDomain0);
954  }
955  destruction_test<DeterministicAtomic>(
956  default_hazptr_domain<DeterministicAtomic>());
957 }
958 
959 TEST(HazptrTest, move) {
960  move_test();
961 }
962 
963 TEST_F(HazptrPreInitTest, dsched_move) {
964  DSched sched(DSched::uniform(0));
965  move_test<DeterministicAtomic>();
966 }
967 
968 TEST(HazptrTest, array) {
969  array_test();
970 }
971 
972 TEST_F(HazptrPreInitTest, dsched_array) {
973  DSched sched(DSched::uniform(0));
974  array_test<DeterministicAtomic>();
975 }
976 
977 TEST(HazptrTest, array_dtor_full_tc) {
979 }
980 
981 TEST_F(HazptrPreInitTest, dsched_array_dtor_full_tc) {
982  DSched sched(DSched::uniform(0));
983  array_dtor_full_tc_test<DeterministicAtomic>();
984 }
985 
986 TEST(HazptrTest, local) {
987  local_test();
988 }
989 
990 TEST_F(HazptrPreInitTest, dsched_local) {
991  DSched sched(DSched::uniform(0));
992  local_test<DeterministicAtomic>();
993 }
994 
995 TEST(HazptrTest, linked_mutable) {
996  linked_test<true>();
997 }
998 
999 TEST_F(HazptrPreInitTest, dsched_linked_mutable) {
1000  DSched sched(DSched::uniform(0));
1001  linked_test<true, DeterministicAtomic>();
1002 }
1003 
1004 TEST(HazptrTest, linked_immutable) {
1005  linked_test<false>();
1006 }
1007 
1008 TEST_F(HazptrPreInitTest, dsched_linked_immutable) {
1009  DSched sched(DSched::uniform(0));
1010  linked_test<false, DeterministicAtomic>();
1011 }
1012 
1013 TEST(HazptrTest, mt_linked_mutable) {
1014  mt_linked_test<true>();
1015 }
1016 
1017 TEST_F(HazptrPreInitTest, dsched_mt_linked_mutable) {
1018  DSched sched(DSched::uniform(0));
1019  mt_linked_test<true, DeterministicAtomic>();
1020 }
1021 
1022 TEST(HazptrTest, mt_linked_immutable) {
1023  mt_linked_test<false>();
1024 }
1025 
1026 TEST_F(HazptrPreInitTest, dsched_mt_linked_immutable) {
1027  DSched sched(DSched::uniform(0));
1028  mt_linked_test<false, DeterministicAtomic>();
1029 }
1030 
1031 TEST(HazptrTest, auto_retire) {
1032  auto_retire_test();
1033 }
1034 
1035 TEST_F(HazptrPreInitTest, dsched_auto_retire) {
1036  DSched sched(DSched::uniform(0));
1037  auto_retire_test<DeterministicAtomic>();
1038 }
1039 
1040 TEST(HazptrTest, free_function_retire) {
1042 }
1043 
1044 TEST_F(HazptrPreInitTest, dsched_free_function_retire) {
1045  DSched sched(DSched::uniform(0));
1046  free_function_retire_test<DeterministicAtomic>();
1047 }
1048 
1049 TEST(HazptrTest, cleanup) {
1050  cleanup_test();
1051 }
1052 
1053 TEST_F(HazptrPreInitTest, dsched_cleanup) {
1054  DSched sched(DSched::uniform(0));
1055  cleanup_test<DeterministicAtomic>();
1056 }
1057 
1058 TEST(HazptrTest, priv_dtor) {
1059  priv_dtor_test();
1060 }
1061 
1062 TEST_F(HazptrPreInitTest, dsched_priv_dtor) {
1063  DSched sched(DSched::uniform(0));
1064  priv_dtor_test<DeterministicAtomic>();
1065 }
1066 
1067 TEST(HazptrTest, lifo) {
1068  lifo_test();
1069 }
1070 
1071 TEST_F(HazptrPreInitTest, dsched_lifo) {
1072  DSched sched(DSched::uniform(0));
1073  lifo_test<DeterministicAtomic>();
1074 }
1075 
1076 TEST(HazptrTest, swmr) {
1077  swmr_test();
1078 }
1079 
1080 TEST_F(HazptrPreInitTest, dsched_swmr) {
1081  DSched sched(DSched::uniform(0));
1082  swmr_test<DeterministicAtomic>();
1083 }
1084 
1085 TEST(HazptrTest, wide_cas) {
1086  wide_cas_test();
1087 }
1088 
1089 TEST_F(HazptrPreInitTest, dsched_wide_cas) {
1090  DSched sched(DSched::uniform(0));
1091  wide_cas_test<DeterministicAtomic>();
1092 }
1093 
1094 TEST(HazptrTest, reclamation_without_calling_cleanup) {
1095  c_.clear();
1096  int nthr = 5;
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) {
1102  auto p = new Node<>;
1103  p->retire();
1104  }
1105  });
1106  }
1107  for (auto& t : thr) {
1108  t.join();
1109  }
1110  ASSERT_GT(c_.dtors(), 0);
1111 }
1112 
1113 // Benchmark drivers
1114 
1115 template <typename InitFunc, typename Func, typename EndFunc>
1117  int nthreads,
1118  const InitFunc& init,
1119  const Func& fn,
1120  const EndFunc& endFn) {
1121  std::atomic<bool> start{false};
1122  Barrier b(nthreads + 1);
1123  init();
1124  std::vector<std::thread> threads(nthreads);
1125  for (int tid = 0; tid < nthreads; ++tid) {
1126  threads[tid] = std::thread([&, tid] {
1127  b.wait();
1128  fn(tid);
1129  });
1130  }
1131  b.wait();
1132  // begin time measurement
1133  auto tbegin = std::chrono::steady_clock::now();
1134  start.store(true);
1135  for (auto& t : threads) {
1136  t.join();
1137  }
1138  hazptr_cleanup();
1139  // end time measurement
1140  auto tend = std::chrono::steady_clock::now();
1141  endFn();
1142  return std::chrono::duration_cast<std::chrono::nanoseconds>(tend - tbegin)
1143  .count();
1144 }
1145 
1146 template <typename RepFunc>
1147 uint64_t bench(std::string name, int ops, const RepFunc& repFn) {
1148  int reps = 10;
1149  uint64_t min = UINTMAX_MAX;
1150  uint64_t max = 0;
1151  uint64_t sum = 0;
1152 
1153  repFn(); // sometimes first run is outlier
1154  for (int r = 0; r < reps; ++r) {
1155  uint64_t dur = repFn();
1156  sum += dur;
1157  min = std::min(min, dur);
1158  max = std::max(max, dur);
1159  }
1160 
1161  const std::string unit = " ns";
1162  uint64_t avg = sum / reps;
1163  uint64_t res = min;
1164  std::cout << name;
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;
1169  return res;
1170 }
1171 
1172 //
1173 // Benchmarks
1174 //
1175 // const int ops = 1000000;
1176 const int ops = 1000000;
1177 
1179  auto repFn = [&] {
1180  auto init = [] {};
1181  auto fn = [&](int tid) {
1182  for (int j = tid; j < 10 * ops; j += nthreads) {
1184  }
1185  };
1186  auto endFn = [] {};
1187  return run_once(nthreads, init, fn, endFn);
1188  };
1189  return bench(name, ops, repFn);
1190 }
1191 
1192 template <size_t M>
1194  auto repFn = [&] {
1195  auto init = [] {};
1196  auto fn = [&](int tid) {
1197  for (int j = tid; j < 10 * ops; j += nthreads) {
1199  }
1200  };
1201  auto endFn = [] {};
1202  return run_once(nthreads, init, fn, endFn);
1203  };
1204  return bench(name, ops, repFn);
1205 }
1206 
1207 template <size_t M>
1209  auto repFn = [&] {
1210  auto init = [] {};
1211  auto fn = [&](int tid) {
1212  for (int j = tid; j < 10 * ops; j += nthreads) {
1214  }
1215  };
1216  auto endFn = [] {};
1217  return run_once(nthreads, init, fn, endFn);
1218  };
1219  return bench(name, ops, repFn);
1220 }
1221 
1223  struct Foo : public hazptr_obj_base<Foo> {};
1224  auto repFn = [&] {
1225  auto init = [] {};
1226  auto fn = [&](int tid) {
1227  for (int j = tid; j < ops; j += nthreads) {
1228  auto p = new Foo;
1229  p->retire();
1230  }
1231  };
1232  auto endFn = [] {};
1233  return run_once(nthreads, init, fn, endFn);
1234  };
1235  return bench(name, ops, repFn);
1236 }
1237 
1239  std::string name,
1240  int nthreads,
1241  int size,
1242  bool provided = false) {
1243  auto repFn = [&] {
1244  List<Node<>> l(size);
1245  auto init = [&] {};
1246  auto fn = [&](int tid) {
1247  if (provided) {
1248  hazptr_local<2> hptr;
1249  for (int j = tid; j < ops; j += nthreads) {
1250  l.hand_over_hand(size, &hptr[0], &hptr[1]);
1251  }
1252  } else {
1253  for (int j = tid; j < ops; j += nthreads) {
1254  l.hand_over_hand(size);
1255  }
1256  }
1257  };
1258  auto endFn = [] {};
1259  return run_once(nthreads, init, fn, endFn);
1260  };
1261  return bench(name, ops, repFn);
1262 }
1263 
1265  std::string name,
1266  int nthreads,
1267  int size,
1268  bool provided = false) {
1269  auto repFn = [&] {
1270  List<NodeRC<true>> l(size);
1271  auto init = [] {};
1272  auto fn = [&](int tid) {
1273  if (provided) {
1274  hazptr_local<1> hptr;
1275  for (int j = tid; j < ops; j += nthreads) {
1276  l.protect_all(size, hptr[0]);
1277  }
1278  } else {
1279  for (int j = tid; j < ops; j += nthreads) {
1280  l.protect_all(size);
1281  }
1282  }
1283  };
1284  auto endFn = [] {};
1285  return run_once(nthreads, init, fn, endFn);
1286  };
1287  return bench(name, ops, repFn);
1288 }
1289 
1291  auto repFn = [&] {
1292  auto init = [] {};
1293  auto fn = [&](int) {
1295  for (int i = 0; i < 1000; i++) {
1296  hazptr_cleanup();
1297  }
1298  };
1299  auto endFn = [] {};
1300  return run_once(nthreads, init, fn, endFn);
1301  };
1302  return bench(name, ops, repFn);
1303 }
1304 
1305 const int nthr[] = {1, 10};
1306 const int sizes[] = {10, 20};
1307 
1308 void benches() {
1309  for (int i : nthr) {
1310  std::cout << "================================ " << std::setw(2) << i
1311  << " threads "
1312  << "================================" << std::endl;
1313  std::cout << "10x construct/destruct hazptr_holder ";
1314  holder_bench("", i);
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 ";
1328  obj_bench("", i);
1329  for (int j : sizes) {
1330  std::cout << j << "-item list hand-over-hand - own hazptrs ";
1331  list_hoh_bench("", i, j, true);
1332  std::cout << j << "-item list hand-over-hand ";
1333  list_hoh_bench("", i, j);
1334  std::cout << j << "-item list protect all - own hazptr ";
1335  list_protect_all_bench("", i, j, true);
1336  std::cout << j << "-item list protect all ";
1337  list_protect_all_bench("", i, j);
1338  }
1339  std::cout << "hazptr_cleanup ";
1340  cleanup_bench("", i);
1341  }
1342 }
1343 
1344 TEST(HazptrTest, bench) {
1345  if (FLAGS_bench) {
1346  benches();
1347  }
1348 }
1349 
1350 /*
1351 $ numactl -N 1 ./buck-out/gen/folly/synchronization/test/hazptr_test --bench
1352 
1353 ================================ 1 threads ================================
1354 10x construct/destruct hazptr_holder 51 ns 51 ns 50 ns
1355 10x construct/destruct hazptr_array<1> 54 ns 52 ns 52 ns
1356 10x construct/destruct hazptr_array<2> 60 ns 59 ns 58 ns
1357 10x construct/destruct hazptr_array<3> 141 ns 88 ns 82 ns
1358 10x construct/destruct hazptr_local<1> 13 ns 12 ns 12 ns
1359 10x construct/destruct hazptr_local<2> 15 ns 15 ns 15 ns
1360 10x construct/destruct hazptr_local<3> 39 ns 39 ns 38 ns
1361 allocate/retire/reclaim object 70 ns 68 ns 67 ns
1362 10-item list hand-over-hand - own hazptrs 22 ns 20 ns 18 ns
1363 10-item list hand-over-hand 28 ns 25 ns 22 ns
1364 10-item list protect all - own hazptr 12 ns 11 ns 11 ns
1365 10-item list protect all 22 ns 13 ns 12 ns
1366 20-item list hand-over-hand - own hazptrs 42 ns 40 ns 38 ns
1367 20-item list hand-over-hand 48 ns 43 ns 41 ns
1368 20-item list protect all - own hazptr 28 ns 28 ns 28 ns
1369 20-item list protect all 31 ns 29 ns 29 ns
1370 hazptr_cleanup 2 ns 1 ns 1 ns
1371 ================================ 10 threads ================================
1372 10x construct/destruct hazptr_holder 11 ns 8 ns 8 ns
1373 10x construct/destruct hazptr_array<1> 8 ns 7 ns 7 ns
1374 10x construct/destruct hazptr_array<2> 9 ns 9 ns 9 ns
1375 10x construct/destruct hazptr_array<3> 19 ns 17 ns 14 ns
1376 10x construct/destruct hazptr_local<1> 8 ns 8 ns 8 ns
1377 10x construct/destruct hazptr_local<2> 8 ns 8 ns 7 ns
1378 10x construct/destruct hazptr_local<3> 11 ns 11 ns 10 ns
1379 allocate/retire/reclaim object 20 ns 17 ns 16 ns
1380 10-item list hand-over-hand - own hazptrs 3 ns 3 ns 3 ns
1381 10-item list hand-over-hand 3 ns 3 ns 3 ns
1382 10-item list protect all - own hazptr 2 ns 2 ns 2 ns
1383 10-item list protect all 2 ns 2 ns 2 ns
1384 20-item list hand-over-hand - own hazptrs 6 ns 6 ns 6 ns
1385 20-item list hand-over-hand 6 ns 6 ns 6 ns
1386 20-item list protect all - own hazptr 4 ns 4 ns 4 ns
1387 20-item list protect all 5 ns 4 ns 4 ns
1388 hazptr_cleanup 119 ns 113 ns 97 ns
1389  */
void push_links(bool m, S &s)
Definition: HazptrTest.cpp:158
Definition: InvokeTest.cpp:58
#define ASSERT_GT(val1, val2)
Definition: gtest.h:1976
std::atomic< int > dtors_
Definition: HazptrTest.cpp:59
uint64_t bench(std::string name, int ops, const RepFunc &repFn)
DEFINE_int64(num_reps, 10,"Number of test reps")
void virtual_test()
Definition: HazptrTest.cpp:369
void array_dtor_full_tc_test()
Definition: HazptrTest.cpp:456
bool hand_over_hand(int val)
Definition: HazptrTest.cpp:220
#define T(v)
Definition: http_parser.c:233
DEFINE_bool(bench, false,"run benchmark")
Atom< Node< Atom > * > * ptr_next() noexcept
Definition: HazptrTest.cpp:120
void destruction_test(hazptr_domain< Atom > &domain)
Definition: HazptrTest.cpp:387
*than *hazptr_holder h
Definition: Hazptr.h:116
void hazptr_retire(T *obj, D reclaim={})
Definition: HazptrDomain.h:378
void mt_linked_test()
Definition: HazptrTest.cpp:540
std::atomic< int64_t > sum(0)
uint64_t holder_bench(std::string name, int nthreads)
auto v
bool hand_over_hand(int val, hazptr_holder< Atom > *hptr_prev, hazptr_holder< Atom > *hptr_curr)
Definition: HazptrTest.cpp:192
#define ASSERT_EQ(val1, val2)
Definition: gtest.h:1956
bool protect_all(int val, hazptr_holder< Atom > &hptr)
Definition: HazptrTest.cpp:225
void local_test()
Definition: HazptrTest.cpp:485
FOLLY_ALWAYS_INLINE bool try_protect(T *&ptr, const Atom< T * > &src) noexcept
Definition: HazptrHolder.h:116
FOLLY_ALWAYS_INLINE T * get_protected(const Atom< T * > &src) noexcept
Definition: HazptrHolder.h:138
char b
LogLevel max
Definition: LogLevel.cpp:31
uint64_t cleanup_bench(std::string name, int nthreads)
int value() const noexcept
Definition: HazptrTest.cpp:149
void basic_protection_test()
Definition: HazptrTest.cpp:354
int retires() const noexcept
Definition: HazptrTest.cpp:77
uint64_t run_once(int nthreads, const InitFunc &init, const Func &fn, const EndFunc &endFn)
hazptr_domain< Atom > & default_hazptr_domain()
Definition: HazptrDomain.h:363
std::chrono::steady_clock::time_point now()
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
const int x
void inc_ctors() noexcept
Definition: HazptrTest.cpp:81
void retire(D deleter={}, hazptr_domain< Atom > &domain=default_hazptr_domain< Atom >())
Definition: HazptrObj.h:229
DeterministicAtomicImpl< T, DeterministicSchedule > DeterministicAtomic
uint64_t obj_bench(std::string name, int nthreads)
FOLLY_NOINLINE void foo2()
const int nthr[]
double val
Definition: String.cpp:273
int value() const noexcept
Definition: HazptrTest.cpp:112
void inc_dtors() noexcept
Definition: HazptrTest.cpp:85
int val_
Definition: HazptrTest.cpp:129
static std::thread thread(Func &&func, Args &&...args)
NodeRC< Mutable, Atom > * next() const noexcept
Definition: HazptrTest.cpp:153
NodeAuto(NodeAuto *l1=nullptr, NodeAuto *l2=nullptr) noexcept
Definition: HazptrTest.cpp:249
requires E e noexcept(noexcept(s.error(std::move(e))))
void init()
static FOLLY_ALWAYS_INLINE hazptr_domain< Atom > & get()
Definition: HazptrDomain.h:349
static Count c_
Definition: HazptrTest.cpp:94
int ctors() const noexcept
Definition: HazptrTest.cpp:69
void clear() noexcept
Definition: HazptrTest.cpp:63
void retire(T *obj, D reclaim={})
Definition: HazptrDomain.h:87
bool cas(T &u, T &v)
Definition: HazptrWideCAS.h:42
std::vector< std::thread::id > threads
int val_
Definition: HazptrTest.cpp:99
static int nthreads
std::atomic< int > ctors_
Definition: HazptrTest.cpp:58
const char * name
Definition: http_parser.c:437
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
static std::function< size_t(size_t)> uniform(uint64_t seed)
Function< void()> Func
Definition: Executor.h:27
Node< Atom > * next() const noexcept
Definition: HazptrTest.cpp:116
LogLevel min
Definition: LogLevel.cpp:30
void auto_retire_test()
Definition: HazptrTest.cpp:607
const int sizes[]
void free_function_retire_test()
Definition: HazptrTest.cpp:697
void cleanup_test()
Definition: HazptrTest.cpp:720
#define Atom
bool contains(const T &val) const
Definition: HazptrSWMRSet.h:88
void lifo_test()
Definition: HazptrTest.cpp:813
void linked_test()
Definition: HazptrTest.cpp:500
void inc_retires() noexcept
Definition: HazptrTest.cpp:89
bool remove(const T &v)
Definition: HazptrSWMRSet.h:71
TEST(HazptrTest, basic_objects)
Definition: HazptrTest.cpp:896
List(int size)
Definition: HazptrTest.cpp:174
static map< string, int > m
constexpr int hazptr_domain_rcount_threshold()
Definition: HazptrDomain.h:38
Definition: InvokeTest.cpp:72
void hazptr_cleanup(hazptr_domain< Atom > &domain=default_hazptr_domain< Atom >()) noexcept
Definition: HazptrDomain.h:384
constexpr Unit unit
Definition: Unit.h:45
char a
const int ops
Atom< Node< Atom > * > next_
Definition: HazptrTest.cpp:100
DEFINE_int32(num_threads, 6,"Number of threads")
void wide_cas_test()
Definition: HazptrTest.cpp:866
NodeRC(int v=0, NodeRC *n=nullptr, bool acq=false) noexcept
Definition: HazptrTest.cpp:132
auto start
std::atomic< int > retires_
Definition: HazptrTest.cpp:60
**Optimized Holders **The template hazptr_array< M > provides most of the functionality *of M hazptr_holder s but with faster construction destruction *for M
Definition: Hazptr.h:104
void move_test()
Definition: HazptrTest.cpp:409
int * count
int dtors() const noexcept
Definition: HazptrTest.cpp:73
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)
const char * string
Definition: Conv.cpp:212
void basic_objects_test()
Definition: HazptrTest.cpp:280
static set< string > s
const
Definition: upload.py:398
Node(int v=0, Node *n=nullptr, bool=false) noexcept
Definition: HazptrTest.cpp:103
Definition: InvokeTest.cpp:65
void array_test()
Definition: HazptrTest.cpp:436
void swmr_test()
Definition: HazptrTest.cpp:840
TEST_F(HazptrPreInitTest, dsched_basic_objects)
Definition: HazptrTest.cpp:900
Atom< NodeRC< Mutable, Atom > * > next_
Definition: HazptrTest.cpp:128
FOLLY_ALWAYS_INLINE void reset(const T *ptr) noexcept
Definition: HazptrHolder.h:153
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
Definition: F14TestUtil.h:414
#define ASSERT_FALSE(condition)
Definition: gtest.h:1868
static void join(std::thread &child)
void acquire_link_safe() noexcept
NodeAuto< Atom > * link(size_t i)
Definition: HazptrTest.cpp:260
uint64_t array_bench(std::string name, int nthreads)
void basic_holders_test()
Definition: HazptrTest.cpp:347
void copy_and_move_test()
Definition: HazptrTest.cpp:317
void priv_dtor_test()
Definition: HazptrTest.cpp:790
char c
#define ASSERT_TRUE(condition)
Definition: gtest.h:1865
bool protect_all(int val)
Definition: HazptrTest.cpp:237
void cleanup()
Definition: Init.cpp:59
uint64_t list_hoh_bench(std::string name, int nthreads, int size, bool provided=false)
void push_links(bool m, S &s)
Definition: HazptrTest.cpp:265
void benches()
def next(obj)
Definition: ast.py:58