test interprocess concurrency (#258)

* added cfoa_interprocess_concurrency_tests

* avoided C++14 digit separators

* stopped using exit codes to pass numerical info to parent

* Reorder includes to make msvc-14.0 happy

* made cumulative_stats interprocess concurrency safe

* disabled interprocess_concurrency_tests for Clang 3.5-8
(Boost.Process compile error)

* made test names shorter for the benefit of MINGW32

---------

Co-authored-by: Peter Dimov <pdimov@gmail.com>
This commit is contained in:
joaquintides 2024-06-22 09:13:30 +02:00 committed by GitHub
parent a166a56401
commit e3818afd45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 146 additions and 7 deletions

View File

@ -170,7 +170,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
</Expand>
</Type>
<Type Name="boost::unordered::detail::cumulative_stats&lt;*&gt;" Inheritable="true">
<Type Name="boost::unordered::detail::foa::cumulative_stats&lt;*&gt;" Inheritable="true">
<Intrinsic Name="bit_cast_to_double" Expression="*reinterpret_cast&lt;double*&gt;(&amp;i)">
<Parameter Name="i" Type="uint64_t" />
</Intrinsic>

View File

@ -41,7 +41,7 @@
#include <utility>
#if defined(BOOST_UNORDERED_ENABLE_STATS)
#include <boost/unordered/detail/cumulative_stats.hpp>
#include <boost/unordered/detail/foa/cumulative_stats.hpp>
#endif
#if !defined(BOOST_UNORDERED_DISABLE_SSE2)

View File

@ -6,8 +6,8 @@
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_DETAIL_CUMULATIVE_STATS_HPP
#define BOOST_UNORDERED_DETAIL_CUMULATIVE_STATS_HPP
#ifndef BOOST_UNORDERED_DETAIL_FOA_CUMULATIVE_STATS_HPP
#define BOOST_UNORDERED_DETAIL_FOA_CUMULATIVE_STATS_HPP
#include <array>
#include <boost/config.hpp>
@ -16,12 +16,14 @@
#include <cstddef>
#if defined(BOOST_HAS_THREADS)
#include <boost/unordered/detail/foa/rw_spinlock.hpp>
#include <mutex>
#endif
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
/* Cumulative one-pass calculation of the average, variance and deviation of
* running sequences.
@ -117,7 +119,7 @@ template<std::size_t N>
class concurrent_cumulative_stats:cumulative_stats<N>
{
using super=cumulative_stats<N>;
using lock_guard=std::lock_guard<std::mutex>;
using lock_guard=std::lock_guard<rw_spinlock>;
public:
using summary=typename super::summary;
@ -157,7 +159,7 @@ public:
private:
concurrent_cumulative_stats(const super& x,lock_guard&&):super{x}{}
mutable std::mutex mut;
mutable rw_spinlock mut;
};
#else
@ -167,6 +169,7 @@ using concurrent_cumulative_stats=cumulative_stats<N>;
#endif
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */

View File

@ -1,6 +1,7 @@
# Copyright 2006-2008 Daniel James.
# Copyright 2022-2023 Christian Mazakas
# 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)
@ -359,6 +360,27 @@ run cfoa/serialization_tests.cpp
<library>/boost//serialization/<warnings>off
: cfoa_serialization_tests ;
rule make_cfoa_interprocess_concurrency_tests ( name : defines ? )
{
run cfoa/interprocess_concurrency_tests.cpp /boost/filesystem//boost_filesystem : :
: <define>$(defines)
<warnings>off
<link>static
<toolset>clang-3.5:<build>no # Boost.Process does not compile
<toolset>clang-3.6:<build>no # idem
<toolset>clang-3.7:<build>no # idem
<toolset>clang-3.8:<build>no # idem
<target-os>cygwin:<build>no
: $(name) ;
}
make_cfoa_interprocess_concurrency_tests cfoa_interproc_conc_tests ;
make_cfoa_interprocess_concurrency_tests cfoa_interproc_conc_tests_stats
: BOOST_UNORDERED_ENABLE_STATS ;
alias cfoa_tests :
cfoa_$(CFOA_TESTS)
cfoa_serialization_tests ;
cfoa_serialization_tests
cfoa_interproc_conc_tests
cfoa_interproc_conc_tests_stats ;

View File

@ -0,0 +1,114 @@
// 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/unordered/concurrent_flat_map.hpp>
#include <atomic>
#include <boost/core/lightweight_test.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/process/child.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <chrono>
#include <cstdlib>
#include <random>
#include <string>
#include <thread>
#include <vector>
namespace bip=boost::interprocess;
static char const* container_name = "shared_map";
static char const* start_name = "shared_start";
static constexpr int NUM_OPS_PER_CHILD = 10000;
using value_type = std::pair<const int, int>;
using allocator = bip::allocator<
value_type, bip::managed_shared_memory::segment_manager>;
using container = boost::concurrent_flat_map<
int, int, boost::hash<int>, std::equal_to<int>, allocator>;
int parent(const char* exe_name)
{
static constexpr int NUM_CHILDS = 10;
static constexpr std::size_t SEGMENT_SIZE = 64*1024;
std::string segment_name_str = to_string(boost::uuids::random_generator()());
auto* segment_name = segment_name_str.c_str();
struct segment_remover
{
char const* name;
segment_remover(char const* name_) : name(name_)
{
bip::shared_memory_object::remove(name);
}
~segment_remover()
{
bip::shared_memory_object::remove(name);
}
} remover(segment_name);
bip::managed_shared_memory segment(
bip::create_only, segment_name, SEGMENT_SIZE);
container& c = *segment.construct<container>(container_name)(
allocator(segment.get_segment_manager()));
std::atomic_int& start = *segment.construct<std::atomic_int>(start_name)(0);
std::vector<boost::process::child> children;
for (int i = 0; i < NUM_CHILDS; ++i) {
children.emplace_back(exe_name, std::to_string(i), segment_name);
}
start.store(1);
for (auto& child : children) {
child.wait();
BOOST_TEST_EQ(child.exit_code(), 0);
}
int num_ops = 0;
c.cvisit_all([&](const value_type& x) {
num_ops += x.second;
});
BOOST_TEST_EQ(num_ops, NUM_CHILDS * NUM_OPS_PER_CHILD);
return boost::report_errors();
}
int child(int id,const char* segment_name)
{
bip::managed_shared_memory segment(bip::open_only, segment_name);
container& c = *segment.find<container>(container_name).first;
std::atomic_int& start = *segment.find<std::atomic_int>(start_name).first;
while(!start.load()){
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
std::mt19937 rnd((unsigned int) id);
std::geometric_distribution<int> d(0.15);
for(int i = 0; i < NUM_OPS_PER_CHILD; ++i) {
c.emplace_or_visit(d(rnd), 1, [&](value_type& x) {
++x.second;
// artificially increase contention
volatile unsigned int n = 10000;
while(n--) ;
});
}
return 0;
}
int main(int argc, char** argv)
{
if (argc == 1) {
return parent(argv[0]);
}
else {
return child(std::atoi(argv[1]),argv[2]);
}
}