@@ -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