@@ -416,6 +416,35 @@ class ConcurrentMap
416416 }
417417};
418418
419+ // / A simple linked list representing pointers that need to be freed. This is
420+ // / not a concurrent data structure, just a bit of support used in the types
421+ // / below.
422+ struct ConcurrentFreeListNode {
423+ ConcurrentFreeListNode *Next;
424+ void *Ptr;
425+
426+ static void add (ConcurrentFreeListNode **head, void *ptr) {
427+ auto *newNode = reinterpret_cast <ConcurrentFreeListNode *>(
428+ malloc (sizeof (ConcurrentFreeListNode)));
429+ newNode->Next = *head;
430+ newNode->Ptr = ptr;
431+ *head = newNode;
432+ }
433+
434+ // / Free all nodes in the free list, resetting `head` to `NULL`. Calls
435+ // / `FreeFn` on the Ptr field of every node.
436+ template <typename FreeFn>
437+ static void freeAll (ConcurrentFreeListNode **head, const FreeFn &freeFn) {
438+ auto *node = *head;
439+ while (node) {
440+ auto *next = node->Next ;
441+ freeFn (node->Ptr );
442+ free (node);
443+ node = next;
444+ }
445+ *head = nullptr ;
446+ }
447+ };
419448
420449// / An append-only array that can be read without taking locks. Writes
421450// / are still locked and serialized, but only with respect to other
@@ -454,8 +483,8 @@ template <class ElemTy> struct ConcurrentReadableArray {
454483 std::atomic<size_t > ReaderCount;
455484 std::atomic<Storage *> Elements;
456485 Mutex WriterLock;
457- std::vector<Storage *> FreeList;
458-
486+ ConcurrentFreeListNode * FreeList{ nullptr } ;
487+
459488 void incrementReaders () {
460489 ReaderCount.fetch_add (1 , std::memory_order_acquire);
461490 }
@@ -465,10 +494,9 @@ template <class ElemTy> struct ConcurrentReadableArray {
465494 }
466495
467496 void deallocateFreeList () {
468- for (Storage *storage : FreeList)
469- storage->deallocate ();
470- FreeList.clear ();
471- FreeList.shrink_to_fit ();
497+ ConcurrentFreeListNode::freeAll (&FreeList, [](void *ptr) {
498+ reinterpret_cast <Storage *>(ptr)->deallocate ();
499+ });
472500 }
473501
474502public:
@@ -522,7 +550,7 @@ template <class ElemTy> struct ConcurrentReadableArray {
522550 if (storage) {
523551 std::copy (storage->data (), storage->data () + count, newStorage->data ());
524552 newStorage->Count .store (count, std::memory_order_release);
525- FreeList. push_back ( storage);
553+ ConcurrentFreeListNode::add (&FreeList, storage);
526554 }
527555
528556 storage = newStorage;
@@ -797,28 +825,6 @@ struct ConcurrentReadableHashMap {
797825 ElemTy *data () { return &Elem; }
798826 };
799827
800- // / A simple linked list representing pointers that need to be freed.
801- struct FreeListNode {
802- FreeListNode *Next;
803- void *Ptr;
804-
805- static void add (FreeListNode **head, void *ptr) {
806- auto *newNode = new FreeListNode{*head, ptr};
807- *head = newNode;
808- }
809-
810- static void freeAll (FreeListNode **head) {
811- auto *node = *head;
812- while (node) {
813- auto *next = node->Next ;
814- free (node->Ptr );
815- delete node;
816- node = next;
817- }
818- *head = nullptr ;
819- }
820- };
821-
822828 // / The number of readers currently active, equal to the number of snapshot
823829 // / objects currently alive.
824830 std::atomic<uint32_t > ReaderCount{0 };
@@ -840,7 +846,7 @@ struct ConcurrentReadableHashMap {
840846 MutexTy WriterLock;
841847
842848 // / The list of pointers to be freed once no readers are active.
843- FreeListNode *FreeList{nullptr };
849+ ConcurrentFreeListNode *FreeList{nullptr };
844850
845851 void incrementReaders () {
846852 ReaderCount.fetch_add (1 , std::memory_order_acquire);
@@ -854,7 +860,7 @@ struct ConcurrentReadableHashMap {
854860 // / there are active readers, do nothing.
855861 void deallocateFreeListIfSafe () {
856862 if (ReaderCount.load (std::memory_order_seq_cst) == 0 )
857- FreeListNode ::freeAll (&FreeList);
863+ ConcurrentFreeListNode ::freeAll (&FreeList, free );
858864 }
859865
860866 // / Grow the elements array, adding the old array to the free list and
@@ -868,7 +874,7 @@ struct ConcurrentReadableHashMap {
868874 if (elements) {
869875 memcpy (newElements->data (), elements->data (),
870876 elementCount * sizeof (ElemTy));
871- FreeListNode ::add (&FreeList, elements);
877+ ConcurrentFreeListNode ::add (&FreeList, elements);
872878 }
873879
874880 // Use seq_cst here to ensure that the subsequent load of ReaderCount is
@@ -916,7 +922,7 @@ struct ConcurrentReadableHashMap {
916922 Indices.store (newIndices.Value , std::memory_order_seq_cst);
917923
918924 if (auto *ptr = indices.pointer ())
919- FreeListNode ::add (&FreeList, ptr);
925+ ConcurrentFreeListNode ::add (&FreeList, ptr);
920926
921927 return newIndices;
922928 }
@@ -1122,8 +1128,8 @@ struct ConcurrentReadableHashMap {
11221128 Elements.store (nullptr , std::memory_order_relaxed);
11231129
11241130 if (auto *ptr = indices.pointer ())
1125- FreeListNode ::add (&FreeList, ptr);
1126- FreeListNode ::add (&FreeList, elements);
1131+ ConcurrentFreeListNode ::add (&FreeList, ptr);
1132+ ConcurrentFreeListNode ::add (&FreeList, elements);
11271133
11281134 deallocateFreeListIfSafe ();
11291135 }
0 commit comments