From: Anton Likhtarov Date: Thu, 19 Nov 2015 19:32:26 +0000 (-0800) Subject: Fix invalid DCHECK X-Git-Tag: deprecate-dynamic-initializer~249 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=2fa98577f4b73f8a2180fb88b5f075cce6d650e6;p=folly.git Fix invalid DCHECK Summary: There's a race between insert() and erase(): as soon as insert() releases the lock (swaps kLockedKey_ with the actual key), an erase() might jump in and invalidate the key. As far as I can tell, this bug existed since the beginning. Reviewed By: nbronson Differential Revision: D2673099 fb-gh-sync-id: 4721893d2ad4836e11acc0fb4ecb0dd7b2b69be1 --- diff --git a/folly/AtomicHashArray-inl.h b/folly/AtomicHashArray-inl.h index e3b32a04..1125bb98 100644 --- a/folly/AtomicHashArray-inl.h +++ b/folly/AtomicHashArray-inl.h @@ -144,9 +144,11 @@ insertInternal(KeyT key_in, ArgTs&&... vCtorArgs) { --numPendingEntries_; throw; } + // An erase() can race here and delete right after our insertion // Direct comparison rather than EqualFcn ok here // (we just inserted it) - DCHECK(relaxedLoadKey(*cell) == key_in); + DCHECK(relaxedLoadKey(*cell) == key_in || + relaxedLoadKey(*cell) == kErasedKey_); --numPendingEntries_; ++numEntries_; // This is a thread cached atomic increment :) if (numEntries_.readFast() >= maxEntries_) { diff --git a/folly/test/AtomicHashMapTest.cpp b/folly/test/AtomicHashMapTest.cpp index 46223107..63523eda 100644 --- a/folly/test/AtomicHashMapTest.cpp +++ b/folly/test/AtomicHashMapTest.cpp @@ -667,6 +667,34 @@ TEST(Ahm, erase_find_race) { write_thread.join(); } +// Erase right after insert race bug repro (t9130653) +TEST(Ahm, erase_after_insert_race) { + const uint64_t limit = 10000; + const size_t num_threads = 100; + const size_t num_iters = 500; + AtomicHashMap map(limit + 10); + + std::atomic go{false}; + std::vector ts; + for (size_t i = 0; i < num_threads; ++i) { + ts.emplace_back([&]() { + while (!go) { + continue; + } + for (size_t n = 0; n < num_iters; ++n) { + map.erase(1); + map.insert(1, 1); + } + }); + } + + go = true; + + for (auto& t : ts) { + t.join(); + } +} + // Repro for a bug when iterator didn't skip empty submaps. TEST(Ahm, iterator_skips_empty_submaps) { AtomicHashMap::Config config;