From: "H.J. Lu" <hjl.tools@gmail.com>
To: "François Dumont" <frs.dumont@gmail.com>
Cc: Jonathan Wakely <jwakely.gcc@gmail.com>,
Jonathan Wakely <jwakely@redhat.com>,
"libstdc++" <libstdc++@gcc.gnu.org>,
gcc-patches <gcc-patches@gcc.gnu.org>
Subject: Re: [PATH][_GLIBCXX_DEBUG] Fix unordered container merge
Date: Tue, 9 Nov 2021 16:05:10 -0800 [thread overview]
Message-ID: <CAMe9rOoA-jREgx9mnvb1_UWzBMLKkFW4onFFovCer5QsQYbgWg@mail.gmail.com> (raw)
In-Reply-To: <f20104db-f50e-7773-c382-28c10acf9bfc@gmail.com>
On Mon, Nov 8, 2021 at 1:37 PM François Dumont via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> Yet another version this time with only 1 guard implementation. The
> predicate to invalidate the safe iterators has been externalized.
>
> Ok to commit ?
>
This may have broken GCC bootstrap on Linux/x86-64:
https://gcc.gnu.org/pipermail/gcc-regression/2021-November/075734.html
In file included from ../../src-master/gcc/sanopt.c:22:
In member function ‘hash_table<Descriptor, Lazy,
Allocator>::value_type* hash_table<Descriptor, Lazy,
Allocator>::alloc_entries(size_t) const [with Descriptor =
hash_map<tree_node*, auto_vec<gimple*> >::hash_entry; bool Lazy =
false; Allocator = xcallocator]’,
inlined from ‘void hash_table<Descriptor, Lazy,
Allocator>::expand() [with Descriptor = hash_map<tree_node*,
auto_vec<gimple*> >::hash_entry; bool Lazy = false; Allocator =
xcallocator]’ at ../../src-master/gcc/hash-table.h:802:40:
../../src-master/gcc/system.h:784:34: error: section type conflict
with ‘void hash_table<Descriptor, Lazy, Allocator>::expand() [with
Descriptor = hash_map<tree_node*, auto_vec<gimple*> >::hash_entry;
bool Lazy = false; Allocator = xcallocator]’
784 | ((void)(!(EXPR) ? fancy_abort (__FILE__, __LINE__,
__FUNCTION__), 0 : 0))
| ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../../src-master/gcc/hash-table.h:715:3: note: in expansion of macro
‘gcc_assert’
715 | gcc_assert (nentries != NULL);
| ^~~~~~~~~~
In file included from ../../src-master/gcc/coretypes.h:482,
from ../../src-master/gcc/sanopt.c:23:
../../src-master/gcc/hash-table.h: In member function ‘void
hash_table<Descriptor, Lazy, Allocator>::expand() [with Descriptor =
hash_map<tree_node*, auto_vec<gimple*> >::hash_entry; bool Lazy =
false; Allocator = xcallocator]’:
../../src-master/gcc/hash-table.h:779:1: note: ‘void
hash_table<Descriptor, Lazy, Allocator>::expand() [with Descriptor =
hash_map<tree_node*, auto_vec<gimple*> >::hash_entry; bool Lazy =
false; Allocator = xcallocator]’ was declared here
779 | hash_table<Descriptor, Lazy, Allocator>::expand ()
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> On 06/11/21 2:51 pm, François Dumont wrote:
> > You were right to delay your reply. Here is a new version with less
> > code duplication and a bug fix in the new _UContMergeGuard where we
> > were using it->second rather than it->first to get the key.
> >
> > Note also that the change to _M_merge_multi implementation is also
> > important because otherwise we might be trying to extract a key from a
> > destructed node.
> >
> > libstdc++: [_GLIBCXX_DEBUG] Implement unordered container merge
> >
> > The _GLIBCXX_DEBUG unordered containers need a dedicated merge
> > implementation
> > so that any existing iterator on the transfered nodes is properly
> > invalidated.
> >
> > Add typedef/using declaration for everything used as-is from
> > normal implementation.
> >
> > libstdc++-v3/ChangeLog:
> >
> > * include/bits/hashtable_policy.h (__distance_fw): Replace
> > class keyword with
> > typename.
> > * include/bits/hashtable.h
> > (_Hashtable<>::_M_merge_unique): Remove noexcept
> > qualification. Use const_iterator for node
> > extraction/reinsert.
> > (_Hashtable<>::_M_merge_multi): Likewise. Compute new hash
> > code before extract.
> > * include/debug/safe_container.h (_Safe_container<>): Make
> > all methods
> > protected.
> > * include/debug/safe_unordered_container.h
> > (_Safe_unordered_container<>::_UContMergeGuard<_ExtractKey, _Source>):
> > New.
> > (_Safe_unordered_container<>::_S_uc_guard<_ExtractKey, _Source>): New.
> > (_Safe_unordered_container<>::_UMContMergeGuard<_ExtractKey,
> > _Source>): New.
> > (_Safe_unordered_container<>::_S_umc_guard<_ExtractKey, _Source>): New.
> > (_Safe_unordered_container<>::_M_invalide_all): Make public.
> > (_Safe_unordered_container<>::_M_invalide_if): Likewise.
> > (_Safe_unordered_container<>::_M_invalide_local_if): Likewise.
> > * include/debug/unordered_map
> > (unordered_map<>::mapped_type, pointer, const_pointer):
> > New typedef.
> > (unordered_map<>::reference, const_reference,
> > difference_type): New typedef.
> > (unordered_map<>::get_allocator, empty, size, max_size):
> > Add usings.
> > (unordered_map<>::bucket_count, max_bucket_count, bucket):
> > Add usings.
> > (unordered_map<>::hash_function, key_equal, count,
> > contains): Add usings.
> > (unordered_map<>::operator[], at, rehash, reserve): Add
> > usings.
> > (unordered_map<>::merge): New.
> > (unordered_multimap<>::mapped_type, pointer,
> > const_pointer): New typedef.
> > (unordered_multimap<>::reference, const_reference,
> > difference_type): New typedef.
> > (unordered_multimap<>::get_allocator, empty, size,
> > max_size): Add usings.
> > (unordered_multimap<>::bucket_count, max_bucket_count,
> > bucket): Add usings.
> > (unordered_multimap<>::hash_function, key_equal, count,
> > contains): Add usings.
> > (unordered_multimap<>::rehash, reserve): Add usings.
> > (unordered_multimap<>::merge): New.
> > * include/debug/unordered_set
> > (unordered_set<>::mapped_type, pointer, const_pointer):
> > New typedef.
> > (unordered_set<>::reference, const_reference,
> > difference_type): New typedef.
> > (unordered_set<>::get_allocator, empty, size, max_size):
> > Add usings.
> > (unordered_set<>::bucket_count, max_bucket_count, bucket):
> > Add usings.
> > (unordered_set<>::hash_function, key_equal, count,
> > contains): Add usings.
> > (unordered_set<>::rehash, reserve): Add usings.
> > (unordered_set<>::merge): New.
> > (unordered_multiset<>::mapped_type, pointer,
> > const_pointer): New typedef.
> > (unordered_multiset<>::reference, const_reference,
> > difference_type): New typedef.
> > (unordered_multiset<>::get_allocator, empty, size,
> > max_size): Add usings.
> > (unordered_multiset<>::bucket_count, max_bucket_count,
> > bucket): Add usings.
> > (unordered_multiset<>::hash_function, key_equal, count,
> > contains): Add usings.
> > (unordered_multiset<>::rehash, reserve): Add usings.
> > (unordered_multiset<>::merge): New.
> > *
> > testsuite/23_containers/unordered_map/debug/merge1_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_map/debug/merge2_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_map/debug/merge3_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_map/debug/merge4_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_multimap/debug/merge1_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_multimap/debug/merge2_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_multimap/debug/merge3_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_multimap/debug/merge4_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_multiset/debug/merge1_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_multiset/debug/merge2_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_multiset/debug/merge3_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_multiset/debug/merge4_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_set/debug/merge1_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_set/debug/merge2_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_set/debug/merge3_neg.cc: New test.
> > *
> > testsuite/23_containers/unordered_set/debug/merge4_neg.cc: New test.
> > * testsuite/util/testsuite_abi.h: [_GLIBCXX_DEBUG] Use
> > normal unordered container implementation.
> >
> > Tested under Linux x86_64.
> >
> > Ok to commit ?
> >
> > François
> >
> > On 25/10/21 8:08 pm, François Dumont wrote:
> >> New patch with the proposed workaround below.
> >>
> >> I also slightly change the _M_merge_multi implementation so that if
> >> the new hash code computation raise an exception the node is simply
> >> not extracted rather than extracted and then released. This way, if
> >> it takes place on the 1st moved node the _GLIBCXX_DEBUG mode won't
> >> try to invalidate anything because the source size won't have changed.
> >>
> >> Ok to commit ?
> >>
> >> François
> >>
> >>
> >> On 16/10/21 4:52 pm, Jonathan Wakely wrote:
> >>>
> >>>
> >>> On Sat, 16 Oct 2021, 14:49 François Dumont via Libstdc++,
> >>> <libstdc++@gcc.gnu.org <mailto:libstdc%2B%2B@gcc.gnu.org>> wrote:
> >>>
> >>> Hi
> >>>
> >>> Here is the new proposal. My only concern is that we are
> >>> also using
> >>> hash or equal_to functors in the guard destructor.
> >>>
> >>>
> >>>
> >>> Can we catch any exception there, invalidate all iterators, and not
> >>> rethrow the exception?
> >>>
> >>>
> >>> I am going to enhance merge normal implementation to make
> >>> use of
> >>> the cached hash code when hash functors are the same between the
> >>> source
> >>> and destination of nodes. Maybe I'll be able to make use of it
> >>> in Debug
> >>> implementation too.
> >>>
> >>> François
> >>>
> >>>
> >>> On 14/10/21 10:23 am, Jonathan Wakely wrote:
> >>> > On Wed, 13 Oct 2021 at 18:10, François Dumont via Libstdc++
> >>> > <libstdc++@gcc.gnu.org <mailto:libstdc%2B%2B@gcc.gnu.org>> wrote:
> >>> >> Hi
> >>> >>
> >>> >> libstdc++: [_GLIBCXX_DEBUG] Implement unordered
> >>> container merge
> >>> >>
> >>> >> The _GLIBCXX_DEBUG unordered containers need a
> >>> dedicated merge
> >>> >> implementation
> >>> >> so that any existing iterator on the transfered nodes
> >>> is properly
> >>> >> invalidated.
> >>> >>
> >>> >> Add typedef/using declaration for everything used as-is
> >>> from normal
> >>> >> implementation.
> >>> >>
> >>> >> libstdc++-v3/ChangeLog:
> >>> >>
> >>> >> * include/debug/safe_container.h
> >>> (_Safe_container<>): Make
> >>> >> all methods
> >>> >> protected.
> >>> >> * include/debug/safe_unordered_container.h
> >>> >> (_Safe_unordered_container<>::_M_invalide_all): Make public.
> >>> >> (_Safe_unordered_container<>::_M_invalide_if): Likewise.
> >>> >> (_Safe_unordered_container<>::_M_invalide_local_if): Likewise.
> >>> >> * include/debug/unordered_map
> >>> >> (unordered_map<>::mapped_type, pointer, const_pointer): New
> >>> >> typedef.
> >>> >> (unordered_map<>::reference, const_reference,
> >>> >> difference_type): New typedef.
> >>> >> (unordered_map<>::get_allocator, empty, size, max_size):
> >>> >> Add usings.
> >>> >> (unordered_map<>::bucket_count, max_bucket_count, bucket):
> >>> >> Add usings.
> >>> >> (unordered_map<>::hash_function, key_equal, count,
> >>> >> contains): Add usings.
> >>> >> (unordered_map<>::operator[], at, rehash, reserve): Add usings.
> >>> >> (unordered_map<>::merge): New.
> >>> >> (unordered_multimap<>::mapped_type, pointer,
> >>> >> const_pointer): New typedef.
> >>> >> (unordered_multimap<>::reference, const_reference,
> >>> >> difference_type): New typedef.
> >>> >> (unordered_multimap<>::get_allocator, empty, size,
> >>> >> max_size): Add usings.
> >>> >> (unordered_multimap<>::bucket_count, max_bucket_count,
> >>> >> bucket): Add usings.
> >>> >> (unordered_multimap<>::hash_function, key_equal, count,
> >>> >> contains): Add usings.
> >>> >> (unordered_multimap<>::rehash, reserve): Add usings.
> >>> >> (unordered_multimap<>::merge): New.
> >>> >> * include/debug/unordered_set
> >>> >> (unordered_set<>::mapped_type, pointer, const_pointer): New
> >>> >> typedef.
> >>> >> (unordered_set<>::reference, const_reference,
> >>> >> difference_type): New typedef.
> >>> >> (unordered_set<>::get_allocator, empty, size, max_size):
> >>> >> Add usings.
> >>> >> (unordered_set<>::bucket_count, max_bucket_count, bucket):
> >>> >> Add usings.
> >>> >> (unordered_set<>::hash_function, key_equal, count,
> >>> >> contains): Add usings.
> >>> >> (unordered_set<>::rehash, reserve): Add usings.
> >>> >> (unordered_set<>::merge): New.
> >>> >> (unordered_multiset<>::mapped_type, pointer,
> >>> >> const_pointer): New typedef.
> >>> >> (unordered_multiset<>::reference, const_reference,
> >>> >> difference_type): New typedef.
> >>> >> (unordered_multiset<>::get_allocator, empty, size,
> >>> >> max_size): Add usings.
> >>> >> (unordered_multiset<>::bucket_count, max_bucket_count,
> >>> >> bucket): Add usings.
> >>> >> (unordered_multiset<>::hash_function, key_equal, count,
> >>> >> contains): Add usings.
> >>> >> (unordered_multiset<>::rehash, reserve): Add usings.
> >>> >> (unordered_multiset<>::merge): New.
> >>> >> *
> >>> >> testsuite/23_containers/unordered_map/debug/merge1_neg.cc:
> >>> New test.
> >>> >> *
> >>> >> testsuite/23_containers/unordered_map/debug/merge2_neg.cc:
> >>> New test.
> >>> >> *
> >>> >> testsuite/23_containers/unordered_map/debug/merge3_neg.cc:
> >>> New test.
> >>> >> *
> >>> >> testsuite/23_containers/unordered_map/debug/merge4_neg.cc:
> >>> New test.
> >>> >> *
> >>> >>
> >>> testsuite/23_containers/unordered_multimap/debug/merge1_neg.cc:
> >>> New test.
> >>> >> *
> >>> >>
> >>> testsuite/23_containers/unordered_multimap/debug/merge2_neg.cc:
> >>> New test.
> >>> >> *
> >>> >>
> >>> testsuite/23_containers/unordered_multimap/debug/merge3_neg.cc:
> >>> New test.
> >>> >> *
> >>> >>
> >>> testsuite/23_containers/unordered_multimap/debug/merge4_neg.cc:
> >>> New test.
> >>> >> *
> >>> >>
> >>> testsuite/23_containers/unordered_multiset/debug/merge1_neg.cc:
> >>> New test.
> >>> >> *
> >>> >>
> >>> testsuite/23_containers/unordered_multiset/debug/merge2_neg.cc:
> >>> New test.
> >>> >> *
> >>> >>
> >>> testsuite/23_containers/unordered_multiset/debug/merge3_neg.cc:
> >>> New test.
> >>> >> *
> >>> >>
> >>> testsuite/23_containers/unordered_multiset/debug/merge4_neg.cc:
> >>> New test.
> >>> >> *
> >>> >> testsuite/23_containers/unordered_set/debug/merge1_neg.cc:
> >>> New test.
> >>> >> *
> >>> >> testsuite/23_containers/unordered_set/debug/merge2_neg.cc:
> >>> New test.
> >>> >> *
> >>> >> testsuite/23_containers/unordered_set/debug/merge3_neg.cc:
> >>> New test.
> >>> >> *
> >>> >> testsuite/23_containers/unordered_set/debug/merge4_neg.cc:
> >>> New test.
> >>> >> * testsuite/util/testsuite_abi.h:
> >>> [_GLIBCXX_DEBUG] Use
> >>> >> normal unordered container implementation.
> >>> >>
> >>> >> Tested under Linux x86_64.
> >>> >>
> >>> >> Ok to commit ?
> >>> > Yes, thanks. But ...
> >>> >
> >>> > This looks like an improvement over what we have now, but not 100%
> >>> > correct. The merge functions can exit via exception (if any hash
> >>> > function or equality predicate throws), and if that happens
> >>> the safe
> >>> > iterators will not get invalidated. I think we need to call
> >>> > _Base::merge in a try-block, and do the iterator invalidation
> >>> whether
> >>> > we return normally or via an exception.
> >>> >
> >>> > Something like:
> >>> >
> >>> > template<typename _H2, typename _P2>
> >>> > void
> >>> > merge(unordered_set<_Value, _H2, _P2, _Alloc>& __source)
> >>> > {
> >>> > struct _Guard
> >>> > {
> >>> > _Guard(unordered_set& __source) noexcept
> >>> > : _M_source(__source), _M_size(__source.size())
> >>> > { }
> >>> >
> >>> > ~_Guard()
> >>> > {
> >>> > const size_type __size = _M_source.size();
> >>> > if (__size != _M_size)
> >>> > {
> >>> > if (__size == 0)
> >>> > _M_source._M_invalidate_all();
> >>> > else
> >>> > {
> >>> > auto __pred = [&__source](auto __it)
> >>> > { return
> >>> __source.count(*__it) == 0; };
> >>> > __source._M_invalidate_if(__pred);
> >>> > __source._M_invalidate_local_if(__pred);
> >>> > }
> >>> > }
> >>> > }
> >>> >
> >>> > _Guard(const _Guard&) = delete;
> >>> >
> >>> > unordered_set& _M_source;
> >>> > const size_type _M_size;
> >>> > };
> >>> > _Guard __guard(__source);
> >>> > _Base::merge(__source._M_base());
> >>> > }
> >>> >
> >>>
> >>
> >
>
--
H.J.
next prev parent reply other threads:[~2021-11-10 0:05 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-10-13 17:10 François Dumont
2021-10-14 8:23 ` Jonathan Wakely
2021-10-16 13:47 ` François Dumont
2021-10-16 14:52 ` Jonathan Wakely
2021-10-21 16:51 ` François Dumont
2021-10-21 16:55 ` Jonathan Wakely
2021-10-22 5:22 ` François Dumont
2021-10-25 18:08 ` François Dumont
2021-11-06 13:51 ` François Dumont
2021-11-08 21:36 ` François Dumont
2021-11-09 16:25 ` Jonathan Wakely
2021-11-10 5:47 ` François Dumont
2021-11-10 9:38 ` Jonathan Wakely
2021-11-15 18:16 ` François Dumont
2021-11-10 11:55 ` Jonathan Wakely
2021-11-11 20:41 ` Jonathan Wakely
2021-11-11 21:33 ` François Dumont
2021-11-11 22:01 ` Jonathan Wakely
2021-11-10 0:05 ` H.J. Lu [this message]
2021-11-10 5:44 ` François Dumont
2021-11-10 7:26 ` Jonathan Wakely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CAMe9rOoA-jREgx9mnvb1_UWzBMLKkFW4onFFovCer5QsQYbgWg@mail.gmail.com \
--to=hjl.tools@gmail.com \
--cc=frs.dumont@gmail.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jwakely.gcc@gmail.com \
--cc=jwakely@redhat.com \
--cc=libstdc++@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).