ATLAS Offline Software
Loading...
Searching...
No Matches
ConcurrentToValMap.icc
Go to the documentation of this file.
1/*
2 * Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
3 */
4/**
5 * @file CxxUtils/ConcurrentToValMap.icc
6 * @author scott snyder <snyder@bnl.gov>
7 * @date Jul, 2023
8 * @brief Hash map from pointers/integers to arbitrary objects allowing
9 * concurrent, lockless reads.
10 */
11
12
13namespace CxxUtils {
14
15
16#define T_CONCURRENTTOVALMAP template <class KEY, class VALUE, \
17 template <class> class UPDATER, \
18 class HASHER, class MATCHER, \
19 detail::ConcurrentHashmapVal_t NULLVAL> \
20 requires (detail::IsConcurrentHashmapPayload<KEY> && \
21 detail::IsUpdater<UPDATER> && \
22 detail::IsHash<HASHER, KEY> && \
23 detail::IsBinaryPredicate<MATCHER, KEY>)
24
25#define CONCURRENTTOVALMAP ConcurrentToValMap<KEY, VALUE, UPDATER, HASHER, MATCHER, NULLVAL>
26
27
28/**
29 * @brief Constructor.
30 * @param updater Object used to manage memory
31 * (see comments at the start of the class).
32 * @param capacity The initial table capacity.
33 * (Will be rounded up to a power of two.)
34 * @param ctx Execution context.
35 */
36T_CONCURRENTTOVALMAP
37CONCURRENTTOVALMAP::ConcurrentToValMap (Updater_t&& updater,
38 size_type capacity /*= 64*/,
39 const Context_t& ctx
40 /* = Updater_t::defaultContext()*/)
41 : m_impl (std::move (updater),
42 capacity,
43 Hasher(),
44 Matcher(),
45 ctx)
46{
47}
48
49
50/**
51 * @brief Constructor from another map.
52 * @param updater Object used to manage memory
53 * (see comments at the start of the class).
54 * @param capacity The initial table capacity of the new table.
55 * (Will be rounded up to a power of two.)
56 * @param ctx Execution context.
57 *
58 * (Not really a copy constructor since we need to pass @c updater.)
59 */
60T_CONCURRENTTOVALMAP
61CONCURRENTTOVALMAP::ConcurrentToValMap (const ConcurrentToValMap& other,
62 Updater_t&& updater,
63 size_type capacity /*= 64*/,
64 const Context_t& ctx
65 /*= Updater_t::defaultContext()*/)
66 : m_impl (std::move (updater),
67 capacity,
68 Hasher(),
69 Matcher(),
70 ctx)
71{
72 // not using reference, because our iterator doesn't return a reference
73 for (const auto p : other) {
74 this->put (p.first, std::make_unique<mapped_type> (p.second), ctx);
75 }
76}
77
78
79/**
80 * @brief Constructor from a range.
81 * @param f Start iterator for the range.
82 * @param l End iterator for the range.
83 * @param updater Object used to manage memory
84 * (see comments at the start of the class).
85 * @param capacity The initial table capacity of the new table.
86 * (Will be rounded up to a power of two.)
87 * @param ctx Execution context.
88 *
89 * Constructor from a range of pairs.
90 */
91T_CONCURRENTTOVALMAP
92template <class InputIterator>
93CONCURRENTTOVALMAP::ConcurrentToValMap (InputIterator f,
94 InputIterator l,
95 Updater_t&& updater,
96 size_t capacity /*= 64*/,
97 const Context_t& ctx
98 /*= Updater_t::defaultContext()*/)
99 : m_impl (std::move (updater),
100 capacity,
101 Hasher(),
102 Matcher(),
103 ctx)
104{
105 if constexpr (std::is_rvalue_reference_v<typename InputIterator::reference>)
106 {
107 for (; f != l; ++f) {
108 emplace (f->first, std::move (f->second), ctx);
109 }
110 }
111 else {
112 for (; f != l; ++f) {
113 emplace (f->first, f->second, ctx);
114 }
115 }
116}
117
118
119/**
120 * @brief Destructor.
121 */
122T_CONCURRENTTOVALMAP
123CONCURRENTTOVALMAP::~ConcurrentToValMap()
124{
125 // Need to delete the values that we've stored.
126 auto [begin, end] = m_impl.range();
127 while (begin != end) {
128 if (begin.key() != Impl_t::nullval) {
129 delete mappedAsMapped (begin.value());
130 }
131 begin.next();
132 }
133}
134
135
136/**
137 * @brief Return the number of items currently in the map.
138 */
139T_CONCURRENTTOVALMAP
140inline
141auto CONCURRENTTOVALMAP::size() const -> size_type
142{
143 return m_impl.size();
144}
145
146
147/**
148 * @brief Test if the map is currently empty.
149 */
150T_CONCURRENTTOVALMAP
151inline
152bool CONCURRENTTOVALMAP::empty() const
153{
154 return !m_impl.size();
155}
156
157
158/**
159 * @brief Return the current size (capacity) of the hash table.
160 */
161T_CONCURRENTTOVALMAP
162inline
163auto CONCURRENTTOVALMAP::capacity() const -> size_t
164{
165 return m_impl.capacity();
166}
167
168
169/**
170 * @brief Constructor.
171 * @param it Iterator of the underlying table.
172 */
173T_CONCURRENTTOVALMAP
174inline
175CONCURRENTTOVALMAP::const_iterator::const_iterator (typename Impl_t::const_iterator it)
176 : m_impl (it)
177{
178}
179
180
181/**
182 * @brief Conversion from non-const iterator (for interoperability).
183 * @param other The other iterator.
184 */
185T_CONCURRENTTOVALMAP
186inline
187CONCURRENTTOVALMAP::const_iterator::const_iterator (const iterator& other)
188 : m_impl (other.m_impl)
189{
190}
191
192
193/**
194 * @brief Test if this iterator is valid.
195 *
196 * This should be the same as testing for != end().
197 */
198T_CONCURRENTTOVALMAP
199inline
200auto CONCURRENTTOVALMAP::const_iterator::valid() const -> bool
201{
202 return m_impl.valid();
203}
204
205
206/**
207 * @brief iterator_facade requirement: Increment the iterator.
208 */
209T_CONCURRENTTOVALMAP
210inline
211auto CONCURRENTTOVALMAP::const_iterator::increment() -> void
212{
213 m_impl.next();
214}
215
216
217/**
218 * @brief iterator_facade requirement: Decrement the iterator.
219 */
220T_CONCURRENTTOVALMAP
221inline
222auto CONCURRENTTOVALMAP::const_iterator::decrement() -> void
223{
224 m_impl.prev();
225}
226
227
228/**
229 * @brief iterator_facade requirement: Dereference the iterator.
230 */
231T_CONCURRENTTOVALMAP
232inline
233auto CONCURRENTTOVALMAP::const_iterator::dereference() const
234 -> const const_iterator_value
235{
236 return const_iterator_value (keyAsKey (m_impl.key()),
237 *mappedAsMapped (m_impl.value()));
238}
239
240
241/**
242 * @brief iterator_facade requirement: Equality test.
243 */
244T_CONCURRENTTOVALMAP
245inline
246auto CONCURRENTTOVALMAP::const_iterator::equal (const const_iterator& other) const
247 -> bool
248{
249 return !(m_impl != other.m_impl);
250}
251
252
253/**
254 * @brief iterator_facade requirement: Equality test.
255 */
256T_CONCURRENTTOVALMAP
257inline
258auto CONCURRENTTOVALMAP::const_iterator::equal (const iterator& other) const
259 -> bool
260{
261 return !(m_impl != other.m_impl);
262}
263
264
265/**
266 * @brief Constructor.
267 * @param it Iterator of the underlying table.
268 */
269T_CONCURRENTTOVALMAP
270inline
271CONCURRENTTOVALMAP::iterator::iterator (typename Impl_t::const_iterator it)
272 : m_impl (it)
273{
274}
275
276
277/**
278 * @brief Test if this iterator is valid.
279 *
280 * This should be the same as testing for != end().
281 */
282T_CONCURRENTTOVALMAP
283inline
284auto CONCURRENTTOVALMAP::iterator::valid() const -> bool
285{
286 return m_impl.valid();
287}
288
289
290/**
291 * @brief iterator_facade requirement: Increment the iterator.
292 */
293T_CONCURRENTTOVALMAP
294inline
295auto CONCURRENTTOVALMAP::iterator::increment() -> void
296{
297 m_impl.next();
298}
299
300
301/**
302 * @brief iterator_facade requirement: Decrement the iterator.
303 */
304T_CONCURRENTTOVALMAP
305inline
306auto CONCURRENTTOVALMAP::iterator::decrement() -> void
307{
308 m_impl.prev();
309}
310
311
312/**
313 * @brief iterator_facade requirement: Dereference the iterator.
314 */
315T_CONCURRENTTOVALMAP
316inline
317auto CONCURRENTTOVALMAP::iterator::dereference() const
318 -> const iterator_value
319{
320 return iterator_value (keyAsKey (m_impl.key()),
321 *mappedAsMapped (m_impl.value()));
322}
323
324
325/**
326 * @brief iterator_facade requirement: Equality test.
327 */
328T_CONCURRENTTOVALMAP
329inline
330auto CONCURRENTTOVALMAP::iterator::equal (const iterator& other) const
331 -> bool
332{
333 return !(m_impl != other.m_impl);
334}
335
336
337/**
338 * @brief iterator_facade requirement: Equality test. (Interoperability.)
339 */
340T_CONCURRENTTOVALMAP
341inline
342auto CONCURRENTTOVALMAP::iterator::equal (const const_iterator& other) const
343 -> bool
344{
345 return !(m_impl != other.m_impl);
346}
347
348
349/**
350 * @brief Return an iterator range covering the entire map.
351 */
352T_CONCURRENTTOVALMAP
353auto CONCURRENTTOVALMAP::range() const -> const_iterator_range
354{
355 auto [begin, end] = m_impl.range();
356 return const_iterator_range (begin, end);
357}
358
359
360/**
361 * @brief Return an iterator range covering the entire map.
362 *
363 * The mapped objects must themselves be thread-safe in order to make
364 * any changes to them through the returned iterators.
365 */
366T_CONCURRENTTOVALMAP
367auto CONCURRENTTOVALMAP::range() -> iterator_range
368{
369 auto [begin, end] = m_impl.range();
370 return iterator_range (begin, end);
371}
372
373
374/**
375 * @brief Iterator at the start of the map.
376 */
377T_CONCURRENTTOVALMAP
378inline
379auto CONCURRENTTOVALMAP::begin() const -> const_iterator
380{
381 return const_iterator (m_impl.begin());
382}
383
384
385/**
386 * @brief Iterator at the end of the map.
387 */
388T_CONCURRENTTOVALMAP
389inline
390auto CONCURRENTTOVALMAP::end() const -> const_iterator
391{
392 return const_iterator (m_impl.end());
393}
394
395
396/**
397 * @brief Iterator at the start of the map.
398 *
399 * The mapped objects must themselves be thread-safe in order to make
400 * any changes to them through the returned iterators.
401 */
402T_CONCURRENTTOVALMAP
403inline
404auto CONCURRENTTOVALMAP::begin() -> iterator
405{
406 return iterator (m_impl.begin());
407}
408
409
410/**
411 * @brief Iterator at the end of the map.
412 *
413 * The mapped objects must themselves be thread-safe in order to make
414 * any changes to them through the returned iterators.
415 */
416T_CONCURRENTTOVALMAP
417inline
418auto CONCURRENTTOVALMAP::end() -> iterator
419{
420 return iterator (m_impl.end());
421}
422
423
424/**
425 * @brief Iterator at the start of the map.
426 */
427T_CONCURRENTTOVALMAP
428inline
429auto CONCURRENTTOVALMAP::cbegin() const -> const_iterator
430{
431 return begin();
432}
433
434
435/**
436 * @brief Iterator at the end of the map.
437 */
438T_CONCURRENTTOVALMAP
439inline
440auto CONCURRENTTOVALMAP::cend() const -> const_iterator
441{
442 return end();
443}
444
445
446/**
447 * @brief Test if a key is in the container.
448 * @param key The key to test.
449 */
450T_CONCURRENTTOVALMAP
451inline
452bool CONCURRENTTOVALMAP::contains (const key_type key) const
453{
454 return get(key).valid();
455}
456
457
458/**
459 * @brief Return the number of times a given key is in the container.
460 * @param key The key to test.
461 *
462 * Returns either 0 or 1, depending on whether or not the key is in the map.
463 */
464T_CONCURRENTTOVALMAP
465inline
466auto CONCURRENTTOVALMAP::count (const key_type key) const -> size_type
467{
468 return contains (key) ? 1 : 0;
469}
470
471
472/**
473 * @brief Look up an element in the map.
474 * @param key The element to find.
475 *
476 * Returns either an iterator referencing the found element or end().
477 */
478T_CONCURRENTTOVALMAP
479inline
480auto CONCURRENTTOVALMAP::find (const key_type key) const -> const_iterator
481{
482 return const_iterator (this->get (key));
483}
484
485
486/**
487 * @brief Look up an element in the map.
488 * @param key The element to find.
489 *
490 * Returns either an iterator referencing the found element or end().
491 *
492 * The mapped object must itself be thread-safe in order to make
493 * any changes to it through the returned iterator.
494 */
495T_CONCURRENTTOVALMAP
496inline
497auto CONCURRENTTOVALMAP::find (const key_type key) -> iterator
498{
499 return iterator (this->get (key));
500}
501
502
503/**
504 * @brief Look up an element in the map.
505 * @param key The element to find.
506 *
507 * Returns the value associated with the key.
508 * Throws @c std::out_of_range if the key does not exist in the map.
509 */
510T_CONCURRENTTOVALMAP
511auto CONCURRENTTOVALMAP::at (const key_type key) const -> const mapped_type&
512{
513 typename Impl_t::const_iterator it = this->get (key);
514 if (!it.valid()) {
515 throw std::out_of_range ("ConcurrentToValMap::at");
516 }
517 return *mappedAsMapped (it.value());
518}
519
520
521/**
522 * @brief Look up an element in the map.
523 * @param key The element to find.
524 *
525 * Returns the value associated with the key.
526 * Throws @c std::out_of_range if the key does not exist in the map.
527 *
528 * This returns a non-const reference to the mapped object.
529 * The mapped object must be thread-safe itself in order to safely
530 * make any changes to it.
531 */
532T_CONCURRENTTOVALMAP
533auto CONCURRENTTOVALMAP::at (const key_type key) -> mapped_type&
534{
535 typename Impl_t::const_iterator it = this->get (key);
536 if (!it.valid()) {
537 throw std::out_of_range ("ConcurrentToValMap::at");
538 }
539 return *mappedAsMapped (it.value());
540}
541
542
543/**
544 * @brief Return a range of iterators with entries matching @c key.
545 * @param key The element to find.
546 *
547 * As keys are unique in this container, this is either a single-element
548 * range, or both iterators are equal to end().
549 */
550T_CONCURRENTTOVALMAP
551auto CONCURRENTTOVALMAP::equal_range (const key_type key) const
552 -> std::pair<const_iterator, const_iterator>
553{
554 const_iterator i1 = find (key);
555 const_iterator i2 = i1;
556 if (i2.valid()) {
557 ++i2;
558 }
559 return std::make_pair (i1, i2);
560}
561
562
563/**
564 * @brief Return a range of iterators with entries matching @c key.
565 * @param key The element to find.
566 *
567 * As keys are unique in this container, this is either a single-element
568 * range, or both iterators are equal to end().
569 *
570 * The mapped objects must themselves be thread-safe in order to make
571 * any changes to them through the returned iterators.
572 */
573T_CONCURRENTTOVALMAP
574auto CONCURRENTTOVALMAP::equal_range (const key_type key)
575 -> std::pair<iterator, iterator>
576{
577 iterator i1 = find (key);
578 iterator i2 = i1;
579 if (i2.valid()) {
580 ++i2;
581 }
582 return std::make_pair (i1, i2);
583}
584
585
586/**
587 * @brief Add an element to the map.
588 * @param key The key of the new item to add.
589 * @param val The value of the new item to add.
590 * @param ctx Execution context.
591 *
592 * This will not overwrite an existing entry.
593 * The first element in the returned pair is an iterator referencing
594 * the added item. The second is a flag that is true if a new element
595 * was added.
596 */
597T_CONCURRENTTOVALMAP
598inline
599auto CONCURRENTTOVALMAP::emplace (key_type key,
600 const mapped_type& val,
601 const Context_t& ctx
602 /*= Updater_t::defaultContext()*/)
603 -> std::pair<const_iterator, bool>
604{
605 return put (key, std::make_unique<mapped_type> (val), ctx);
606}
607
608
609/**
610 * @brief Add an element to the map.
611 * @param key The key of the new item to add.
612 * @param val The value of the new item to add.
613 * @param ctx Execution context.
614 *
615 * This will not overwrite an existing entry.
616 * The first element in the returned pair is an iterator referencing
617 * the added item. The second is a flag that is true if a new element
618 * was added.
619 */
620T_CONCURRENTTOVALMAP
621inline
622auto CONCURRENTTOVALMAP::emplace (key_type key,
623 mapped_type&& val,
624 const Context_t& ctx
625 /*= Updater_t::defaultContext()*/)
626 -> std::pair<const_iterator, bool>
627{
628 return put (key, std::make_unique<mapped_type> (std::move (val)), ctx);
629}
630
631
632/**
633 * @brief Add an element to the map.
634 * @param key The key of the new item to add.
635 * @param val The value of the new item to add.
636 * @param ctx Execution context.
637 *
638 * This will not overwrite an existing entry.
639 * The first element in the returned pair is an iterator referencing
640 * the added item. The second is a flag that is true if a new element
641 * was added.
642 */
643T_CONCURRENTTOVALMAP
644inline
645auto CONCURRENTTOVALMAP::emplace (key_type key,
646 std::unique_ptr<mapped_type> val,
647 const Context_t& ctx
648 /*= Updater_t::defaultContext()*/)
649 -> std::pair<const_iterator, bool>
650{
651 return put (key, std::move (val), ctx);
652}
653
654
655/**
656 * @brief Add an element to the map.
657 * @param p The item to add.
658 * Should be a pair where first is the string key
659 * and second is the integer value.
660 * @param ctx Execution context.
661 *
662 * This will not overwrite an existing entry.
663 * The first element in the returned pair is an iterator referencing
664 * the added item. The second is a flag that is true if a new element
665 * was added.
666 */
667T_CONCURRENTTOVALMAP
668template <class PAIR>
669inline
670auto CONCURRENTTOVALMAP::insert (const PAIR& p,
671 const Context_t& ctx /*= Updater_t::defaultContext()*/)
672 -> std::pair<const_iterator, bool>
673{
674 return emplace (p.first, p.second, ctx);
675}
676
677
678/**
679 * @brief Add an element to the map.
680 * @param p The item to add.
681 * Should be a pair where first is the string key
682 * and second is the integer value.
683 * @param ctx Execution context.
684 *
685 * This will not overwrite an existing entry.
686 * The first element in the returned pair is an iterator referencing
687 * the added item. The second is a flag that is true if a new element
688 * was added.
689 */
690T_CONCURRENTTOVALMAP
691template <class PAIR>
692inline
693auto CONCURRENTTOVALMAP::insert (PAIR&& p,
694 const Context_t& ctx /*= Updater_t::defaultContext()*/)
695 -> std::pair<const_iterator, bool>
696{
697 return emplace (p.first, std::move (p.second), ctx);
698}
699
700
701/**
702 * @brief Insert a range of elements to the map.
703 * @param first Start of the range.
704 * @param last End of the range.
705 * @param ctx Execution context.
706 *
707 * The range should be a sequence of pairs where first is the string key
708 * and second is the integer value.
709 */
710T_CONCURRENTTOVALMAP
711template <class InputIterator>
712void CONCURRENTTOVALMAP::insert (InputIterator first, InputIterator last,
713 const Context_t& ctx /*= Updater_t::defaultContext()*/)
714{
715 if constexpr (std::is_rvalue_reference_v<typename InputIterator::reference>)
716 {
717 for (; first != last; ++first) {
718 emplace (first->first, std::move (first->second), ctx);
719 }
720 }
721 else {
722 for (; first != last; ++first) {
723 emplace (first->first, first->second, ctx);
724 }
725 }
726}
727
728
729/**
730 * @brief Increase the table capacity.
731 * @param capacity The new table capacity.
732 * @param ctx Execution context.
733 *
734 * No action will be taken if @c capacity is smaller
735 * than the current capacity.
736 */
737T_CONCURRENTTOVALMAP
738inline
739void CONCURRENTTOVALMAP::reserve (size_type capacity,
740 const Context_t& ctx
741 /*= Updater_t::defaultContext()*/)
742{
743 return m_impl.reserve (capacity, ctx);
744}
745
746
747/**
748 * @brief Increase the table capacity.
749 * @param capacity The new table capacity.
750 *
751 * No action will be taken if @c capacity is smaller
752 * than the current capacity.
753 */
754T_CONCURRENTTOVALMAP
755inline
756void CONCURRENTTOVALMAP::rehash (size_type capacity)
757{
758 return reserve (capacity);
759}
760
761
762/**
763 * @brief Called when this thread is no longer referencing anything
764 * from this container.
765 * @param ctx Execution context.
766 */
767T_CONCURRENTTOVALMAP
768inline
769void CONCURRENTTOVALMAP::quiescent (const Context_t& ctx)
770{
771 return m_impl.quiescent (ctx);
772}
773
774
775/**
776 * @brief Swap this container with another.
777 * @param other The container with which to swap.
778 *
779 * This will also call swap on the Updater object; hence, the Updater
780 * object must also support swap.
781 *
782 * This operation is NOT thread-safe. No other threads may be accessing
783 * either container during this operation.
784 */
785T_CONCURRENTTOVALMAP
786void CONCURRENTTOVALMAP::swap (ConcurrentToValMap& other)
787{
788 m_impl.swap (other.m_impl);
789}
790
791
792/**
793 * @brief Access the Updater instance.
794 */
795T_CONCURRENTTOVALMAP
796auto CONCURRENTTOVALMAP::updater() -> Updater_t&
797{
798 return m_impl.updater();
799}
800
801
802/**
803 * @brief Convert an underlying key value to this type's key value.
804 * @param val The underlying key value.
805 */
806T_CONCURRENTTOVALMAP
807inline
808auto CONCURRENTTOVALMAP::keyAsKey (val_t val) -> key_type
809{
810 return CxxUtils::detail::UIntConv<key_type>::uintToVal (val);
811}
812
813
814/**
815 * @brief Convert this type's key value to an underlying key value.
816 * @param k The key.
817 */
818T_CONCURRENTTOVALMAP
819inline
820auto CONCURRENTTOVALMAP::keyAsVal (key_type k) -> val_t
821{
822 return CxxUtils::detail::UIntConv<key_type>::valToUInt (k);
823}
824
825
826/**
827 * @brief Convert an underlying mapped value a pointer to
828 * this type's mapped value.
829 * @param val The underlying mapped value.
830 */
831T_CONCURRENTTOVALMAP
832inline
833auto CONCURRENTTOVALMAP::mappedAsMapped (val_t val) -> mapped_type*
834{
835 return CxxUtils::detail::UIntConv<mapped_type*>::uintToVal (val);
836}
837
838
839/**
840 * @brief Convert this type's mapped value to an underlying mapped value.
841 * @param val The mapped value.
842 */
843T_CONCURRENTTOVALMAP
844inline
845auto CONCURRENTTOVALMAP::mappedAsVal (mapped_type* val) -> val_t
846{
847 return CxxUtils::detail::UIntConv<mapped_type*>::valToUInt (val);
848}
849
850
851/**
852 * @brief Do a lookup in the table.
853 * @param key The key to look up.
854 *
855 * Returns an iterator of the underlying map pointing at the found
856 * entry or end();
857 */
858T_CONCURRENTTOVALMAP
859auto CONCURRENTTOVALMAP::get (const key_type key) const
860 -> typename Impl_t::const_iterator
861{
862 val_t kval = keyAsVal (key);
863 size_t hash = m_impl.hasher() (kval);
864 return m_impl.get (keyAsVal(key), hash);
865}
866
867
868/**
869 * @brief Insert an entry in the table.
870 * @param key The key of the new item to add.
871 * @param val The new mapped value to add.
872 * @param ctx Execution context.
873 *
874 * The first element in the returned pair is an iterator referencing
875 * the added item. The second is a flag that is true if a new element
876 * was added.
877 */
878T_CONCURRENTTOVALMAP
879auto CONCURRENTTOVALMAP::put (const key_type key,
880 std::unique_ptr<mapped_type> val,
881 const Context_t& ctx /*= Updater_t::defaultContext()*/)
882 -> std::pair<const_iterator, bool>
883{
884 val_t kval = keyAsVal (key);
885 size_t hash = m_impl.hasher() (kval);
886 auto [it, flag] = m_impl.put (kval, hash,
887 mappedAsVal (val.get()),
888 false, ctx);
889 if (flag) val.release();
890 return std::make_pair (const_iterator (it), flag);
891}
892
893
894#undef T_CONCURRENTTOVALMAP
895#undef CONCURRENTTOVALMAP
896
897
898} // namespace CxxUtils