]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR libstdc++/88199 (memory leak on unordered container move assignment)
authorFrançois Dumont <fdumont@gcc.gnu.org>
Wed, 28 Nov 2018 06:19:38 +0000 (06:19 +0000)
committerFrançois Dumont <fdumont@gcc.gnu.org>
Wed, 28 Nov 2018 06:19:38 +0000 (06:19 +0000)
2018-11-28  François Dumont  <fdumont@gcc.gnu.org>

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

libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/hashtable.h
libstdc++-v3/testsuite/23_containers/unordered_set/allocator/move_assign.cc

index 15ca57cd0062bd0b5986b726656c8558778fd387..ec2e9cb9d13946c39018301e678dd9f7e456e7a8 100644 (file)
@@ -1,3 +1,12 @@
+2018-11-28  François Dumont  <fdumont@gcc.gnu.org>
+
+       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  <jwakely@redhat.com>
 
        Backport from mainline
index e0806dc93a155e83d1a3020ff2fa8b172ddd490d..e9c349ac9560db8608066da590009722fad8b62b 100644 (file)
@@ -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(...)
index 01d4dd6c0a1c0a5a3ef0ed86dd4d06c8e8781fbc..75006fadd5bd9d1710f9cba32e40115be9b40ee6 100644 (file)
 // { dg-do run { target c++11 } }
 
 #include <unordered_set>
+
 #include <testsuite_hooks.h>
 #include <testsuite_allocator.h>
 #include <testsuite_counter_type.h>
 
 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<counter_type, false> alloc_type;
-  typedef __gnu_test::counter_type_hasher hash;
-  typedef std::unordered_set<counter_type, hash,
-                            std::equal_to<counter_type>,
-                            alloc_type> test_type;
+  tracker_allocator_counter::reset();
+  {
+    typedef propagating_allocator<counter_type, false,
+                                 tracker_allocator<counter_type>> alloc_type;
+    typedef __gnu_test::counter_type_hasher hash;
+    typedef std::unordered_set<counter_type, hash,
+                              std::equal_to<counter_type>,
+                              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<counter_type, true> alloc_type;
-  typedef __gnu_test::counter_type_hasher hash;
-  typedef std::unordered_set<counter_type, hash,
-                            std::equal_to<counter_type>,
-                            alloc_type> test_type;
+  tracker_allocator_counter::reset();
+  {
+    typedef propagating_allocator<counter_type, true,
+                                 tracker_allocator<counter_type>> alloc_type;
+    typedef __gnu_test::counter_type_hasher hash;
+    typedef std::unordered_set<counter_type, hash,
+                              std::equal_to<counter_type>,
+                              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<counter_type, false,
+                                 tracker_allocator<counter_type>> alloc_type;
+    typedef __gnu_test::counter_type_hasher hash;
+    typedef std::unordered_set<counter_type, hash,
+                              std::equal_to<counter_type>,
+                              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;
 }