@@ -593,6 +593,51 @@ struct ConcurrentReadableHashMap {
593
593
}
594
594
}
595
595
596
+ // Common implementation for `getOrInsert` and `GetOrInsertManyScope`
597
+ template <class KeyTy , typename Call>
598
+ void getOrInsertExternallyLocked (KeyTy key, const Call &call) {
599
+ auto indices = IndexStorage{Indices.load (std::memory_order_relaxed)};
600
+ auto indicesCapacityLog2 = indices.getCapacityLog2 ();
601
+ auto elementCount = ElementCount.load (std::memory_order_relaxed);
602
+ auto *elements = Elements.load (std::memory_order_relaxed);
603
+ auto *elementsPtr = elements ? elements->data () : nullptr ;
604
+
605
+
606
+ auto found = this ->find (key, indices, elementCount, elementsPtr);
607
+ if (found.first ) {
608
+ call (found.first , false );
609
+ return ;
610
+ }
611
+
612
+ auto indicesCapacity = 1UL << indicesCapacityLog2;
613
+
614
+ // The number of slots in use is elementCount + 1, since the capacity also
615
+ // takes a slot.
616
+ auto emptyCount = indicesCapacity - (elementCount + 1 );
617
+ auto proportion = indicesCapacity / emptyCount;
618
+ if (proportion >= ResizeProportion) {
619
+ indices = resize (indices, indicesCapacityLog2, elementsPtr);
620
+ found = find (key, indices, elementCount, elementsPtr);
621
+ assert (!found.first && " Shouldn't suddenly find the key after rehashing" );
622
+ }
623
+
624
+ if (!elements || elementCount >= elements->Capacity ) {
625
+ elements = resize (elements, elementCount);
626
+ }
627
+ auto *element = &elements->data ()[elementCount];
628
+
629
+ // Order matters: fill out the element, then update the count,
630
+ // then update the index.
631
+ bool keep = call (element, true );
632
+ if (keep) {
633
+ assert (hash_value (key) == hash_value (*element) &&
634
+ " Element must have the same hash code as its key." );
635
+ ElementCount.store (elementCount + 1 , std::memory_order_release);
636
+ indices.storeIndexAt (&Indices, elementCount + 1 , found.second ,
637
+ std::memory_order_release);
638
+ }
639
+ }
640
+
596
641
public:
597
642
// Implicitly trivial constructor/destructor.
598
643
ConcurrentReadableHashMap () = default ;
@@ -684,6 +729,48 @@ struct ConcurrentReadableHashMap {
684
729
return Snapshot (this , indices, elementsPtr, elementCount);
685
730
}
686
731
732
+ // / A wrapper that allows performing several `getOrInsert` operations under
733
+ // / the same lock.
734
+ class GetOrInsertManyScope {
735
+ GetOrInsertManyScope () = delete ;
736
+ GetOrInsertManyScope (const GetOrInsertManyScope &) = delete ;
737
+ GetOrInsertManyScope &operator =(const GetOrInsertManyScope &) = delete ;
738
+ GetOrInsertManyScope (GetOrInsertManyScope &&) = delete ;
739
+ GetOrInsertManyScope &operator =(GetOrInsertManyScope &&) = delete ;
740
+
741
+ ConcurrentReadableHashMap ⤅
742
+
743
+ public:
744
+ GetOrInsertManyScope (ConcurrentReadableHashMap &map) : Map(map) {
745
+ Map.WriterLock .lock ();
746
+ }
747
+
748
+ ~GetOrInsertManyScope () {
749
+ Map.deallocateFreeListIfSafe ();
750
+ Map.WriterLock .unlock ();
751
+ }
752
+
753
+ // / Get an element by key, or insert a new element for that key if one is
754
+ // / not already present. Invoke `call` with the pointer to the element.
755
+ // /
756
+ // / `call` is passed the following parameters:
757
+ // / - `element`: the pointer to the element corresponding to `key`
758
+ // / - `created`: true if the element is newly created, false if it already
759
+ // / exists
760
+ // / `call` returns a `bool`. When `created` is `true`, the return values
761
+ // / mean:
762
+ // / - `true` the new entry is to be kept
763
+ // / - `false` indicates that the new entry is discarded
764
+ // / If the new entry is kept, then the new element MUST be initialized, and
765
+ // / have a hash value that matches the hash value of `key`.
766
+ // /
767
+ // / The return value is ignored when `created` is `false`.
768
+ template <class KeyTy , typename Call>
769
+ void getOrInsert (KeyTy key, const Call &call) {
770
+ Map.getOrInsertExternallyLocked (key, call);
771
+ }
772
+ };
773
+
687
774
// / Get an element by key, or insert a new element for that key if one is not
688
775
// / already present. Invoke `call` with the pointer to the element. BEWARE:
689
776
// / `call` is invoked with the internal writer lock held, keep work to a
@@ -703,48 +790,7 @@ struct ConcurrentReadableHashMap {
703
790
template <class KeyTy , typename Call>
704
791
void getOrInsert (KeyTy key, const Call &call) {
705
792
typename MutexTy::ScopedLock guard (WriterLock);
706
-
707
- auto indices = IndexStorage{Indices.load (std::memory_order_relaxed)};
708
- auto indicesCapacityLog2 = indices.getCapacityLog2 ();
709
- auto elementCount = ElementCount.load (std::memory_order_relaxed);
710
- auto *elements = Elements.load (std::memory_order_relaxed);
711
- auto *elementsPtr = elements ? elements->data () : nullptr ;
712
-
713
- auto found = this ->find (key, indices, elementCount, elementsPtr);
714
- if (found.first ) {
715
- call (found.first , false );
716
- deallocateFreeListIfSafe ();
717
- return ;
718
- }
719
-
720
- auto indicesCapacity = 1UL << indicesCapacityLog2;
721
-
722
- // The number of slots in use is elementCount + 1, since the capacity also
723
- // takes a slot.
724
- auto emptyCount = indicesCapacity - (elementCount + 1 );
725
- auto proportion = indicesCapacity / emptyCount;
726
- if (proportion >= ResizeProportion) {
727
- indices = resize (indices, indicesCapacityLog2, elementsPtr);
728
- found = find (key, indices, elementCount, elementsPtr);
729
- assert (!found.first && " Shouldn't suddenly find the key after rehashing" );
730
- }
731
-
732
- if (!elements || elementCount >= elements->Capacity ) {
733
- elements = resize (elements, elementCount);
734
- }
735
- auto *element = &elements->data ()[elementCount];
736
-
737
- // Order matters: fill out the element, then update the count,
738
- // then update the index.
739
- bool keep = call (element, true );
740
- if (keep) {
741
- assert (hash_value (key) == hash_value (*element) &&
742
- " Element must have the same hash code as its key." );
743
- ElementCount.store (elementCount + 1 , std::memory_order_release);
744
- indices.storeIndexAt (&Indices, elementCount + 1 , found.second ,
745
- std::memory_order_release);
746
- }
747
-
793
+ getOrInsertExternallyLocked (key, call);
748
794
deallocateFreeListIfSafe ();
749
795
}
750
796
0 commit comments