#include "gtest/gtest.h" #include #include #include #include "data_structures/concurrent/skiplist.hpp" #include "logging/streams/stderr.hpp" /** * FakeItem class which increments a variable in the destructor. * Used to keep track of the number of destroyed elements in GC. */ class FakeItem { public: FakeItem(std::atomic &count, int value) : count(count), value(value) {} ~FakeItem() { count.fetch_add(1); } bool operator<(const FakeItem &item) const { return this->value < item.value; } bool operator>(const FakeItem &item) const { return this->value > item.value; } private: std::atomic &count; int value; }; TEST(SkipListGC, TripleScopeGC) { SkipList skiplist; std::atomic *count = new std::atomic{0}; auto item = FakeItem(*count, 1); { auto access_1 = skiplist.access(); { auto access_2 = skiplist.access(); { auto access_3 = skiplist.access(); access_1.insert(item); // add with 1 access_2.remove(item); // remove with 2 std::this_thread::sleep_for(std::chrono::milliseconds(100)); EXPECT_EQ(*count, 0); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); EXPECT_EQ(*count, 0); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); EXPECT_EQ(*count, 0); } // scope end - GC called for (int i = 0; i < 10; ++i) { if (*count != 0) break; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } EXPECT_EQ(*count, 1); } TEST(SkipListGC, BlockedGCNoGC) { SkipList skiplist; std::atomic *count = new std::atomic{0}; auto item = FakeItem(*count, 1); auto blocking_access = skiplist.access(); { auto access = skiplist.access(); access.insert(item); access.remove(item); } // scope end - GC still isn't called because of blocking_access std::this_thread::sleep_for(std::chrono::milliseconds(100)); EXPECT_EQ(*count, 0); } TEST(SkipListGC, NotInScopeGC) { SkipList skiplist; std::atomic *count = new std::atomic{0}; auto item = FakeItem(*count, 1); { auto access = skiplist.access(); access.insert(item); access.remove(item); } // scope end - GC called for (int i = 0; i < 10; ++i) { if (*count == 1) break; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // If this count is not 1 that means we are still doing GC in the background // and might crash the test if we try to modify count variable after it's been // deallocated. ASSERT_EQ(*count, 1); } TEST(SkipListGC, StillInScopeNoGC) { SkipList skiplist; std::atomic *count = new std::atomic{0}; auto item = FakeItem(*count, 1); auto access = skiplist.access(); access.insert(item); access.remove(item); std::this_thread::sleep_for(std::chrono::milliseconds(100)); EXPECT_EQ(*count, 0); } int main(int argc, char **argv) { logging::init_sync(); logging::log->pipe(std::make_unique()); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }