From ef0b3a0cd8460755a9f7e48e486f29ca2e049cf1 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Wed, 4 Oct 2023 17:38:33 +0200 Subject: [PATCH] fixed #201 --- include/boost/unordered/detail/fca.hpp | 31 ++- .../boost/unordered/detail/implementation.hpp | 1 + test/Jamfile.v2 | 2 + test/unordered/fancy_pointer_noleak.cpp | 183 ++++++++++++++++++ 4 files changed, 213 insertions(+), 4 deletions(-) create mode 100644 test/unordered/fancy_pointer_noleak.cpp diff --git a/include/boost/unordered/detail/fca.hpp b/include/boost/unordered/detail/fca.hpp index e7b60107..cd16dc47 100644 --- a/include/boost/unordered/detail/fca.hpp +++ b/include/boost/unordered/detail/fca.hpp @@ -603,25 +603,48 @@ namespace boost { return *this; } +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable : 4100) // unreferenced formal parameter (dtor calls) +#endif + void deallocate() noexcept { if (buckets) { + size_type const num_buckets = buckets_len(); + bucket_type* pb = boost::to_address(buckets); + (void)pb; // VS complains when dtor is trivial + + for (size_type i = 0; i < num_buckets; ++i) { + (pb + i)->~bucket_type(); + } + bucket_allocator_type bucket_alloc = this->get_bucket_allocator(); - boost::allocator_deallocate( - bucket_alloc, buckets, this->buckets_len()); + boost::allocator_deallocate(bucket_alloc, buckets, num_buckets); buckets = bucket_pointer(); } if (groups) { + size_type const num_groups = groups_len(); + group* pg = boost::to_address(groups); + (void)pg; // VS complains when dtor is trivial + + for (size_type i = 0; i < num_groups; ++i) { + (pg + i)->~group(); + } + group_allocator_type group_alloc = this->get_group_allocator(); - boost::allocator_deallocate( - group_alloc, groups, this->groups_len()); + boost::allocator_deallocate(group_alloc, groups, num_groups); groups = group_pointer(); } } +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + void swap(grouped_bucket_array& other) { std::swap(size_index_, other.size_index_); diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 2ca5cda6..858bafd8 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1501,6 +1501,7 @@ namespace boost { value_allocator val_alloc(alloc); boost::allocator_destroy(val_alloc, p->value_ptr()); + boost::unordered::detail::func::destroy(boost::to_address(p)); boost::allocator_deallocate(alloc, p, 1); } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 929d59a4..09d62122 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -116,6 +116,7 @@ local FCA_TESTS = swap_tests transparent_tests unnecessary_copy_tests + fancy_pointer_noleak ; for local test in $(FCA_TESTS) @@ -220,6 +221,7 @@ local FOA_TESTS = node_handle_tests uses_allocator hash_is_avalanching_test + fancy_pointer_noleak ; for local test in $(FOA_TESTS) diff --git a/test/unordered/fancy_pointer_noleak.cpp b/test/unordered/fancy_pointer_noleak.cpp new file mode 100644 index 00000000..d7f5fb65 --- /dev/null +++ b/test/unordered/fancy_pointer_noleak.cpp @@ -0,0 +1,183 @@ + +// Copyright (C) 2023 Joaquin M Lopez Munoz +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include "../helpers/unordered.hpp" + +#include "../helpers/random_values.hpp" +#include "../objects/test.hpp" + +#include +#include + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) +#pragma warning(disable:4714) /* marked as __forceinline not inlined */ +#endif + +namespace { + + test::seed_t initialize_seed(1002310); + + static std::size_t counted_pointer_count = 0; + + template + class counted_pointer { + public: + counted_pointer(T* p_ = nullptr) : p{p_} { + ++counted_pointer_count; + } + counted_pointer(counted_pointer const& x) : p{x.p} { + ++counted_pointer_count; + } + ~counted_pointer() { + --counted_pointer_count; + } + + counted_pointer& operator=(counted_pointer const&) = default; + + counted_pointer& operator=(T* p_) { + p = p_; + return *this; + } + + operator T*() const noexcept { return p; } + + template + Q& operator*() const noexcept { + return *p; + } + + T* operator->() const noexcept { return p; } + counted_pointer& operator++() noexcept { + ++p; + return *this; + } + counted_pointer operator++(int) noexcept { + auto x = *this; + ++p; + return x; + } + counted_pointer& operator+=(std::ptrdiff_t n) noexcept { + p += n; + return *this; + } + counted_pointer& operator-=(std::ptrdiff_t n) noexcept { + p -= n; + return *this; + } + friend bool operator==(const counted_pointer& x, const counted_pointer& y) + { + return x.p == y.p; + } + friend bool operator!=(const counted_pointer& x, const counted_pointer& y) + { + return x.p != y.p; + } + friend bool operator<(const counted_pointer& x, const counted_pointer& y) + { + return x.p < y.p; + } + friend bool operator<=(const counted_pointer& x, const counted_pointer& y) + { + return x.p <= y.p; + } + friend bool operator>(const counted_pointer& x, const counted_pointer& y) + { + return x.p > y.p; + } + friend bool operator>=(const counted_pointer& x, const counted_pointer& y) + { + return x.p >= y.p; + } + + template + static counted_pointer pointer_to(Q& x) noexcept { + return std::addressof(x); + } + + private: + T* p; + }; + + template + struct counted_pointer_allocator { + using value_type = T; + using pointer = counted_pointer; + + counted_pointer_allocator() = default; + template + counted_pointer_allocator(const counted_pointer_allocator&) noexcept {} + + template + bool operator==(const counted_pointer_allocator&) const noexcept { + return true; + } + + template + bool operator!=(const counted_pointer_allocator&) const noexcept { + return false; + } + + pointer allocate(std::size_t n) const { + return std::allocator().allocate(n); + } + + void deallocate(pointer p, std::size_t n) const noexcept { + std::allocator().deallocate(p, n); + } + }; + + template + void fancy_pointer_noleak_test(T*, test::random_generator const& generator) + { + // https://github.com/boostorg/unordered/issues/201 + + auto const pointer_count = counted_pointer_count; + { + test::random_values v(1000, generator); + T x(v.begin(), v.end()); + (void)x.begin(); + } + BOOST_TEST_EQ(pointer_count, counted_pointer_count); + } + + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set >* test_flat_set; + boost::unordered_node_set >* test_node_set; + boost::unordered_flat_map > >* test_flat_map; + boost::unordered_node_map > >* test_node_map; + + UNORDERED_TEST(fancy_pointer_noleak_test, + ((test_flat_set)(test_node_set)(test_flat_map)(test_node_map)) + ((default_generator))) +#else + boost::unordered_set >* test_set; + boost::unordered_multiset >* test_multiset; + boost::unordered_map > >* test_map; + boost::unordered_multimap > >* test_multimap; + + UNORDERED_TEST(fancy_pointer_noleak_test, + ((test_set)(test_multiset)(test_map)(test_multimap)) + ((default_generator))) +#endif + +} // namespace + +RUN_TESTS_QUIET()