TEST_F(HazptrTest, basic_refcount) { constructed.store(0); destroyed.store(0); Foo* p = nullptr; int num = 20; for (int i = 0; i < num; ++i) { p = new Foo(i, p); if (i & 1) { p->acquire_ref_safe(); } else { p->acquire_ref(); } } hazptr_holder hptr; hptr.reset(p); for (auto q = p->next_; q; q = q->next_) { q->retire(); } int v = num; for (auto q = p; q; q = q->next_) { CHECK_GT(v, 0); --v; CHECK_EQ(q->val_, v); } CHECK(!p->release_ref()); CHECK_EQ(constructed.load(), num); CHECK_EQ(destroyed.load(), 0); p->retire(); CHECK_EQ(constructed.load(), num); CHECK_EQ(destroyed.load(), 0); hptr.reset(); /* retire enough objects to guarantee reclamation of Foo objects */ for (int i = 0; i < 100; ++i) { auto a = new Dummy; a->retire(); } CHECK_EQ(constructed.load(), num); CHECK_EQ(destroyed.load(), num); }
TEST_F(HazptrTest, mt_refcount) { constructed.store(0); destroyed.store(0); std::atomic<bool> ready(false); std::atomic<int> setHazptrs(0); std::atomic<Foo*> head; int num = 20; int nthr = 10; std::vector<std::thread> thr(nthr); for (int i = 0; i < nthr; ++i) { thr[i] = std::thread([&] { while (!ready.load()) { /* spin */ } hazptr_holder hptr; auto p = hptr.get_protected(head); ++setHazptrs; /* Concurrent with removal */ int v = num; for (auto q = p; q; q = q->next_) { CHECK_GT(v, 0); --v; CHECK_EQ(q->val_, v); } CHECK_EQ(v, 0); }); } Foo* p = nullptr; for (int i = 0; i < num; ++i) { p = new Foo(i, p); p->acquire_ref_safe(); } head.store(p); ready.store(true); while (setHazptrs.load() < nthr) { /* spin */ } /* this is concurrent with traversal by reader */ head.store(nullptr); for (auto q = p; q; q = q->next_) { q->retire(); } HAZPTR_DEBUG_PRINT("Foo should not be destroyed"); CHECK_EQ(constructed.load(), num); CHECK_EQ(destroyed.load(), 0); HAZPTR_DEBUG_PRINT("Foo may be destroyed after releasing the last reference"); if (p->release_ref()) { delete p; } /* retire enough objects to guarantee reclamation of Foo objects */ for (int i = 0; i < 100; ++i) { auto a = new Dummy; a->retire(); } for (int i = 0; i < nthr; ++i) { thr[i].join(); } CHECK_EQ(constructed.load(), num); CHECK_EQ(destroyed.load(), num); }