Skip to content

Commit 9ce8250

Browse files
authored
Merge pull request swiftlang#33595 from mikeash/fix-concurrenthashmap-clear-race
[Runtime] Fix a race condition in ConcurrentReadableHashMap with concurrent reads and clears.
2 parents 0bd8b14 + af35936 commit 9ce8250

File tree

1 file changed

+31
-3
lines changed

1 file changed

+31
-3
lines changed

include/swift/Runtime/Concurrent.h

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -830,9 +830,37 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
830830
/// Take a snapshot of the current state of the hash map.
831831
Snapshot snapshot() {
832832
incrementReaders();
833-
auto *indices = Indices.load(SWIFT_MEMORY_ORDER_CONSUME);
834-
auto elementCount = ElementCount.load(std::memory_order_acquire);
835-
auto *elements = Elements.load(std::memory_order_acquire);
833+
834+
// Carefully loading the indices, element count, and elements pointer in
835+
// order ensures a consistent view of the table with respect to concurrent
836+
// inserts. However, this is not sufficient to avoid an inconsistent view
837+
// with respect to concurrent clears. The danger scenario is:
838+
//
839+
// 1. Read indices and elementCount from a table with N entries.
840+
// 2. Another thread clears the table.
841+
// 3. Another thread inserts M entries, where M < N.
842+
// 4. The reader thread reads elements.
843+
// 5. The reader thread performs a find. The key's hash leads us to an index
844+
// I, where > M.
845+
// 6. The reader thread reads from element I, which is off the end of the
846+
// elements array.
847+
//
848+
// To avoid this, read the elements pointer twice, at the beginning and end.
849+
// If the values are not the same then there may have been a clear in the
850+
// middle, so we retry. This will have false positives: a new element
851+
// pointer can just mean a concurrent insert that triggered a resize of the
852+
// elements array. This is harmless aside from a small performance hit, and
853+
// should not happen often.
854+
IndexStorage *indices;
855+
size_t elementCount;
856+
ElemTy *elements;
857+
ElemTy *elements2;
858+
do {
859+
elements = Elements.load(std::memory_order_acquire);
860+
indices = Indices.load(SWIFT_MEMORY_ORDER_CONSUME);
861+
elementCount = ElementCount.load(std::memory_order_acquire);
862+
elements2 = Elements.load(std::memory_order_acquire);
863+
} while (elements != elements2);
836864

837865
return Snapshot(this, indices, elements, elementCount);
838866
}

0 commit comments

Comments
 (0)