Here is the new version, most of the feedbacks considered, replies below.

I noticed that __scoped_lock instances are named 'sentry', so not uglified. Is it because it is in the Standard in the iostream part ? If so let me know if I should name also the method argument 'sentry'.

Note also that I was also maintaining a lock while erasing container elements like in unordered_multiset::erase(const key_type&). I review this at the cost of looping twice on the iterator range.

    libstdc++: [_GLIBCXX_DEBUG] Reduce unordered containers mutex locks/unlocks

    The unordered containers have 2 types of iterators, the usual ones and the     local_iterator to iterate through a given bucket. In _GLIBCXX_DEBUG mode there
    are then 4 lists of iterators, 2 for iterator/const_iterator and 2 for
    local_iterator/const_local_iterator.

    This patch is making sure that the unordered container's mutex is only lock/unlock     1 time when those lists of iterators needed to be iterate to invalidate some of
    them.

    Also remove calls to _M_check_rehashed after erase operations. Standard do not permit
    to rehash on erase operation so we will never implement it.

    libstdc++-v3/ChangeLog

            * include/debug/safe_unordered_container.h
            (_Safe_unordered_container::_M_invalidate_locals): Remove.
            (_Safe_unordered_container::_M_invalidate_all): Lock mutex while calling
            _M_invalidate_if and _M_invalidate_locals.
            (_Safe_unordered_container::_M_invalidate_all_if): New.
            (_Safe_unordered_container::_M_invalidate): New.
            (_Safe_unordered_container::_M_invalidate_if): Make private, add __scoped_lock
            argument.
            (_Safe_unordered_container::_M_invalidate_local_if): Likewise.
            * include/debug/safe_unordered_container.tcc
            (_Safe_unordered_container::_M_invalidate_if): Adapt and remove lock.
            (_Safe_unordered_container::_M_invalidate_local_if): Likewise.
            * include/debug/unordered_map
            (unordered_map::erase(const_iterator, const_iterator)): Lock before loop on
            iterators. Remove _M_check_rehashed call.
            (unordered_map::_M_self): New.
            (unordered_map::_M_invalidate): Remove.
            (unordered_map::_M_erase): Adapt and remove _M_check_rehashed call.             (unordered_multimap::_M_erase(_Base_const_iterator, _Base_const_iterator)): New.
            (unordered_multimap::erase(_Kt&&)): Use latter.
            (unordered_multimap::erase(const key_type&)): Likewise.
            (unordered_multimap::erase(const_iterator, const_iterator)):
            Lock before loop on iterators. Remove _M_check_rehashed.
            (unordered_multimap::_M_self): New.
            (unordered_multimap::_M_invalidate): Remove.
            (unordered_multimap::_M_erase): Adapt. Remove _M_check_rehashed call.
            * include/debug/unordered_set
            (unordered_set::erase(const_iterator, const_iterator)): Add lock before loop
            for iterator invalidation. Remove _M_check_rehashed call.
            (unordered_set::_M_self): New.
            (unordered_set::_M_invalidate): Remove.
            (unordered_set::_M_erase): Adapt and remove _M_check_rehashed call.             (unordered_multiset::_M_erase(_Base_const_iterator, _Base_const_iterator)): New.
            (unordered_multiset::erase(_Kt&&)): Use latter.
            (unordered_multiset::erase(const key_type&)): Likewise.
            (unordered_multiset::erase(const_iterator, const_iterator)):
            Lock before loop on iterators. Remove _M_check_rehashed.
            (unordered_multiset::_M_self): New.
            (unordered_multiset::_M_invalidate): Remove.
            (unordered_multiset::_M_erase): Adapt. Remove _M_check_rehashed call.

Tested under Linux x86_64 _GLIBCXX_DEBUG mode.

Ok to commit ?

François


On 2/23/26 12:03, Jonathan Wakely wrote:
On Mon, 23 Feb 2026 at 10:30 +0000, Jonathan Wakely wrote:
On Sat, 21 Feb 2026 at 17:40 +0100, François Dumont wrote:

