ATLAS Offline Software
ConcurrentRangeMap.icc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3 */
4 /**
5  * @file CxxUtils/ConcurrentRangeMap.icc
6  * @author scott snyder <snyder@bnl.gov>
7  * @date Nov, 2017
8  * @brief Map from range to payload object, allowing concurrent, lockless reads.
9  */
10 
11 
12 namespace CxxUtils {
13 
14 
15 /**
16  * @brief Constructor.
17  * @param delfcn Function to delete a payload object immediately.
18  */
19 template <class T, class CONTEXT>
20 IRangeMapPayloadDeleter<T, CONTEXT>::IRangeMapPayloadDeleter
21  (delete_function* delfcn)
22  : m_delfcn (delfcn)
23 {
24  // cppcheck-suppress missingReturn; false positive
25 }
26 
27 
28 /**
29  * @brief Return a function to delete a payload object immediately.
30  */
31 template <class T, class CONTEXT>
32 inline
33 typename IRangeMapPayloadDeleter<T, CONTEXT>::delete_function*
34 IRangeMapPayloadDeleter<T, CONTEXT>::delfcn() const
35 {
36  return m_delfcn;
37 }
38 
39 
40 //*****************************************************************************
41 
42 
43 #define T_CONCURRENTRANGEMAP template <class RANGE, class KEY, class T, class COMPARE, template <class> class UPDATER> \
44  ATH_REQUIRES (detail::IsUpdater<UPDATER> && \
45  detail::IsConcurrentRangeCompare<COMPARE, RANGE, KEY, typename UPDATER<int>::Context_t>)
46 
47 #define CONCURRENTRANGEMAP ConcurrentRangeMap<RANGE, KEY, T, COMPARE, UPDATER>
48 
49 
50 /**
51  * @brief Constructor.
52  * @param capacity Size of the data vector to allocate.
53  */
54 T_CONCURRENTRANGEMAP
55 CONCURRENTRANGEMAP::Impl::Impl (size_t capacity /*= 10*/)
56  : m_data (capacity)
57 {
58 }
59 
60 
61 /**
62  * @brief Return a pointer to the start of the data vector.
63  */
64 T_CONCURRENTRANGEMAP
65 typename CONCURRENTRANGEMAP::value_type*
66 CONCURRENTRANGEMAP::Impl::data()
67 {
68  return m_data.data();
69 }
70 
71 
72 /**
73  * @brief Return the size of the current data vector.
74  */
75 T_CONCURRENTRANGEMAP
76 size_t
77 CONCURRENTRANGEMAP::Impl::capacity() const
78 {
79  return m_data.capacity();
80 }
81 
82 
83 /**
84  * @brief Constructor.
85  * @param updater Object used to manage memory
86  * (see comments at the start of the class).
87  * @param payloadDeleter Object for deleting payload objects.
88  * This is owned via a @c shared_ptr.
89  * @param capacity Initial capacity of the map.
90  * @param compare Comparison object.
91  */
92 T_CONCURRENTRANGEMAP
93 CONCURRENTRANGEMAP::ConcurrentRangeMap (Updater_t&& updater,
94  std::shared_ptr<IPayloadDeleter> payloadDeleter,
95  size_t capacity /*= 16*/,
96  const COMPARE& compare /*= COMPARE()*/)
97 
98  : m_updater (std::move (updater)),
99  m_compare (compare),
100  m_payloadDeleter (payloadDeleter),
101  m_nInserts (0),
102  m_maxSize (0)
103 {
104  auto impl = std::make_unique<Impl> (capacity);
105  value_type* data = impl->data();
106  installImpl (std::move (impl),
107  data, data,
108  Updater_t::defaultContext());
109 }
110 
111 
112 /**
113  * @brief Destructor.
114  *
115  * Clean up any remaining payload objects.
116  */
117 T_CONCURRENTRANGEMAP
118 CONCURRENTRANGEMAP::~ConcurrentRangeMap()
119 {
120  value_type* last = m_last;
121  if (last) {
122  delete_function* delfcn = m_payloadDeleter->delfcn();
123  for (value_type* p = m_begin; p <= m_last; ++p) {
124  delfcn (p->second);
125  }
126 
127  }
128 }
129 
130 
131 /**
132  * @brief Return a reference to the payload deleter object.
133  */
134 T_CONCURRENTRANGEMAP
135 inline
136 typename CONCURRENTRANGEMAP::IPayloadDeleter& CONCURRENTRANGEMAP::deleter()
137 {
138  return *m_payloadDeleter;
139 }
140 
141 
142 /**
143  * @brief Return a reference to the payload deleter object.
144  */
145 T_CONCURRENTRANGEMAP
146 inline
147 const typename CONCURRENTRANGEMAP::IPayloadDeleter&
148 CONCURRENTRANGEMAP::deleter() const
149 {
150  return *m_payloadDeleter;
151 }
152 
153 
154 /**
155  * @brief Search for the first item less than or equal to KEY.
156  * @param key The key to search for.
157  * @returns The value, or nullptr if not found.
158  */
159 T_CONCURRENTRANGEMAP
160 inline
161 typename CONCURRENTRANGEMAP::const_iterator
162 CONCURRENTRANGEMAP::find (const key_query_type& key) const
163 {
164  // Return right away if the map's empty;
165  const_iterator last = m_last;
166  if (!last) return nullptr;
167 
168  // Check the last value.
169  if (!m_compare (key, last->first)) {
170  return last;
171  }
172 
173  // Do a binary search to find the proper position.
174  const_iterator begin = getBegin (last);
175  if (!last) return nullptr;
176  const_iterator pos = std::upper_bound (begin, last+1, key,
177  [this](const key_query_type& key2,
178  const value_type& v)
179  { return m_compare (key2,v.first); } );
180 
181  // Fail if it would be before the first value.
182  if (pos == begin) return nullptr;
183 
184  // Proper place.
185  return pos-1;
186 }
187 
188 
189 /**
190  * @brief Add a new element to the map.
191  * @param range Validity range for this element.
192  * @param ptr Payload for this element.
193  * @param tryExtend If true, then allow an existing range to be extended
194  * (see below).
195  * @param ctx Execution context.
196  *
197  * Returns SUCCESS if the new element was successfully inserted.
198  * Returns DUPLICATE if the range compared equal to an existing one.
199  * In that case, no new element is inserted (and @c ptr gets deleted).
200  * Returns EXTEND if the range of the last element was extended to @c range.
201  * This happens if @c tryExtend is true, @c range is equal
202  * to the range of the last element (according to @c m_compare),
203  * and the range can be extended according to @c extendRange.
204  * (This will generally mean that the start time of @c range
205  * matches the last range, and end time of @c range is after
206  * the end time of the last range.) In this case, again no
207  * new element is inserted and @c ptr is deleted.
208  * Returns OVERLAP if the range of the new element overlaps
209  * an existing element (the new element is still inserted).
210  */
211 T_CONCURRENTRANGEMAP
212 typename CONCURRENTRANGEMAP::EmplaceResult
213 CONCURRENTRANGEMAP::emplace (const RANGE& range,
214  payload_unique_ptr ptr,
215  bool tryExtend /*= false*/,
216  const typename Updater_t::Context_t& ctx
217  /*= Updater_t::defaultContext()*/)
218 {
219  lock_t lock (m_mutex);
220 
221  value_type* last = m_last;
222  value_type* begin = m_begin;
223 
224  // Check if the element to be inserted is greater than all existing entries.
225  bool pastEnd = (!last || m_compare (last->first, range));
226 
227  // See if we want to extend the last entry. Also check for duplicates.
228  if (last && !pastEnd) {
229  RANGE extendedRange = last->first;
230  int flag = m_compare.extendRange (extendedRange, range);
231  if (tryExtend && flag > 0 && extendImpl (lock, extendedRange, ctx) > 0) {
232  return EmplaceResult::EXTENDED;
233  }
234  if (flag < 0) {
235  return EmplaceResult::DUPLICATE;
236  }
237  }
238 
239  // Can we add this to the end?
240  // There has to be room for another, and either the container must be empty,
241  // or the new element must be greater than the current last one.
242  value_type* end = last ? last+1 : begin;
243  if (pastEnd && end < m_impl->data() + m_impl->capacity())
244  {
245  // Yes, we can add it to the end.
246  // Check for overlap with the previous range.
247  EmplaceResult ret = EmplaceResult::SUCCESS;
248  RANGE newRange = range;
249  if (end > begin) {
250  int flag = m_compare.overlap (ctx, (end-1)->first, newRange);
251  if (flag < 0) {
252  return EmplaceResult::DUPLICATE;
253  }
254  else if (flag > 0) {
255  ret = EmplaceResult::OVERLAP;
256  }
257  }
258 
259  // Copy the data to the container.
260  end->first = newRange;
261  end->second = ptr.release();
262 
263  std::atomic_thread_fence (std::memory_order_seq_cst);
264 
265  // Update the last pointer.
266  m_last = end;
267  // Now the new element is visible to other threads.
268  ++m_nInserts;
269  m_maxSize = std::max (m_maxSize, static_cast<size_t> (end+1 - begin));
270 
271  return ret;
272  }
273 
274  // No --- need to make a new implementation object and copy.
275  // Make the new one bigger, if needed.
276  int new_capacity = m_impl->capacity();
277  int old_size = end-begin;
278  if (old_size == 0) old_size = 1;
279  if (old_size > new_capacity/2) {
280  new_capacity = old_size*2;
281  }
282 
283  EmplaceResult ret = EmplaceResult::SUCCESS;
284 
285  // Allocate the new object.
286  auto new_impl = std::make_unique<Impl> (new_capacity);
287  value_type* new_begin = new_impl->data();
288  value_type* new_end = new_begin;
289 
290  // Copy the data, adding the new item at the proper place.
291  // Separate out the case where the new item goes at the end,
292  // since we can do that faster.
293  if (pastEnd) {
294  // Check for overlap with the previous range.
295  RANGE newRange = range;
296  int flag = 0;
297  if (end > begin) {
298  flag = m_compare.overlap (ctx, (end-1)->first, newRange);
299  }
300  if (flag < 0) {
301  ret = EmplaceResult::DUPLICATE;
302  }
303  else {
304  if (flag > 0) {
305  ret = EmplaceResult::OVERLAP;
306  }
307 
308  new_end = std::copy (begin, end, new_end);
309  new_end->first = newRange;
310  new_end->second = ptr.release();
311 
312  ++new_end;
313  }
314  }
315  else {
316  // Otherwise we need to search for the proper place to insert the new entry.
317  RANGE newRange = range;
318  for (; begin < end; *new_end++ = *begin++) {
319  if (ptr && m_compare (newRange, begin->first)) {
320  // Check for overlap / duplicate
321  if (begin > m_begin) {
322  int flag = m_compare.overlap (ctx, (begin-1)->first, newRange);
323  if (flag > 0) {
324  ret = EmplaceResult::OVERLAP;
325  // The range start may have been adjusted forward. Make sure
326  // that we're still at the insertion point.
327  if (!m_compare (newRange, begin->first)) {
328  continue;
329  }
330  }
331  else if (flag < 0) {
332  // Duplicate entry; fail. Everything allocated is held by unique_ptr,
333  // so should be cleaned up properly.
334  return EmplaceResult::DUPLICATE;
335  }
336  }
337 
338  int flag = m_compare.overlap (ctx, begin->first, newRange);
339  if (flag > 0) {
340  ret = EmplaceResult::OVERLAP;
341  // The range start may have been adjusted forward. Make sure
342  // that we're still at the insertion point.
343  if (!m_compare (newRange, begin->first)) {
344  continue;
345  }
346  }
347  else if (flag < 0) {
348  // Duplicate entry; fail. Everything allocated is held by unique_ptr,
349  // so should be cleaned up properly.
350  return EmplaceResult::DUPLICATE;
351  }
352 
353  new_end->first = newRange;
354  new_end->second = ptr.release();
355  ++new_end;
356  }
357  }
358 
359  if (ptr) {
360  // Possible to get here if overlap() moved the start of the range
361  // forward past all existing ranges.
362  new_end->first = newRange;
363  new_end->second = ptr.release();
364  ++new_end;
365  }
366  }
367 
368  // Install the new implementation.
369  installImpl (std::move (new_impl), new_begin, new_end, ctx);
370 
371  ++m_nInserts;
372  m_maxSize = std::max (m_maxSize, static_cast<size_t> (new_end - new_begin));
373  return ret;
374 }
375 
376 
377 /**
378  * @brief Erase the first item less than or equal to KEY.
379  * @param key The key to search erase.
380  * @param ctx Execution context.
381  */
382 T_CONCURRENTRANGEMAP
383 void
384 CONCURRENTRANGEMAP::erase (const key_query_type& key,
385  const typename Updater_t::Context_t& ctx
386  /*= Updater_t::defaultContext()*/)
387 {
388  lock_t lock (m_mutex);
389 
390  // Return if the container's empty.
391  value_type* last = m_last;
392  if (last == nullptr) return;
393 
394  value_type* begin = m_begin;
395  value_type* end = last+1;
396 
397  // Don't do anything if key is before the first element.
398  if (m_compare (key, begin->first)) return;
399 
400  // Is the first element the one we're deleting?
401  if (begin == last || m_compare (key, (begin+1)->first)) {
402  // Yes --- remember the payload for deletion.
403  // (Don't actually queue it for deletion until the pointers have
404  // been updated.)
405  mapped_type todel = begin->second;
406  ++begin;
407 
408  // Make the update visible to other threads.
409  // If we need to update both pointers, then clear m_begin first.
410  if (begin == end) {
411  m_begin = nullptr;
412  m_last = nullptr;
413  }
414  m_begin = begin;
415  m_payloadDeleter->discard (todel);
416  return;
417  }
418 
419  // Need to make a new implementation object and copy data.
420  size_t capacity = m_impl->capacity();
421  auto new_impl = std::make_unique<Impl> (capacity);
422  value_type* new_begin = new_impl->data();
423  value_type* new_end = new_begin;
424 
425  // Copy the data, skipping the object to be deleted.
426  while (begin < end-1 && !m_compare (key, (begin+1)->first)) {
427  *new_end++ = *begin++;
428  }
429  mapped_type todel = begin->second;
430  ++begin;
431  while (begin < end) {
432  *new_end++ = *begin++;
433  }
434 
435  // Install the new implementation.
436  installImpl (std::move (new_impl), new_begin, new_end, ctx);
437  m_payloadDeleter->discard (todel);
438 }
439 
440 
441 /**
442  * @brief Extend the range of the last entry of the map.
443  * @param newRange New range to use for the last entry.
444  * @param ctx Execution context.
445  *
446  * The range of the last entry in the map is updated to @c newRange.
447  * Does nothing if the map is empty.
448  * This will make a new version of implementation data.
449  *
450  * The semantics of what it means to extend a range are given by the
451  * @c extendRange method of the @c COMPARE object (see above).
452  *
453  * Returns -1 if there was an error; 1 if the last range was extended;
454  * and 0 if nothing was changed.
455  */
456 T_CONCURRENTRANGEMAP
457 int
458 CONCURRENTRANGEMAP::extendLastRange (const RANGE& newRange,
459  const typename Updater_t::Context_t& ctx
460  /*= Updater_t::defaultContext()*/)
461 {
462  lock_t lock (m_mutex);
463  value_type* last = m_last;
464  if (!last) {
465  return -1;
466  }
467  RANGE extendedRange = last->first;
468  int flag = m_compare.extendRange (extendedRange, newRange);
469  if (flag > 0) {
470  return extendImpl (lock, extendedRange, ctx);
471  }
472  if (flag == 0) {
473  return -1;
474  }
475  return 0;
476 }
477 
478 
479 /**
480  * @brief Update all range objects.
481  * @param rangeUpater Functional to call on each range object.
482  * @param ctx Execution context.
483  *
484  * This will iterate through the list of entries and call @c rangeUpdater
485  * on the @c RANGE part of each. Be careful: rangeUpdater must not
486  * change any part of the range which might affect the sorting
487  * of entries.
488  */
489 T_CONCURRENTRANGEMAP
490 void
491 CONCURRENTRANGEMAP::updateRanges (std::function<void (RANGE&)> rangeUpdater,
492  const typename Updater_t::Context_t& ctx
493  /*= Updater_t::defaultContext()*/)
494 {
495  lock_t lock (m_mutex);
496 
497  // Return if the container's empty.
498  value_type* last = m_last;
499  if (last == nullptr) return;
500 
501  // Make a new implementation object and copy data.
502  size_t capacity = m_impl->capacity();
503  auto new_impl = std::make_unique<Impl> (capacity);
504  value_type* new_begin = new_impl->data();
505  value_type* new_end = new_begin;
506  value_type* begin = m_begin;
507  value_type* end = m_last+1;
508  new_end = std::copy (begin, end, new_end);
509 
510  // Update ranges in the new copy.
511  for (value_type* v = new_begin; v != new_end; ++v) {
512  rangeUpdater (v->first);
513  }
514 
515  // Install the new implementation.
516  installImpl (std::move (new_impl), new_begin, new_end, ctx);
517 }
518 
519 
520 /**
521  * @brief Extend the range of the last entry of the map.
522  * @param Lock object.
523  * @param extendedRange New range to use for the last entry.
524  * @param ctx Execution context.
525  *
526  * Implementation of @c extendLastRange; see there for details.
527  * Must be called with the lock held.
528  */
529 T_CONCURRENTRANGEMAP
530 int
531 CONCURRENTRANGEMAP::extendImpl (lock_t& /*lock*/,
532  const RANGE& extendedRange,
533  const typename Updater_t::Context_t& ctx)
534 {
535  // Return if the container's empty.
536  value_type* last = m_last;
537  if (last == nullptr) return -1;
538 
539  // Make a new implementation object and copy data.
540  size_t capacity = m_impl->capacity();
541  auto new_impl = std::make_unique<Impl> (capacity);
542  value_type* new_begin = new_impl->data();
543  value_type* new_end = new_begin;
544  value_type* begin = m_begin;
545  value_type* end = m_last+1;
546  new_end = std::copy (begin, end, new_end);
547 
548  // Update the range of the last entry.
549  (new_end-1)->first = extendedRange;
550 
551  // Install the new implementation.
552  installImpl (std::move (new_impl), new_begin, new_end, ctx);
553 
554  return 1;
555 }
556 
557 
558 /**
559  * @brief Remove unused entries from the front of the list.
560  * @param keys List of keys that may still be in use.
561  * (Must be sorted.)
562  * @param trimall If true, then allow removing all elements in the container.
563  * Otherwise, stop when there's one left.
564  *
565  * We examine the objects in the container, starting with the earliest one.
566  * If none of the keys in @c keys match the range for this object, then
567  * it is removed from the container. We stop when we either find
568  * an object with a range matching a key in @c keys or (if trimall is false)
569  * when there is only one object left.
570  *
571  * Removed objects are queued for deletion once all slots have been
572  * marked as quiescent.
573  *
574  * The list @c keys MUST be sorted.
575  *
576  * Returns the number of objects that were removed.
577  */
578 T_CONCURRENTRANGEMAP
579 size_t
580 CONCURRENTRANGEMAP::trim (const std::vector<key_query_type>& keys,
581  bool trimall /*= false*/)
582 {
583  size_t ndel = 0;
584 
585  lock_t lock (m_mutex);
586 
587  // Return immediately if the container is empty.
588  value_type* last = m_last;
589  if (last == nullptr) return ndel;
590 
591  value_type* pos = m_begin;
592 
593  // If trimall is set, then we want to compare trimall against end=last+1.
594  // Otherwise, we compare against last in order to skip considering
595  // the last element.
596  if (trimall) ++last;
597  while (pos < last) {
598  // FIXME: Can use the position where we found the last one as a hint?
599  if (anyInRange (pos->first, keys)) {
600  // One of the keys matched the range of this object. Stop here.
601  break;
602  }
603 
604  // We're deleting the object now.
605 
606  // Discard it. Be sure to adjust m_begin first.
607  mapped_type todel = pos->second;
608  ++pos;
609  if (pos > m_last) {
610  // Removing the last entry. Be sure to clear m_begin first.
611  m_begin = nullptr;
612  m_last = nullptr;
613  }
614  m_begin = pos;
615  m_payloadDeleter->discard (todel);
616  ++ndel;
617  }
618 
619  return ndel;
620 }
621 
622 
623 /**
624  * @brief Remove all entries in the container.
625  * Mostly for testing --- should not normally be used.
626  */
627 T_CONCURRENTRANGEMAP
628 void CONCURRENTRANGEMAP::clear()
629 {
630  lock_t lock (m_mutex);
631 
632  // Don't do anything if the container's already empty.
633  value_type* last = m_last;
634  if (last == nullptr) return;
635 
636  value_type* begin = m_begin;
637 
638  while (begin != last) {
639  mapped_type todel = begin->second;
640  ++begin;
641  m_begin = begin;
642  m_payloadDeleter->discard (todel);
643  }
644 
645  // Now have one element left. Be sure to clear m_begin first.
646  mapped_type todel = begin->second;
647  ++begin;
648  m_begin = nullptr;
649  m_last = nullptr;
650  m_begin = begin;
651  m_payloadDeleter->discard (todel);
652 }
653 
654 
655 /**
656  * @brief Return the current number of elements in the map.
657  */
658 T_CONCURRENTRANGEMAP
659 inline
660 size_t CONCURRENTRANGEMAP::size() const
661 {
662  const_iterator last = m_last;
663  if (!last) return 0;
664  const_iterator begin = getBegin (last);
665  if (!last) return 0;
666  return last+1 - begin;
667 }
668 
669 
670 /**
671  * @brief Test if the map is empty.
672  */
673 T_CONCURRENTRANGEMAP
674 inline
675 bool CONCURRENTRANGEMAP::empty() const
676 {
677  return m_last == nullptr;
678 }
679 
680 
681 /**
682  * @brief Return the current capacity of the map.
683  */
684 T_CONCURRENTRANGEMAP
685 inline
686 size_t CONCURRENTRANGEMAP::capacity() const
687 {
688  return m_updater.get().capacity();
689 }
690 
691 
692 /**
693  * @brief Return the number times an item was inserted into the map.
694  */
695 T_CONCURRENTRANGEMAP
696 inline
697 size_t CONCURRENTRANGEMAP::nInserts() const
698 {
699  return m_nInserts;
700 }
701 
702 
703 /**
704  * @brief Return the maximum size of the map.
705  */
706 T_CONCURRENTRANGEMAP
707 inline
708 size_t CONCURRENTRANGEMAP::maxSize() const
709 {
710  return m_maxSize;
711 }
712 
713 
714 /**
715  * @brief Return a range that can be used to iterate over the container.
716  */
717 T_CONCURRENTRANGEMAP
718 inline
719 typename CONCURRENTRANGEMAP::const_iterator_range
720 CONCURRENTRANGEMAP::range() const
721 {
722  const_iterator last = m_last;
723  if (!last) return const_iterator_range (nullptr, nullptr);
724  const_iterator begin = getBegin (last);
725  if (!last) return const_iterator_range (nullptr, nullptr);
726  return const_iterator_range (begin, last+1);
727 }
728 
729 
730 /**
731  * @brief Called when this thread is no longer referencing anything
732  * from this container.
733  * @param ctx Execution context.
734  */
735 T_CONCURRENTRANGEMAP
736 void
737 CONCURRENTRANGEMAP::quiescent (const typename Updater_t::Context_t& ctx /*= Updater_t::defaultContext()*/)
738 {
739  m_updater.quiescent (ctx);
740  m_payloadDeleter->quiescent (ctx);
741 }
742 
743 
744 /**
745  * @brief Return the begin/last pointers.
746  * @param [inout] last Current value of last.
747  *
748  * Retrieve consistent values of the begin and last pointers.
749  * The last pointer should have already been fetched, and may be updated.
750  * Usage should be like this:
751  *
752  *@code
753  * const_iterator last = m_last;
754  * if (!last) return; // Container empty.
755  * const_iterator begin = getBegin (last);
756  * if (!last) return; // Container empty.
757  @endcode
758 */
759 T_CONCURRENTRANGEMAP
760 inline
761 typename CONCURRENTRANGEMAP::const_iterator
762 CONCURRENTRANGEMAP::getBegin (const_iterator& last) const
763 {
764  // First fetch the begin pointer, then check that both there is not
765  // an update in progress and that the last pointer
766  // hasn't changed. In either case, we need to refetch both pointers.
767  // ABA isn't an issue here since the pointers go only in one direction,
768  // and if we allocate a new chunk, it will be in a disjoint
769  // piece of memory.
770  const_iterator begin;
771  while (true) {
772  begin = m_begin;
773  if (begin && last == m_last) break;
774  CxxUtils::stall();
775  last = m_last;
776  }
777  return begin;
778 }
779 
780 
781 /**
782  * @brief Consistently update both the begin and last pointers.
783  * @param begin New begin pointer.
784  * @param end New end pointer.
785  */
786 T_CONCURRENTRANGEMAP
787 inline
788 void
789 CONCURRENTRANGEMAP::updatePointers (value_type* new_begin, value_type* new_end)
790 {
791  // Mark that there's an update in progress.
792  m_begin = nullptr;
793  // Then update the last pointer.
794  if (new_begin == new_end) {
795  m_last = nullptr;
796  }
797  else {
798  m_last = new_end-1;
799  }
800  // Then set the begin pointer.
801  m_begin = new_begin;
802 }
803 
804 
805 /**
806  * @brief Test to see if any keys within @c keys match @c r.
807  * @brief r Range to test.
808  * @break keys List of keys to test. MUST be sorted.
809  */
810 T_CONCURRENTRANGEMAP
811 bool
812 CONCURRENTRANGEMAP::anyInRange (const key_type& r,
813  const std::vector<key_query_type>& keys) const
814 {
815  auto pos = std::lower_bound (keys.begin(), keys.end(), r, m_compare);
816  return pos != keys.end() && m_compare.inRange (*pos, r);
817 }
818 
819 
820 /**
821  * @brief Install a new implementation instance and make it visible.
822  * @param new_impl The new instance.
823  * @param new_begin Begin pointer for the new instance.
824  * @param new_end End pointer for the new instance.
825  * (Usual STL meaning of end. If the instance is empty,
826  * then new_end should match new_begin.)
827  * @param ctx Execution context.
828  */
829 T_CONCURRENTRANGEMAP
830 inline
831 void
832 CONCURRENTRANGEMAP::installImpl (std::unique_ptr<Impl> new_impl,
833  value_type* new_begin,
834  value_type* new_end,
835  const typename Updater_t::Context_t& ctx)
836 {
837  // Install the new implementation.
838  m_impl = new_impl.get();
839 
840  // Make the update visible to other threads.
841  // Make sure not to add the old version to the garbage list before
842  // we've updated the pointers.
843  updatePointers (new_begin, new_end);
844  m_updater.update (std::move (new_impl), ctx);
845 }
846 
847 
848 #undef T_CONCURRENTRANGEMAP
849 #undef CONCURRENTRANGEMAP
850 
851 
852 } // namespace CxxUtils