From: François Dumont Date: Wed, 28 Nov 2018 06:19:38 +0000 (+0000) Subject: re PR libstdc++/88199 (memory leak on unordered container move assignment) X-Git-Tag: releases/gcc-7.4.0~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e070cb28f5a84946371449289b8d22db1fea7ac;p=thirdparty%2Fgcc.git re PR libstdc++/88199 (memory leak on unordered container move assignment) 2018-11-28 François Dumont PR libstdc++/88199 * include/bits/hashtable.h (_Hashtable<>::_M_move_assign(_Hashtable&&, false_type)): Deallocate former buckets after assignment. * testsuite/23_containers/unordered_set/allocator/move_assign.cc (test03): New. From-SVN: r266542 --- diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 15ca57cd0062..ec2e9cb9d139 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,12 @@ +2018-11-28 François Dumont + + PR libstdc++/88199 + * include/bits/hashtable.h + (_Hashtable<>::_M_move_assign(_Hashtable&&, false_type)): Deallocate + former buckets after assignment. + * testsuite/23_containers/unordered_set/allocator/move_assign.cc + (test03): New. + 2018-10-31 Jonathan Wakely Backport from mainline diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h index e0806dc93a15..e9c349ac9560 100644 --- a/libstdc++-v3/include/bits/hashtable.h +++ b/libstdc++-v3/include/bits/hashtable.h @@ -1206,6 +1206,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_assign(__ht, [&__roan](__node_type* __n) { return __roan(std::move_if_noexcept(__n->_M_v())); }); + + if (__former_buckets) + _M_deallocate_buckets(__former_buckets, __former_bucket_count); __ht.clear(); } __catch(...) diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/allocator/move_assign.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/allocator/move_assign.cc index 01d4dd6c0a1c..75006fadd5bd 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_set/allocator/move_assign.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/allocator/move_assign.cc @@ -18,71 +18,133 @@ // { dg-do run { target c++11 } } #include + #include #include #include using __gnu_test::propagating_allocator; using __gnu_test::counter_type; +using __gnu_test::tracker_allocator; +using __gnu_test::tracker_allocator_counter; void test01() { - typedef propagating_allocator alloc_type; - typedef __gnu_test::counter_type_hasher hash; - typedef std::unordered_set, - alloc_type> test_type; + tracker_allocator_counter::reset(); + { + typedef propagating_allocator> alloc_type; + typedef __gnu_test::counter_type_hasher hash; + typedef std::unordered_set, + alloc_type> test_type; + + test_type v1(alloc_type(1)); + v1.emplace(0); - test_type v1(alloc_type(1)); - v1.emplace(0); + test_type v2(alloc_type(2)); + v2.emplace(1); - test_type v2(alloc_type(2)); - v2.emplace(1); + counter_type::reset(); - counter_type::reset(); + v2 = std::move(v1); - v2 = std::move(v1); + VERIFY( 1 == v1.get_allocator().get_personality() ); + VERIFY( 2 == v2.get_allocator().get_personality() ); - VERIFY( 1 == v1.get_allocator().get_personality() ); - VERIFY( 2 == v2.get_allocator().get_personality() ); + VERIFY( counter_type::move_count == 1 ); + VERIFY( counter_type::destructor_count == 2 ); + } - VERIFY( counter_type::move_count == 1 ); - VERIFY( counter_type::destructor_count == 2 ); + // Check there's nothing left allocated or constructed. + VERIFY( tracker_allocator_counter::get_construct_count() + == tracker_allocator_counter::get_destruct_count() ); + VERIFY( tracker_allocator_counter::get_allocation_count() + == tracker_allocator_counter::get_deallocation_count() ); } void test02() { - typedef propagating_allocator alloc_type; - typedef __gnu_test::counter_type_hasher hash; - typedef std::unordered_set, - alloc_type> test_type; + tracker_allocator_counter::reset(); + { + typedef propagating_allocator> alloc_type; + typedef __gnu_test::counter_type_hasher hash; + typedef std::unordered_set, + alloc_type> test_type; + + test_type v1(alloc_type(1)); + v1.emplace(0); - test_type v1(alloc_type(1)); - v1.emplace(0); + auto it = v1.begin(); - auto it = v1.begin(); + test_type v2(alloc_type(2)); + v2.emplace(0); - test_type v2(alloc_type(2)); - v2.emplace(0); + counter_type::reset(); - counter_type::reset(); + v2 = std::move(v1); - v2 = std::move(v1); + VERIFY(0 == v1.get_allocator().get_personality()); + VERIFY(1 == v2.get_allocator().get_personality()); - VERIFY(0 == v1.get_allocator().get_personality()); - VERIFY(1 == v2.get_allocator().get_personality()); + VERIFY( counter_type::move_count == 0 ); + VERIFY( counter_type::copy_count == 0 ); + VERIFY( counter_type::destructor_count == 1 ); - VERIFY( counter_type::move_count == 0 ); - VERIFY( counter_type::copy_count == 0 ); - VERIFY( counter_type::destructor_count == 1 ); + VERIFY( it == v2.begin() ); + } - VERIFY( it == v2.begin() ); + // Check there's nothing left allocated or constructed. + VERIFY( tracker_allocator_counter::get_construct_count() + == tracker_allocator_counter::get_destruct_count() ); + VERIFY( tracker_allocator_counter::get_allocation_count() + == tracker_allocator_counter::get_deallocation_count() ); +} + +void test03() +{ + tracker_allocator_counter::reset(); + { + typedef propagating_allocator> alloc_type; + typedef __gnu_test::counter_type_hasher hash; + typedef std::unordered_set, + alloc_type> test_type; + + test_type v1(alloc_type(1)); + v1.emplace(0); + + test_type v2(alloc_type(2)); + int i = 0; + v2.emplace(i++); + for (; v2.bucket_count() == v1.bucket_count(); ++i) + v2.emplace(i); + + counter_type::reset(); + + v2 = std::move(v1); + + VERIFY( 1 == v1.get_allocator().get_personality() ); + VERIFY( 2 == v2.get_allocator().get_personality() ); + + VERIFY( counter_type::move_count == 1 ); + VERIFY( counter_type::destructor_count == i + 1 ); + } + + // Check there's nothing left allocated or constructed. + VERIFY( tracker_allocator_counter::get_construct_count() + == tracker_allocator_counter::get_destruct_count() ); + VERIFY( tracker_allocator_counter::get_allocation_count() + == tracker_allocator_counter::get_deallocation_count() ); } int main() { test01(); test02(); + test03(); return 0; }