diff --git a/src/data_structures/concurrent/skiplist.hpp b/src/data_structures/concurrent/skiplist.hpp index d68d96a5b..f0a4d8ea2 100644 --- a/src/data_structures/concurrent/skiplist.hpp +++ b/src/data_structures/concurrent/skiplist.hpp @@ -794,8 +794,11 @@ class SkipList : private Lockable { int h = static_cast(pred->height) - 1; while (true) { - // try to descend down first the next key on this layer overshoots - for (; h >= 0 && less(item, node = pred->forward(h)); --h) { + // try to descend down first while the next key on this layer overshoots + // or the next key is marked for deletion + for (; h >= 0 && (less(item, node = pred->forward(h)) || + (node && node->flags.is_marked())); + --h) { } // if we overshoot at every layer, item doesn't exist diff --git a/tests/concurrent/sl_hang.cpp b/tests/concurrent/sl_hang.cpp new file mode 100644 index 000000000..402d00f84 --- /dev/null +++ b/tests/concurrent/sl_hang.cpp @@ -0,0 +1,37 @@ +#include "gtest/gtest.h" + +#include +#include + +#include "data_structures/concurrent/skiplist.hpp" + +// Try to provoke find_or_larger to hang. This happened before and caused +// Jenkins to stop responding. It is hard to recreate deterministically and this +// is the best we can do without doing friend_tests or refactoring skiplist. +TEST(SkipList, HangDuringFindOrLarger) { + std::vector threads; + SkipList skiplist; + const int num_of_threads = 8; + const int iter = 100000; + for (int i = 0; i < num_of_threads; ++i) { + threads.emplace_back([&iter, &skiplist]() { + auto accessor = skiplist.access(); + for (int i = 0; i < iter; ++i) accessor.insert(rand() % 3); + }); + threads.emplace_back([&iter, &skiplist]() { + auto accessor = skiplist.access(); + for (int i = 0; i < iter; ++i) accessor.remove(rand() % 3); + }); + threads.emplace_back([&iter, &skiplist]() { + auto accessor = skiplist.access(); + for (int i = 0; i < iter; ++i) + accessor.find_or_larger::ConstIterator>(rand() % 3); + }); + } + for (auto &thread : threads) thread.join(); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}