diff --git a/libstdc++-v3/include/debug/safe_unordered_container.h b/libstdc++-v3/include/debug/safe_unordered_container.h
index e76d60d2605..0571efaba49 100644
--- a/libstdc++-v3/include/debug/safe_unordered_container.h
+++ b/libstdc++-v3/include/debug/safe_unordered_container.h
@@ -57,7 +57,6 @@ namespace __gnu_debug
 template<typename _Container>
   class _Safe_unordered_container : public _Safe_unordered_container_base
   {
-    private:
     _Container&
     _M_cont() noexcept
     { return *static_cast<_Container*>(this); }
@@ -66,17 +65,17 @@ namespace __gnu_debug
     _M_self() const
     { return this; }

-    protected:
     void
     _M_invalidate_locals()
     {
    auto __local_end = _M_cont()._M_base().cend(0);
-    this->_M_invalidate_local_if(
+    _M_invalidate_local_if(
      [__local_end](__decltype(__local_end) __it)
      { return __it != __local_end; });
     }

#if __cplusplus > 201402L
+    protected:
     template<typename _ExtractKey, typename _Source>
    struct _UContInvalidatePred
    {
@@ -130,10 +129,7 @@ namespace __gnu_debug
        if (__size == 0)
          _M_source._M_invalidate_all();
        else
-          {
-            _M_source._M_invalidate_if(_M_pred);
-            _M_source._M_invalidate_local_if(_M_pred);
-          }
+          _M_source._M_invalidate_all_if(_M_pred);
          }
        __catch(...)
          {
@@ -169,12 +165,61 @@ namespace __gnu_debug
     void
     _M_invalidate_all()
     {
+    __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
    auto __end = _M_cont()._M_base().cend();
-    this->_M_invalidate_if([__end](__decltype(__end) __it)
+    _M_invalidate_if([__end](__decltype(__end) __it)
             { return __it != __end; });
    _M_invalidate_locals();
     }

+      template<typename _Predicate>
+    void
+    _M_invalidate_all_if(_Predicate __pred)
+    {
+      __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
+      _M_invalidate_all_single_if(__pred);
+    }
+
+    protected:
+      template<typename _Predicate>
+    void
+    _M_invalidate_all_single_if(_Predicate __pred)
+    {
+      _M_invalidate_if(__pred);
+      _M_invalidate_local_if(__pred);
+    }
+
+      template<typename _VictimIt>
+    struct _InvalidatePred
+    {
+      _InvalidatePred(_VictimIt __victim)
+      : _M_victim(__victim) { }
+
+      template<typename _Iterator>
+        bool
+        operator()(_Iterator __it) const
+        { return __it == _M_victim; }
+
+      _VictimIt _M_victim;
+    };
+
+      template<typename _VictimIt>
+    void
+    _M_invalidate(_VictimIt __victim)

Does this need to be a function template? Could its parameter just be _Base_const_iterator instead?

And then _InvalidatePred::_M_victim could be the same type, and that
doesn't need to be a class template.

Not without doing some refactoring.

At the time of _Safe_unordered_container<_Container> instantiation _Container is an incomplete type, we cannot get _Base_const_iterator from it.

We would need to pass the 'normal' container type along with current _Container type so no gain.



+    {
+      _InvalidatePred<_VictimIt> __pred(__victim);
+      _M_invalidate_all_if(__pred);
+    }
+
+      template<typename _VictimIt>
+    void
+    _M_invalidate_single(_VictimIt __victim)
+    {
+      _InvalidatePred<_VictimIt> __pred(__victim);
+      _M_invalidate_all_single_if(__pred);
+    }
+
+    private:
     /** Invalidates all iterators @c x that reference this container,
      are not singular, and for which @c __pred(x) returns @c
      true. @c __pred will be invoked with the normal iterators nested
diff --git a/libstdc++-v3/include/debug/safe_unordered_container.tcc b/libstdc++-v3/include/debug/safe_unordered_container.tcc
index f60761534c8..6f2975116c7 100644
--- a/libstdc++-v3/include/debug/safe_unordered_container.tcc
+++ b/libstdc++-v3/include/debug/safe_unordered_container.tcc
@@ -40,7 +40,6 @@ namespace __gnu_debug
    typedef typename _Container::iterator iterator;
    typedef typename _Container::const_iterator const_iterator;

-    __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());

I find the changes in this file make the code more confusing. You've
added new member functions with "single" in the name to do the work without
locking the mutex, but then you've changed _M_invalidate_if and
_M_invalidate_local_if to not lock the mutex. But they don't have
"single" in the name. Without checking the code carefully, I would
assume that _M_invalidate_if acquires the lock, because that's what
the naming convention in safe_unordered_container.h seems to imply.


I think adding the new "single" functions is what causes this
confusion, because the names are not used consistently for all
non-locking functions. The names can also be misread so that
"invalidate single" looks like it invalidates a single iterator. We
have "invalidate all" to invalidate all iterators, and "invalidate
single" to invalidate a single iterator, right?! ... no, that's wrong.
And we also have "invalidate all single"!

I know we already use "_single" as a suffix in _Safe_base but I think
here it is confusing.

And one of those new "single" functions doesn't seem necessary at all:
_M_invalidate_all_single_if is just two lines, and is only used in two
places. You could just write those two lines inline where it's called.
That gets rid of _M_invalidate_all_single_if.

_M_invalidate_all_if is also only used in two places, so that can be
inlined into its callers too.

So now there are only two new member functions, _M_invalidate(victim)
and _M_invalidate_single(victim), one which acquires a lock and one
which assuems the caller has already acquired a lock. They could be
written like this:


     void
     _M_invalidate(_Base_const_iterator __victim)
     {
       __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
       _M_invalidate(__victim, sentry);
     }

     // Caller must hold lock
     void
     _M_invalidate(_Base_const_iterator __victim,
                   const __gnu_cxx::__scoped_lock&)
     {
       auto __pred = [__victim](_Base_const_iterator __it) {
         return __it == __victim;
       };
       _M_invalidate_if(__pred);
       _M_invalidate_local_if(__pred);
     }

And then callers that have already taken the lock call the second
overload.

This also removes the need for _InvalidatePred.

Actually, maybe the lambda won't work, because it needs to accept
_Base::const_local_iterator as well as _Base::const_iterator.

But you could do:

       auto __pred = [__victim](_Base_const_iterator __it) {
         return __it == __victim;
       };
       _M_invalidate_if(__pred);
       auto __local_pred = [__victim](_Base_const_local_iterator __it) {
         return __it == __victim;
       _M_invalidate_local_if(__local_pred);

Or define _InvalidatePred as a local class with overloaded operator():

       struct {
         _Base_const_iterator _M_victim;

         bool operator()(_Base_const_iterator __it) const
         { return __it == _M_victim; }

         bool operator()(_Base_const_local_iterator __it) const
         { return __it == _M_victim; }
       } __pred = { _M_victim };

       _M_invalidate_if(__pred);
       _M_invalidate_local_if(__pred);
    (I've just opened https://gcc.gnu.org/PR124210 about the fact that we
support equality comparison between const_local_iterator and
const_iterator, that doesn't seem useful).. But until that's fixed,
_M_invalidate_local_if can still rely on those comparisons.)

So this avoids adding several new member functions and a new class
template, and avoids the naming inconsistency for when "single" is
used or not.

For the two function which no longer acquire the lock
(_M_invalidate_if and _M_invalidate_local_if) please add a similar
"Caller must hold lock" comment to their declarations in the header.

I added the __scoped_lock arguments like proposed for other methods. Any backward compatibility issue with that ?

diff --git a/libstdc++-v3/include/debug/safe_unordered_container.h 
b/libstdc++-v3/include/debug/safe_unordered_container.h
index e76d60d2605..eeb63df5e5a 100644
--- a/libstdc++-v3/include/debug/safe_unordered_container.h
+++ b/libstdc++-v3/include/debug/safe_unordered_container.h
@@ -57,7 +57,6 @@ namespace __gnu_debug
   template<typename _Container>
     class _Safe_unordered_container : public _Safe_unordered_container_base
     {
-    private:
       _Container&
       _M_cont() noexcept
       { return *static_cast<_Container*>(this); }
@@ -66,17 +65,8 @@ namespace __gnu_debug
       _M_self() const
       { return this; }
 
-    protected:
-      void
-      _M_invalidate_locals()
-      {
-       auto __local_end = _M_cont()._M_base().cend(0);
-       this->_M_invalidate_local_if(
-               [__local_end](__decltype(__local_end) __it)
-               { return __it != __local_end; });
-      }
-
 #if __cplusplus > 201402L
+    protected:
       template<typename _ExtractKey, typename _Source>
        struct _UContInvalidatePred
        {
@@ -130,10 +120,7 @@ namespace __gnu_debug
                if (__size == 0)
                  _M_source._M_invalidate_all();
                else
-                 {
-                   _M_source._M_invalidate_if(_M_pred);
-                   _M_source._M_invalidate_local_if(_M_pred);
-                 }
+                 _M_source._M_invalidate_all_if(_M_pred);
              }
            __catch(...)
              {
@@ -168,20 +155,64 @@ namespace __gnu_debug
     public:
       void
       _M_invalidate_all()
+      {
+       __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
+       auto __end = _M_cont()._M_base().cend();
+       _M_invalidate_if(
+         [__end](__decltype(__end) __it)
+         { return __it != __end; },
+         sentry);
+
+       auto __local_end = _M_cont()._M_base().cend(0);
+       _M_invalidate_local_if(
+         [__local_end](__decltype(__local_end) __it)
+         { return __it != __local_end; },
+         sentry);
+      }
+
+      template<typename _Predicate>
+       void
+       _M_invalidate_all_if(_Predicate __pred)
+       {
+         __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
+         _M_invalidate_if(__pred, sentry);
+         _M_invalidate_local_if(__pred, sentry);
+       }
+
+    protected:
+      template<typename _VictimIt>
+       void
+       _M_invalidate(_VictimIt __victim)
+       {
+         __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
+         _M_invalidate(__victim, sentry);
+       }
+
+      template<typename _VictimIt>
+       void
+       _M_invalidate(_VictimIt __victim, const __gnu_cxx::__scoped_lock& 
__lock)
        {
          auto __end = _M_cont()._M_base().cend();
-       this->_M_invalidate_if([__end](__decltype(__end) __it)
-                              { return __it != __end; });
-       _M_invalidate_locals();
+         _M_invalidate_if(
+           [__victim](__decltype(__end) __it)
+           { return __it == __victim; },
+           __lock);
+
+         auto __local_end = _M_cont()._M_base().cend(0);
+         _M_invalidate_local_if(
+           [__victim](__decltype(__local_end) __it)
+           { return __it == __victim; },
+           __lock);
        }
 
+    private:
       /** Invalidates all iterators @c x that reference this container,
          are not singular, and for which @c __pred(x) returns @c
          true. @c __pred will be invoked with the normal iterators nested
          in the safe ones. */
       template<typename _Predicate>
        void
-       _M_invalidate_if(_Predicate __pred);
+       _M_invalidate_if(_Predicate __pred, const __gnu_cxx::__scoped_lock&);
 
       /** Invalidates all local iterators @c x that reference this container,
          are not singular, and for which @c __pred(x) returns @c
@@ -189,7 +220,8 @@ namespace __gnu_debug
          nested in the safe ones. */
       template<typename _Predicate>
        void
-       _M_invalidate_local_if(_Predicate __pred);
+       _M_invalidate_local_if(_Predicate __pred,
+                              const __gnu_cxx::__scoped_lock&);
     };
 } // namespace __gnu_debug
 
diff --git a/libstdc++-v3/include/debug/safe_unordered_container.tcc 
b/libstdc++-v3/include/debug/safe_unordered_container.tcc
index f60761534c8..75860d6d055 100644
--- a/libstdc++-v3/include/debug/safe_unordered_container.tcc
+++ b/libstdc++-v3/include/debug/safe_unordered_container.tcc
@@ -35,12 +35,11 @@ namespace __gnu_debug
     template<typename _Predicate>
       void
       _Safe_unordered_container<_Container>::
-      _M_invalidate_if(_Predicate __pred)
+      _M_invalidate_if(_Predicate __pred, const __gnu_cxx::__scoped_lock&)
       {
        typedef typename _Container::iterator iterator;
        typedef typename _Container::const_iterator const_iterator;
 
-       __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
        for (_Safe_iterator_base* __iter = _M_iterators; __iter;)
          {
            iterator* __victim = static_cast<iterator*>(__iter);
@@ -67,12 +66,11 @@ namespace __gnu_debug
     template<typename _Predicate>
       void
       _Safe_unordered_container<_Container>::
-      _M_invalidate_local_if(_Predicate __pred)
+      _M_invalidate_local_if(_Predicate __pred, const 
__gnu_cxx::__scoped_lock&)
       {
        typedef typename _Container::local_iterator local_iterator;
        typedef typename _Container::const_local_iterator const_local_iterator;
 
-       __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
        for (_Safe_iterator_base* __iter = _M_local_iterators; __iter;)
          {
            local_iterator* __victim = static_cast<local_iterator*>(__iter);
diff --git a/libstdc++-v3/include/debug/unordered_map 
b/libstdc++-v3/include/debug/unordered_map
index 1ea480a4e8b..89806adae10 100644
--- a/libstdc++-v3/include/debug/unordered_map
+++ b/libstdc++-v3/include/debug/unordered_map
@@ -762,18 +762,19 @@ namespace __debug
       erase(const_iterator __first, const_iterator __last)
       {
        __glibcxx_check_erase_range(__first, __last);
+       {
+         __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
          for (auto __tmp = __first.base(); __tmp != __last.base(); ++__tmp)
            {
              _GLIBCXX_DEBUG_VERIFY(__tmp != _Base::cend(),
                                    _M_message(__gnu_debug::__msg_valid_range)
                                    ._M_iterator(__first, "first")
                                    ._M_iterator(__last, "last"));
-           _M_invalidate(__tmp);
+             this->_M_invalidate(__tmp, sentry);
+           }
        }
 
-       size_type __bucket_count = this->bucket_count();
        auto __next = _Base::erase(__first.base(), __last.base());
-       _M_check_rehashed(__bucket_count);
        return { __next, this };
       }
 
@@ -787,6 +788,10 @@ namespace __debug
       _M_base() const noexcept { return *this; }
 
     private:
+      const unordered_map*
+      _M_self() const noexcept
+      { return this; }
+
       void
       _M_check_rehashed(size_type __prev_count)
       {
@@ -794,31 +799,18 @@ namespace __debug
          this->_M_invalidate_all();
       }
 
-      void
-      _M_invalidate(_Base_const_iterator __victim)
-      {
-       this->_M_invalidate_if(
-         [__victim](_Base_const_iterator __it) { return __it == __victim; });
-       this->_M_invalidate_local_if(
-         [__victim](_Base_const_local_iterator __it)
-         { return __it == __victim; });
-      }
-
       _Base_iterator
       _M_erase(_Base_const_iterator __victim)
       {
-       _M_invalidate(__victim);
-       size_type __bucket_count = this->bucket_count();
-       _Base_iterator __next = _Base::erase(__victim);
-       _M_check_rehashed(__bucket_count);
-       return __next;
+       this->_M_invalidate(__victim);
+       return _Base::erase(__victim);
       }
 
 #ifdef __glibcxx_node_extract // >= C++17 && HOSTED
       node_type
       _M_extract(_Base_const_iterator __victim)
       {
-       _M_invalidate(__victim);
+       this->_M_invalidate(__victim);
        return _Base::extract(__victim);
       }
 #endif
@@ -1532,18 +1524,8 @@ namespace __debug
       size_type
       erase(const key_type& __key)
       {
-       size_type __ret(0);
-       size_type __bucket_count = this->bucket_count();
-       auto __pair = _Base::equal_range(__key);
-       for (auto __victim = __pair.first; __victim != __pair.second;)
-         {
-           _M_invalidate(__victim);
-           __victim = _Base::erase(__victim);
-           ++__ret;
-         }
-
-       _M_check_rehashed(__bucket_count);
-       return __ret;
+       auto __victims = _Base::equal_range(__key);
+       return _M_erase(__victims.first, __victims.second);
       }
 
 # ifdef __glibcxx_associative_heterogeneous_erasure
@@ -1551,15 +1533,8 @@ namespace __debug
        size_type
        erase(_Kt&& __key)
        {
-         size_type __count(0);
          auto __victims = _Base::equal_range(__key);
-         for (auto __victim = __victims.first; __victim != __victims.second;)
-           {
-             _M_invalidate(__victim);
-             __victim = _Base::erase(__victim);
-             ++__count;
-           }
-         return __count;
+         return _M_erase(__victims.first, __victims.second);
        }
 #endif
 
@@ -1588,18 +1563,19 @@ namespace __debug
       erase(const_iterator __first, const_iterator __last)
       {
        __glibcxx_check_erase_range(__first, __last);
+       {
+         __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
          for (auto __tmp = __first.base(); __tmp != __last.base(); ++__tmp)
            {
              _GLIBCXX_DEBUG_VERIFY(__tmp != _Base::cend(),
                                    _M_message(__gnu_debug::__msg_valid_range)
                                    ._M_iterator(__first, "first")
                                    ._M_iterator(__last, "last"));
-           _M_invalidate(__tmp);
+             this->_M_invalidate(__tmp, sentry);
+           }
        }
 
-       size_type __bucket_count = this->bucket_count();
        auto __next = _Base::erase(__first.base(), __last.base());
-       _M_check_rehashed(__bucket_count);
        return { __next, this };
       }
 
@@ -1613,6 +1589,10 @@ namespace __debug
       _M_base() const noexcept { return *this; }
 
     private:
+      const unordered_multimap*
+      _M_self() const noexcept
+      { return this; }
+
       void
       _M_check_rehashed(size_type __prev_count)
       {
@@ -1620,31 +1600,35 @@ namespace __debug
          this->_M_invalidate_all();
       }
 
-      void
-      _M_invalidate(_Base_const_iterator __victim)
+      _Base_iterator
+      _M_erase(_Base_const_iterator __victim)
       {
-       this->_M_invalidate_if(
-         [__victim](_Base_const_iterator __it) { return __it == __victim; });
-       this->_M_invalidate_local_if(
-         [__victim](_Base_const_local_iterator __it)
-         { return __it == __victim; });
+       this->_M_invalidate(__victim);
+       return _Base::erase(__victim);
       }
 
-      _Base_iterator
-      _M_erase(_Base_const_iterator __victim)
+      size_type
+      _M_erase(_Base_const_iterator __first, _Base_const_iterator __last)
       {
-       _M_invalidate(__victim);
-       size_type __bucket_count = this->bucket_count();
-       _Base_iterator __next = _Base::erase(__victim);
-       _M_check_rehashed(__bucket_count);
-       return __next;
+       size_type __ret(0);
+       {
+         __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
+         for (auto __victim = __first; __victim != __last; ++__victim)
+           {
+             this->_M_invalidate(__victim, sentry);
+             ++__ret;
+           }
+       }
+
+       _Base::erase(__first, __last);
+       return __ret;
       }
 
 #ifdef __glibcxx_node_extract // >= C++17 && HOSTED
       node_type
       _M_extract(_Base_const_iterator __victim)
       {
-       _M_invalidate(__victim);
+       this->_M_invalidate(__victim);
        return _Base::extract(__victim);
       }
 #endif
diff --git a/libstdc++-v3/include/debug/unordered_set 
b/libstdc++-v3/include/debug/unordered_set
index e26b52d7029..7fff913bdb9 100644
--- a/libstdc++-v3/include/debug/unordered_set
+++ b/libstdc++-v3/include/debug/unordered_set
@@ -647,18 +647,19 @@ namespace __debug
       erase(const_iterator __first, const_iterator __last)
       {
        __glibcxx_check_erase_range(__first, __last);
+       {
+         __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
          for (auto __tmp = __first.base(); __tmp != __last.base(); ++__tmp)
            {
              _GLIBCXX_DEBUG_VERIFY(__tmp != _Base::cend(),
                                    _M_message(__gnu_debug::__msg_valid_range)
                                    ._M_iterator(__first, "first")
                                    ._M_iterator(__last, "last"));
-           _M_invalidate(__tmp);
+             this->_M_invalidate(__tmp, sentry);
+           }
        }
 
-       size_type __bucket_count = this->bucket_count();
        auto __next = _Base::erase(__first.base(), __last.base());
-       _M_check_rehashed(__bucket_count);
        return { __next, this };
       }
 
@@ -669,6 +670,10 @@ namespace __debug
       _M_base() const noexcept { return *this; }
 
     private:
+      const unordered_set*
+      _M_self() const noexcept
+      { return this; }
+
       void
       _M_check_rehashed(size_type __prev_count)
       {
@@ -676,31 +681,18 @@ namespace __debug
          this->_M_invalidate_all();
       }
 
-      void
-      _M_invalidate(_Base_const_iterator __victim)
-      {
-       this->_M_invalidate_if(
-         [__victim](_Base_const_iterator __it) { return __it == __victim; });
-       this->_M_invalidate_local_if(
-         [__victim](_Base_const_local_iterator __it)
-         { return __it == __victim; });
-      }
-
       _Base_iterator
       _M_erase(_Base_const_iterator __victim)
       {
-       _M_invalidate(__victim);
-       size_type __bucket_count = this->bucket_count();
-       _Base_iterator __next = _Base::erase(__victim);
-       _M_check_rehashed(__bucket_count);
-       return __next;
+       this->_M_invalidate(__victim);
+       return _Base::erase(__victim);
       }
 
 #ifdef __glibcxx_node_extract // >= C++17 && HOSTED
       node_type
       _M_extract(_Base_const_iterator __victim)
       {
-       _M_invalidate(__victim);
+       this->_M_invalidate(__victim);
        return _Base::extract(__victim);
       }
 #endif
@@ -1353,15 +1345,8 @@ namespace __debug
       size_type
       erase(const key_type& __key)
       {
-       size_type __count(0);
        auto __victims = _Base::equal_range(__key);
-       for (auto __victim = __victims.first; __victim != __victims.second;)
-         {
-           _M_invalidate(__victim);
-           __victim = _Base::erase(__victim);
-           ++__count;
-         }
-       return __count;
+       return _M_erase(__victims.first, __victims.second);
       }
 
 # ifdef __glibcxx_associative_heterogeneous_erasure
@@ -1369,15 +1354,8 @@ namespace __debug
        size_type
        erase(_Kt&& __key)
        {
-         size_type __count(0);
          auto __victims = _Base::equal_range(__key);
-         for (auto __victim = __victims.first; __victim != __victims.second;)
-           {
-             _M_invalidate(__victim);
-             __victim = _Base::erase(__victim);
-             ++__count;
-           }
-         return __count;
+         return _M_erase(__victims.first, __victims.second);
        }
 #endif
 
@@ -1406,14 +1384,18 @@ namespace __debug
       erase(const_iterator __first, const_iterator __last)
       {
        __glibcxx_check_erase_range(__first, __last);
+       {
+         __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
          for (auto __tmp = __first.base(); __tmp != __last.base(); ++__tmp)
            {
              _GLIBCXX_DEBUG_VERIFY(__tmp != _Base::cend(),
                                    _M_message(__gnu_debug::__msg_valid_range)
                                    ._M_iterator(__first, "first")
                                    ._M_iterator(__last, "last"));
-           _M_invalidate(__tmp);
+             this->_M_invalidate(__tmp, sentry);
+           }
        }
+
        return { _Base::erase(__first.base(), __last.base()), this };
       }
 
@@ -1424,6 +1406,10 @@ namespace __debug
       _M_base() const noexcept { return *this; }
 
     private:
+      const unordered_multiset*
+      _M_self() const noexcept
+      { return this; }
+
       void
       _M_check_rehashed(size_type __prev_count)
       {
@@ -1431,31 +1417,35 @@ namespace __debug
          this->_M_invalidate_all();
       }
 
-      void
-      _M_invalidate(_Base_const_iterator __victim)
+      _Base_iterator
+      _M_erase(_Base_const_iterator __victim)
       {
-       this->_M_invalidate_if(
-         [__victim](_Base_const_iterator __it) { return __it == __victim; });
-       this->_M_invalidate_local_if(
-         [__victim](_Base_const_local_iterator __it)
-         { return __it == __victim; });
+       this->_M_invalidate(__victim);
+       return _Base::erase(__victim);
       }
 
-      _Base_iterator
-      _M_erase(_Base_const_iterator __victim)
+      size_type
+      _M_erase(_Base_const_iterator __first, _Base_const_iterator __last)
       {
-       _M_invalidate(__victim);
-       size_type __bucket_count = this->bucket_count();
-       _Base_iterator __next = _Base::erase(__victim);
-       _M_check_rehashed(__bucket_count);
-       return __next;
+       size_type __count(0);
+       {
+         __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
+         for (auto __victim = __first; __victim != __last; ++__victim)
+           {
+             this->_M_invalidate(__victim, sentry);
+             ++__count;
+           }
+       }
+
+       _Base::erase(__first, __last);
+       return __count;
       }
 
 #ifdef __glibcxx_node_extract // >= C++17 && HOSTED
       node_type
       _M_extract(_Base_const_iterator __victim)
       {
-       _M_invalidate(__victim);
+       this->_M_invalidate(__victim);
        return _Base::extract(__victim);
       }
 #endif

Reply via email to