forked from boostorg/unordered
Feature/concurrent node containers (#271)
* added concurrent node containers
* removed spurious typename
* added missing includes
* avoided unused param warning
* worked around Clang bug
* s/{}/() to work around GCC4.8 problems with aggregate initialization
* used /bigobj for cfoa/visit_tests.cpp
* suppressed localized maybe-uninitialized warnings
* fixed comments
* added /bigobj to cfoa/insert_tests.cpp
* instrumented double exact comparison to spot a spurious error
* fixed pedantic error
* refactored byte_span machinery
* compromised on sub-epsilon equality for doubles that should be identical
* documented boost::concurrent_node_(map|set)
* added concurrent_node_set
* added missing AlternativeType
* tested empty node insertion
* tested node_handle allocator management
* added nonassignable_allocator and node_handle_allocator_swap_tests
* fixed warning disabling
* silenced spurious GCC warning
* broadened scope of previous pragma
* broadened even more
* worked around spurious constexpr-related msvc-14.0 bug
https://godbolt.org/z/v78545Ebf
* added workaround back
* replaced previous workaround with built-in one
* added workaround back on top of built-in solution (which doesn't work 100% of the time)
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Copyright 2024 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at htT://www.boost.org/LICENSE_1_0.txt)
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#else
|
||||
#include "../helpers/unordered.hpp"
|
||||
#endif
|
||||
|
||||
#include "../helpers/helpers.hpp"
|
||||
#include "../helpers/test.hpp"
|
||||
#include <boost/type_traits/make_void.hpp>
|
||||
#include <memory>
|
||||
@@ -62,7 +65,16 @@ template <class Container> void test_explicit_alloc_ctor_extract(
|
||||
template <class Container> void test_explicit_alloc_ctor_extract(
|
||||
Container& c, std::true_type)
|
||||
{
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
typename Container::key_type k;
|
||||
c.cvisit_while([&](typename Container::value_type const & x) {
|
||||
k = test::get_key<Container>(x);
|
||||
return false;
|
||||
});
|
||||
auto n = c.extract(k);
|
||||
#else
|
||||
auto n = c.extract(c.begin());
|
||||
#endif
|
||||
c.insert(std::move(n));
|
||||
n = c.extract(typename Container::key_type());
|
||||
c.insert(std::move(n));
|
||||
@@ -122,8 +134,13 @@ UNORDERED_AUTO_TEST (explicit_alloc_ctor) {
|
||||
test_explicit_alloc_ctor<boost::concurrent_flat_map<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
explicit_allocator<std::pair<const int, int> > > >();
|
||||
test_explicit_alloc_ctor<boost::concurrent_node_map<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
explicit_allocator<std::pair<const int, int> > > >();
|
||||
test_explicit_alloc_ctor<boost::concurrent_flat_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
|
||||
test_explicit_alloc_ctor<boost::concurrent_node_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
|
||||
#elif defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
test_explicit_alloc_ctor<boost::unordered_flat_map<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
// Copyright (C) 2024 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 <boost/config.hpp>
|
||||
|
||||
#if defined(BOOST_GCC)
|
||||
// Spurious maybe-uninitialized warnings with allocators contained
|
||||
// in node handles.
|
||||
// Maybe related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108230
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#else
|
||||
#include "../helpers/unordered.hpp"
|
||||
#endif
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace {
|
||||
template <class T> struct nonassignable_allocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
nonassignable_allocator() = default;
|
||||
nonassignable_allocator(nonassignable_allocator const&) = default;
|
||||
|
||||
template <class U>
|
||||
nonassignable_allocator(nonassignable_allocator<U> const&) {}
|
||||
|
||||
nonassignable_allocator& operator=(nonassignable_allocator const&) = delete;
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) { ::operator delete(p); }
|
||||
|
||||
bool operator==(nonassignable_allocator const&) const { return true; }
|
||||
bool operator!=(nonassignable_allocator const&) const { return false; }
|
||||
};
|
||||
|
||||
template <class T> struct pocx_allocator
|
||||
{
|
||||
int x_;
|
||||
|
||||
using value_type = T;
|
||||
using propagate_on_container_copy_assignment = std::true_type;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using propagate_on_container_swap = std::true_type;
|
||||
|
||||
pocx_allocator() : x_{-1} {}
|
||||
pocx_allocator(pocx_allocator const&) = default;
|
||||
pocx_allocator(int const x) : x_{x} {}
|
||||
|
||||
template <class U>
|
||||
pocx_allocator(pocx_allocator<U> const& rhs) : x_{rhs.x_}
|
||||
{
|
||||
}
|
||||
|
||||
pocx_allocator& operator=(pocx_allocator const&) = default;
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) { ::operator delete(p); }
|
||||
|
||||
bool operator==(pocx_allocator const& rhs) const { return x_ == rhs.x_; }
|
||||
bool operator!=(pocx_allocator const& rhs) const { return x_ != rhs.x_; }
|
||||
};
|
||||
|
||||
template <typename Container, typename Allocator>
|
||||
struct replace_allocator_impl;
|
||||
|
||||
template <typename Container, typename Allocator>
|
||||
using replace_allocator =
|
||||
typename replace_allocator_impl<Container, Allocator>::type;
|
||||
|
||||
template <
|
||||
typename K, typename H, typename P, typename A,
|
||||
template <typename, typename, typename, typename> class Set,
|
||||
typename Allocator
|
||||
>
|
||||
struct replace_allocator_impl<Set<K, H, P, A>, Allocator>
|
||||
{
|
||||
using type = Set<
|
||||
K, H, P, boost::allocator_rebind_t<Allocator, K> >;
|
||||
};
|
||||
|
||||
template <
|
||||
typename K, typename H, typename T, typename P, typename A,
|
||||
template <typename, typename, typename, typename, typename> class Map,
|
||||
typename Allocator
|
||||
>
|
||||
struct replace_allocator_impl<Map<K, T, H, P, A>, Allocator>
|
||||
{
|
||||
using type = Map<
|
||||
K, T, H, P,
|
||||
boost::allocator_rebind_t<Allocator, std::pair<K const, T> > >;
|
||||
};
|
||||
|
||||
template<typename X, typename Allocator>
|
||||
void node_handle_allocator_tests(
|
||||
X*, std::pair<Allocator, Allocator> allocators)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
using replaced_allocator_container = replace_allocator<X, Allocator>;
|
||||
using node_type = typename replaced_allocator_container::node_type;
|
||||
|
||||
replaced_allocator_container x1(allocators.first);
|
||||
node_type nh;
|
||||
|
||||
x1.emplace(value_type());
|
||||
nh = x1.extract(0);
|
||||
|
||||
BOOST_TEST(!nh.empty());
|
||||
BOOST_TEST(nh.get_allocator() == x1.get_allocator());
|
||||
|
||||
replaced_allocator_container x2(allocators.second);
|
||||
|
||||
x2.emplace(value_type());
|
||||
nh = x2.extract(0);
|
||||
|
||||
BOOST_TEST(!nh.empty());
|
||||
BOOST_TEST(nh.get_allocator() == x2.get_allocator());
|
||||
}
|
||||
|
||||
template<typename X, typename Allocator>
|
||||
void node_handle_allocator_swap_tests(
|
||||
X*, std::pair<Allocator, Allocator> allocators)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
using replaced_allocator_container = replace_allocator<X, Allocator>;
|
||||
using node_type = typename replaced_allocator_container::node_type;
|
||||
|
||||
replaced_allocator_container x1(allocators.first), x2(allocators.second);
|
||||
x1.emplace(value_type());
|
||||
x2.emplace(value_type());
|
||||
|
||||
node_type nh1, nh2;
|
||||
|
||||
nh1 = x1.extract(0);
|
||||
swap(nh1, nh2);
|
||||
|
||||
BOOST_TEST(nh1.empty());
|
||||
BOOST_TEST(!nh2.empty());
|
||||
BOOST_TEST(nh2.get_allocator() == x1.get_allocator());
|
||||
|
||||
nh1 = x2.extract(0);
|
||||
swap(nh1, nh2);
|
||||
|
||||
BOOST_TEST(!nh1.empty());
|
||||
BOOST_TEST(nh1.get_allocator() == x1.get_allocator());
|
||||
BOOST_TEST(!nh2.empty());
|
||||
BOOST_TEST(nh2.get_allocator() == x2.get_allocator());
|
||||
}
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1900)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4592) // symbol will be dynamically initialized
|
||||
#endif
|
||||
|
||||
std::pair<
|
||||
std::allocator<int>, std::allocator<int> > test_std_allocators({},{});
|
||||
std::pair<
|
||||
nonassignable_allocator<int>,
|
||||
nonassignable_allocator<int> > test_nonassignable_allocators({},{});
|
||||
std::pair<
|
||||
pocx_allocator<int>, pocx_allocator<int> > test_pocx_allocators(5,6);
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1900)
|
||||
#pragma warning(pop) // C4592
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
boost::unordered_node_map<int, int>* test_map;
|
||||
boost::unordered_node_set<int>* test_set;
|
||||
#elif defined(BOOST_UNORDERED_CFOA_TESTS)
|
||||
boost::concurrent_node_map<int, int>* test_map;
|
||||
boost::concurrent_node_set<int>* test_set;
|
||||
#else
|
||||
boost::unordered_map<int, int>* test_map;
|
||||
boost::unordered_set<int>* test_set;
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
node_handle_allocator_tests,
|
||||
((test_map)(test_set))
|
||||
((test_std_allocators)(test_nonassignable_allocators)
|
||||
(test_pocx_allocators)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
node_handle_allocator_swap_tests,
|
||||
((test_map)(test_set))
|
||||
((test_std_allocators)(test_pocx_allocators)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -33,12 +33,23 @@ namespace pmr_allocator_tests {
|
||||
test_string_flat_map;
|
||||
static boost::unordered::pmr::concurrent_flat_map<pmr_string, pmr_string>*
|
||||
test_pmr_string_flat_map;
|
||||
static boost::unordered::pmr::concurrent_node_map<std::string, std::string>*
|
||||
test_string_node_map;
|
||||
static boost::unordered::pmr::concurrent_node_map<pmr_string, pmr_string>*
|
||||
test_pmr_string_node_map;
|
||||
static boost::unordered::pmr::concurrent_flat_set<std::string>*
|
||||
test_string_flat_set;
|
||||
static boost::unordered::pmr::concurrent_flat_set<pmr_string>*
|
||||
test_pmr_string_flat_set;
|
||||
static boost::unordered::pmr::concurrent_node_set<std::string>*
|
||||
test_string_node_set;
|
||||
static boost::unordered::pmr::concurrent_node_set<pmr_string>*
|
||||
test_pmr_string_node_set;
|
||||
#define PMR_ALLOCATOR_TESTS_ARGS \
|
||||
((test_string_flat_map)(test_pmr_string_flat_map)(test_string_flat_set)(test_pmr_string_flat_set))
|
||||
((test_string_flat_map)(test_pmr_string_flat_map) \
|
||||
(test_string_node_map)(test_pmr_string_node_map) \
|
||||
(test_string_flat_set)(test_pmr_string_flat_set) \
|
||||
(test_string_node_set)(test_pmr_string_node_set))
|
||||
#elif defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
static boost::unordered::pmr::unordered_flat_map<std::string, std::string>*
|
||||
test_string_flat_map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Copyright 2024 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)
|
||||
|
||||
@@ -7,8 +7,12 @@
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
#include "../cfoa/helpers.hpp"
|
||||
#else
|
||||
#include "../helpers/unordered.hpp"
|
||||
@@ -19,6 +23,7 @@
|
||||
#include "../helpers/test.hpp"
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/make_span.hpp>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
template <class T> struct unequal_allocator
|
||||
@@ -45,16 +50,20 @@ template <class T> struct unequal_allocator
|
||||
int n_;
|
||||
};
|
||||
|
||||
bool exact_same(double x, double y)
|
||||
bool esentially_same(double x, double y)
|
||||
{
|
||||
return std::memcmp(
|
||||
reinterpret_cast<void*>(&x), reinterpret_cast<void*>(&y),
|
||||
sizeof(double))==0;
|
||||
// Some optimizer-related issues in GCC X86 result in last-bit differences
|
||||
// on doubles that should otherwise be identical.
|
||||
|
||||
// https://stackoverflow.com/a/253874/213114
|
||||
|
||||
static constexpr double epsilon = 1.0E-6;
|
||||
return fabs(x - y) <= ( (fabs(x) > fabs(y) ? fabs(x) : fabs(y)) * epsilon);
|
||||
}
|
||||
|
||||
bool not_exact_same(double x, double y)
|
||||
bool not_esentially_same(double x, double y)
|
||||
{
|
||||
return !exact_same(x, y);
|
||||
return !esentially_same(x, y);
|
||||
}
|
||||
|
||||
enum check_stats_contition
|
||||
@@ -69,19 +78,19 @@ void check_stat(const Stats& s, check_stats_contition cond)
|
||||
{
|
||||
switch (cond) {
|
||||
case stats_empty:
|
||||
BOOST_TEST(exact_same(s.average, 0.0));
|
||||
BOOST_TEST(exact_same(s.variance, 0.0));
|
||||
BOOST_TEST(exact_same(s.deviation, 0.0));
|
||||
BOOST_TEST(esentially_same(s.average, 0.0));
|
||||
BOOST_TEST(esentially_same(s.variance, 0.0));
|
||||
BOOST_TEST(esentially_same(s.deviation, 0.0));
|
||||
break;
|
||||
case stats_full:
|
||||
BOOST_TEST_GT(s.average, 0.0);
|
||||
if(not_exact_same(s.variance, 0.0)) {
|
||||
if(not_esentially_same(s.variance, 0.0)) {
|
||||
BOOST_TEST_GT(s.variance, 0.0);
|
||||
BOOST_TEST_GT(s.deviation, 0.0);
|
||||
}
|
||||
break;
|
||||
case stats_mostly_full:
|
||||
if(not_exact_same(s.variance, 0.0)) {
|
||||
if(not_esentially_same(s.variance, 0.0)) {
|
||||
BOOST_TEST_GT(s.average, 0.0);
|
||||
BOOST_TEST_GT(s.variance, 0.0);
|
||||
BOOST_TEST_GT(s.deviation, 0.0);
|
||||
@@ -94,9 +103,9 @@ void check_stat(const Stats& s, check_stats_contition cond)
|
||||
|
||||
template <class Stats> void check_stat(const Stats& s1, const Stats& s2)
|
||||
{
|
||||
BOOST_TEST(exact_same(s1.average, s2.average));
|
||||
BOOST_TEST(exact_same(s1.variance, s2.variance));
|
||||
BOOST_TEST(exact_same(s1.deviation, s2.deviation));
|
||||
BOOST_TEST(esentially_same(s1.average, s2.average));
|
||||
BOOST_TEST(esentially_same(s1.variance, s2.variance));
|
||||
BOOST_TEST(esentially_same(s1.deviation, s2.deviation));
|
||||
}
|
||||
|
||||
template <class Stats>
|
||||
@@ -345,15 +354,28 @@ UNORDERED_AUTO_TEST (stats_) {
|
||||
boost::concurrent_flat_map<
|
||||
int, int, boost::hash<int>, std::equal_to<int>,
|
||||
unequal_allocator< std::pair< const int, int> >>>();
|
||||
test_stats<
|
||||
boost::concurrent_node_map<
|
||||
int, int, boost::hash<int>, std::equal_to<int>,
|
||||
unequal_allocator< std::pair< const int, int> >>>();
|
||||
test_stats<
|
||||
boost::concurrent_flat_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, unequal_allocator<int>>>();
|
||||
test_stats<
|
||||
boost::concurrent_node_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, unequal_allocator<int>>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_flat_map<int, int>,
|
||||
boost::concurrent_flat_map<int, int>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_node_map<int, int>,
|
||||
boost::concurrent_node_map<int, int>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_flat_set<int>,
|
||||
boost::concurrent_flat_set<int>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_node_set<int>,
|
||||
boost::concurrent_node_set<int>>();
|
||||
#elif defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
test_stats<
|
||||
boost::unordered_flat_map<
|
||||
|
||||
Reference in New Issue
Block a user