@@ -416,6 +416,35 @@ class ConcurrentMap
416
416
}
417
417
};
418
418
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
+ };
419
448
420
449
// / An append-only array that can be read without taking locks. Writes
421
450
// / are still locked and serialized, but only with respect to other
@@ -454,8 +483,8 @@ template <class ElemTy> struct ConcurrentReadableArray {
454
483
std::atomic<size_t > ReaderCount;
455
484
std::atomic<Storage *> Elements;
456
485
Mutex WriterLock;
457
- std::vector<Storage *> FreeList;
458
-
486
+ ConcurrentFreeListNode * FreeList{ nullptr } ;
487
+
459
488
void incrementReaders () {
460
489
ReaderCount.fetch_add (1 , std::memory_order_acquire);
461
490
}
@@ -465,10 +494,9 @@ template <class ElemTy> struct ConcurrentReadableArray {
465
494
}
466
495
467
496
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
+ });
472
500
}
473
501
474
502
public:
@@ -522,7 +550,7 @@ template <class ElemTy> struct ConcurrentReadableArray {
522
550
if (storage) {
523
551
std::copy (storage->data (), storage->data () + count, newStorage->data ());
524
552
newStorage->Count .store (count, std::memory_order_release);
525
- FreeList. push_back ( storage);
553
+ ConcurrentFreeListNode::add (&FreeList, storage);
526
554
}
527
555
528
556
storage = newStorage;
@@ -797,28 +825,6 @@ struct ConcurrentReadableHashMap {
797
825
ElemTy *data () { return &Elem; }
798
826
};
799
827
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
-
822
828
// / The number of readers currently active, equal to the number of snapshot
823
829
// / objects currently alive.
824
830
std::atomic<uint32_t > ReaderCount{0 };
@@ -840,7 +846,7 @@ struct ConcurrentReadableHashMap {
840
846
MutexTy WriterLock;
841
847
842
848
// / The list of pointers to be freed once no readers are active.
843
- FreeListNode *FreeList{nullptr };
849
+ ConcurrentFreeListNode *FreeList{nullptr };
844
850
845
851
void incrementReaders () {
846
852
ReaderCount.fetch_add (1 , std::memory_order_acquire);
@@ -854,7 +860,7 @@ struct ConcurrentReadableHashMap {
854
860
// / there are active readers, do nothing.
855
861
void deallocateFreeListIfSafe () {
856
862
if (ReaderCount.load (std::memory_order_seq_cst) == 0 )
857
- FreeListNode ::freeAll (&FreeList);
863
+ ConcurrentFreeListNode ::freeAll (&FreeList, free );
858
864
}
859
865
860
866
// / Grow the elements array, adding the old array to the free list and
@@ -868,7 +874,7 @@ struct ConcurrentReadableHashMap {
868
874
if (elements) {
869
875
memcpy (newElements->data (), elements->data (),
870
876
elementCount * sizeof (ElemTy));
871
- FreeListNode ::add (&FreeList, elements);
877
+ ConcurrentFreeListNode ::add (&FreeList, elements);
872
878
}
873
879
874
880
// Use seq_cst here to ensure that the subsequent load of ReaderCount is
@@ -916,7 +922,7 @@ struct ConcurrentReadableHashMap {
916
922
Indices.store (newIndices.Value , std::memory_order_seq_cst);
917
923
918
924
if (auto *ptr = indices.pointer ())
919
- FreeListNode ::add (&FreeList, ptr);
925
+ ConcurrentFreeListNode ::add (&FreeList, ptr);
920
926
921
927
return newIndices;
922
928
}
@@ -1122,8 +1128,8 @@ struct ConcurrentReadableHashMap {
1122
1128
Elements.store (nullptr , std::memory_order_relaxed);
1123
1129
1124
1130
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);
1127
1133
1128
1134
deallocateFreeListIfSafe ();
1129
1135
}
0 commit comments