@@ -601,7 +601,7 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
601
601
// / rounds up to the next allocation quantum by calling `malloc_good_size`.
602
602
// / Otherwise, just return the passed-in size, which is always valid even if
603
603
// / not necessarily optimal.
604
- size_t goodSize (size_t size) {
604
+ static size_t goodSize (size_t size) {
605
605
#if defined(__APPLE__) && defined(__MACH__)
606
606
return malloc_good_size (size);
607
607
#else
@@ -693,6 +693,29 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
693
693
}
694
694
};
695
695
696
+ // / The struct used for element storage. The `Elem` member is considered to be
697
+ // / the first element of a variable-length array, whose size is determined by
698
+ // / the allocation.
699
+ struct ElementStorage {
700
+ uint32_t Capacity;
701
+ ElemTy Elem;
702
+
703
+ static ElementStorage *allocate (size_t capacity) {
704
+ auto headerSize = offsetof (ElementStorage, Elem);
705
+ auto size = goodSize (headerSize + capacity * sizeof (ElemTy));
706
+
707
+ auto *ptr = reinterpret_cast <ElementStorage *>(malloc (size));
708
+ if (!ptr)
709
+ swift::crash (" Could not allocate memory." );
710
+
711
+ ptr->Capacity = (size - headerSize) / sizeof (ElemTy);
712
+
713
+ return ptr;
714
+ }
715
+
716
+ ElemTy *data () { return &Elem; }
717
+ };
718
+
696
719
// / A simple linked list representing pointers that need to be freed.
697
720
struct FreeListNode {
698
721
FreeListNode *Next;
@@ -723,17 +746,14 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
723
746
std::atomic<uint32_t > ElementCount{0 };
724
747
725
748
// / The array of elements.
726
- std::atomic<ElemTy *> Elements{nullptr };
749
+ std::atomic<ElementStorage *> Elements{nullptr };
727
750
728
751
// / The array of indices.
729
752
std::atomic<IndexStorage *> Indices{nullptr };
730
753
731
754
// / The writer lock, which must be taken before any mutation of the table.
732
755
StaticMutex WriterLock;
733
756
734
- // / The maximum number of elements that the current elements array can hold.
735
- uint32_t ElementCapacity{0 };
736
-
737
757
// / The list of pointers to be freed once no readers are active.
738
758
FreeListNode *FreeList{nullptr };
739
759
@@ -754,22 +774,18 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
754
774
755
775
// / Grow the elements array, adding the old array to the free list and
756
776
// / returning the new array with all existing elements copied into it.
757
- ElemTy *resize (ElemTy *elements, size_t elementCount) {
777
+ ElementStorage *resize (ElementStorage *elements, size_t elementCount) {
758
778
// Grow capacity by 25%, making sure we grow by at least 1.
759
779
size_t newCapacity =
760
780
std::max (elementCount + (elementCount >> 2 ), elementCount + 1 );
761
- size_t newSize = newCapacity * sizeof (ElemTy);
762
-
763
- newSize = goodSize (newSize);
764
- newCapacity = newSize / sizeof (ElemTy);
781
+ auto *newElements = ElementStorage::allocate (newCapacity);
765
782
766
- ElemTy *newElements = static_cast <ElemTy *>(malloc (newSize));
767
783
if (elements) {
768
- memcpy (newElements, elements, elementCount * sizeof (ElemTy));
784
+ memcpy (newElements->data (), elements->data (),
785
+ elementCount * sizeof (ElemTy));
769
786
FreeListNode::add (&FreeList, elements);
770
787
}
771
788
772
- ElementCapacity = newCapacity;
773
789
Elements.store (newElements, std::memory_order_release);
774
790
return newElements;
775
791
}
@@ -919,16 +935,17 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
919
935
// should not happen often.
920
936
IndexStorage *indices;
921
937
size_t elementCount;
922
- ElemTy *elements;
923
- ElemTy *elements2;
938
+ ElementStorage *elements;
939
+ ElementStorage *elements2;
924
940
do {
925
941
elements = Elements.load (std::memory_order_acquire);
926
942
indices = Indices.load (std::memory_order_acquire);
927
943
elementCount = ElementCount.load (std::memory_order_acquire);
928
944
elements2 = Elements.load (std::memory_order_acquire);
929
945
} while (elements != elements2);
930
946
931
- return Snapshot (this , indices, elements, elementCount);
947
+ ElemTy *elementsPtr = elements ? elements->data () : nullptr ;
948
+ return Snapshot (this , indices, elementsPtr, elementCount);
932
949
}
933
950
934
951
// / Get an element by key, or insert a new element for that key if one is not
@@ -958,8 +975,9 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
958
975
auto indicesCapacityLog2 = indices->CapacityLog2 ;
959
976
auto elementCount = ElementCount.load (std::memory_order_relaxed);
960
977
auto *elements = Elements.load (std::memory_order_relaxed);
978
+ auto *elementsPtr = elements ? elements->data () : nullptr ;
961
979
962
- auto found = this ->find (key, indices, elementCount, elements );
980
+ auto found = this ->find (key, indices, elementCount, elementsPtr );
963
981
if (found.first ) {
964
982
call (found.first , false );
965
983
deallocateFreeListIfSafe ();
@@ -973,15 +991,15 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
973
991
auto emptyCount = indicesCapacity - (elementCount + 1 );
974
992
auto proportion = indicesCapacity / emptyCount;
975
993
if (proportion >= ResizeProportion) {
976
- indices = resize (indices, indicesCapacityLog2, elements );
977
- found = find (key, indices, elementCount, elements );
994
+ indices = resize (indices, indicesCapacityLog2, elementsPtr );
995
+ found = find (key, indices, elementCount, elementsPtr );
978
996
assert (!found.first && " Shouldn't suddenly find the key after rehashing" );
979
997
}
980
998
981
- if (elementCount >= ElementCapacity ) {
999
+ if (!elements || elementCount >= elements-> Capacity ) {
982
1000
elements = resize (elements, elementCount);
983
1001
}
984
- auto *element = &elements[elementCount];
1002
+ auto *element = &elements-> data () [elementCount];
985
1003
986
1004
// Order matters: fill out the element, then update the count,
987
1005
// then update the index.
@@ -1010,7 +1028,6 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
1010
1028
Indices.store (nullptr , std::memory_order_relaxed);
1011
1029
ElementCount.store (0 , std::memory_order_relaxed);
1012
1030
Elements.store (nullptr , std::memory_order_relaxed);
1013
- ElementCapacity = 0 ;
1014
1031
1015
1032
FreeListNode::add (&FreeList, indices);
1016
1033
FreeListNode::add (&FreeList, elements);
0 commit comments