Hi
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
(__gnu_debug::_Safe_unordered_container<>::_M_invalidate_all()): Lock mutex
while calling _M_invalidate_if and _M_invalidate_locals.
(_Safe_unordered_container<>::_M_invalidate_all_if<_Pred>()): New.
(_Safe_unordered_container<>::_M_invalidate_all_single_if<_Pred>()): New.
(_Safe_unordered_container<>::struct _InvalidatePred<>): New.
(_Safe_unordered_container<>::_M_invalidate<_It>(_It)): New.
(_Safe_unordered_container<>::_M_invalidate_single<_It>(_It)): New.
* include/debug/safe_unordered_container.tcc
(_Safe_unordered_container<>::_M_invalidate_if<_Pred>(_Pred)): Remove lock.
(_Safe_unordered_container<>::_M_invalidate_local_if<_Pred>(_Pred)):
Remove lock.
* include/debug/unordered_map
(unordered_map<>::erase(const_iterator, const_iterator)):
Add lock before loop
for iterator invalidation. Use _M_invalidate_single for
invalidation. Remove
_M_check_rehashed call.
(unordered_map<>::_M_self()): New.
(unordered_map<>::_M_invalidate(_Base_const_iterator)): Remove.
(unordered_map<>::_M_erase(_Base_const_iterator)): Adapt. Remove
_M_check_rehashed
call.
(unordered_multimap<>::erase(const key_type&)): Add lock
before loop for iterator
invalidation. Use _M_invalidate_single for invalidation.
Remove _M_check_rehashed
call.
(unordered_multimap<>::erase(const_iterator,
const_iterator)): Likewise.
(unordered_multimap<>::_M_self()): New.
(unordered_multimap<>::_M_invalidate(_Base_const_iterator)): Remove.
(unordered_multimap<>::_M_erase(_Base_const_iterator)): Adapt. Remove
_M_check_rehashed
call.
* include/debug/unordered_set
(unordered_set<>::erase(const_iterator, const_iterator)):
Add lock before loop
for iterator invalidation. Use _M_invalidate_single for
invalidation. Remove
_M_check_rehashed call.
(unordered_set<>::_M_self()): New.
(unordered_set<>::_M_invalidate(_Base_const_iterator)): Remove.
(unordered_set<>::_M_erase(_Base_const_iterator)): Adapt. Remove
_M_check_rehashed
call.
(unordered_multiset<>::erase(const key_type&)): Add lock
before loop for iterator
invalidation. Use _M_invalidate_single for invalidation.
Remove _M_check_rehashed
call.
(unordered_multiset<>::erase(const_iterator,
const_iterator)): Likewise.
(unordered_multiset<>::_M_self()): New.
(unordered_multiset<>::_M_invalidate(_Base_const_iterator)): Remove.
(unordered_multiset<>::_M_erase(_Base_const_iterator)): Adapt. Remove
_M_check_rehashed
call.
All 23_containers/unordered_* tests run under Linux x64_86 in
_GLIBCXX_DEBUG mode.
Note that patch was generated with a git diff -b option.
Ok to commit ?
François
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)
+ {
+ _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());
for (_Safe_iterator_base* __iter = _M_iterators; __iter;)
{
iterator* __victim = static_cast<iterator*>(__iter);
@@ -72,7 +71,6 @@ namespace __gnu_debug
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 4bde18c917b..30953417edc 100644
--- a/libstdc++-v3/include/debug/unordered_map
+++ b/libstdc++-v3/include/debug/unordered_map
@@ -738,18 +738,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_single(__tmp);
+ }
}
- size_type __bucket_count = this->bucket_count();
auto __next = _Base::erase(__first.base(), __last.base());
- _M_check_rehashed(__bucket_count);
return { __next, this };
}
@@ -763,6 +764,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)
{
@@ -770,31 +775,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
@@ -1497,16 +1489,15 @@ namespace __debug
erase(const key_type& __key)
{
size_type __ret(0);
- size_type __bucket_count = this->bucket_count();
auto __pair = _Base::equal_range(__key);
+ __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
for (auto __victim = __pair.first; __victim != __pair.second;)
{
- _M_invalidate(__victim);
+ this->_M_invalidate_single(__victim);
__victim = _Base::erase(__victim);
++__ret;
}
- _M_check_rehashed(__bucket_count);
return __ret;
}
@@ -1535,18 +1526,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_single(__tmp);
+ }
}
- size_type __bucket_count = this->bucket_count();
auto __next = _Base::erase(__first.base(), __last.base());
- _M_check_rehashed(__bucket_count);
return { __next, this };
}
@@ -1560,6 +1552,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)
{
@@ -1567,31 +1563,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
diff --git a/libstdc++-v3/include/debug/unordered_set
b/libstdc++-v3/include/debug/unordered_set
index de999a76890..cc55d15f7be 100644
--- a/libstdc++-v3/include/debug/unordered_set
+++ b/libstdc++-v3/include/debug/unordered_set
@@ -623,18 +623,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_single(__tmp);
+ }
}
- size_type __bucket_count = this->bucket_count();
auto __next = _Base::erase(__first.base(), __last.base());
- _M_check_rehashed(__bucket_count);
return { __next, this };
}
@@ -645,6 +646,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)
{
@@ -652,31 +657,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
@@ -1320,9 +1312,10 @@ namespace __debug
{
size_type __ret(0);
auto __pair = _Base::equal_range(__key);
+ __gnu_cxx::__scoped_lock sentry(_M_self()->_M_get_mutex());
for (auto __victim = __pair.first; __victim != __pair.second;)
{
- _M_invalidate(__victim);
+ this->_M_invalidate_single(__victim);
__victim = _Base::erase(__victim);
++__ret;
}
@@ -1355,14 +1348,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_single(__tmp);
+ }
}
+
return { _Base::erase(__first.base(), __last.base()), this };
}
@@ -1373,6 +1370,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)
{
@@ -1380,31 +1381,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