@@ -830,9 +830,37 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
830
830
// / Take a snapshot of the current state of the hash map.
831
831
Snapshot snapshot () {
832
832
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);
836
864
837
865
return Snapshot (this , indices, elements, elementCount);
838
866
}
0 commit comments