mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
add: [concur] ut for prod-cons
This commit is contained in:
parent
4981ce7b9c
commit
8540de7797
@ -95,14 +95,14 @@ struct producer<trans::unicast, relation::single> {
|
|||||||
auto f_ct = elem.get_flag();
|
auto f_ct = elem.get_flag();
|
||||||
/// @remark Verify index.
|
/// @remark Verify index.
|
||||||
if ((f_ct != state::invalid_value) &&
|
if ((f_ct != state::invalid_value) &&
|
||||||
(f_ct != static_cast<state::flag_t>(w_idx))) {
|
(f_ct != w_idx)) {
|
||||||
return false; // full
|
return false; // full
|
||||||
}
|
}
|
||||||
/// @remark Get a valid index and iterate backwards.
|
/// @remark Get a valid index and iterate backwards.
|
||||||
ctx.w_idx += 1;
|
ctx.w_idx += 1;
|
||||||
/// @remark Set data & flag.
|
/// @remark Set data & flag.
|
||||||
elem.set_data(std::forward<U>(src));
|
elem.set_data(std::forward<U>(src));
|
||||||
elem.set_flag(static_cast<state::flag_t>(~w_idx));
|
elem.set_flag(static_cast<index_t>(~w_idx));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -131,12 +131,12 @@ struct producer<trans::unicast, relation::multi> {
|
|||||||
return false; // full
|
return false; // full
|
||||||
}
|
}
|
||||||
/// @remark Get a valid index and iterate backwards.
|
/// @remark Get a valid index and iterate backwards.
|
||||||
if (!ctx.w_idx.compare_exchange_week(w_idx, w_idx + 1, std::memory_order_acq_rel)) {
|
if (!ctx.w_idx.compare_exchange_weak(w_idx, w_idx + 1, std::memory_order_acq_rel)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/// @remark Set data & flag.
|
/// @remark Set data & flag.
|
||||||
elem.set_data(std::forward<U>(src));
|
elem.set_data(std::forward<U>(src));
|
||||||
elem.set_flag(~w_idx);
|
elem.set_flag(static_cast<index_t>(~w_idx));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,7 +160,7 @@ struct consumer<trans::unicast, relation::single> {
|
|||||||
auto &elem = elems[r_cur];
|
auto &elem = elems[r_cur];
|
||||||
auto f_ct = elem.get_flag();
|
auto f_ct = elem.get_flag();
|
||||||
/// @remark Verify index.
|
/// @remark Verify index.
|
||||||
if (f_ct != ~r_idx) {
|
if (f_ct != static_cast<index_t>(~r_idx)) {
|
||||||
return false; // empty
|
return false; // empty
|
||||||
}
|
}
|
||||||
/// @remark Get a valid index and iterate backwards.
|
/// @remark Get a valid index and iterate backwards.
|
||||||
@ -191,11 +191,11 @@ struct consumer<trans::unicast, relation::multi> {
|
|||||||
auto &elem = elems[r_cur];
|
auto &elem = elems[r_cur];
|
||||||
auto f_ct = elem.get_flag();
|
auto f_ct = elem.get_flag();
|
||||||
/// @remark Verify index.
|
/// @remark Verify index.
|
||||||
if (f_ct != ~r_idx) {
|
if (f_ct != static_cast<index_t>(~r_idx)) {
|
||||||
return false; // empty
|
return false; // empty
|
||||||
}
|
}
|
||||||
/// @remark Get a valid index and iterate backwards.
|
/// @remark Get a valid index and iterate backwards.
|
||||||
if (!ctx.r_idx.compare_exchange_week(r_idx, r_idx + 1, std::memory_order_acq_rel)) {
|
if (!ctx.r_idx.compare_exchange_weak(r_idx, r_idx + 1, std::memory_order_acq_rel)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/// @remark Get data & set flag.
|
/// @remark Get data & set flag.
|
||||||
@ -240,7 +240,7 @@ struct consumer<trans::broadcast, relation::multi> {
|
|||||||
template <typename TransModT, typename ProdModT, typename ConsModT>
|
template <typename TransModT, typename ProdModT, typename ConsModT>
|
||||||
struct prod_cons : producer<TransModT, ProdModT>
|
struct prod_cons : producer<TransModT, ProdModT>
|
||||||
, consumer<TransModT, ConsModT> {
|
, consumer<TransModT, ConsModT> {
|
||||||
|
|
||||||
/// @brief Mixing producer and consumer context definitions.
|
/// @brief Mixing producer and consumer context definitions.
|
||||||
struct context : producer<TransModT, ProdModT>::context_impl
|
struct context : producer<TransModT, ProdModT>::context_impl
|
||||||
, consumer<TransModT, ConsModT>::context_impl {
|
, consumer<TransModT, ConsModT>::context_impl {
|
||||||
@ -249,6 +249,10 @@ struct prod_cons : producer<TransModT, ProdModT>
|
|||||||
constexpr context(index_t cs) noexcept
|
constexpr context(index_t cs) noexcept
|
||||||
: circ_size(cs) {}
|
: circ_size(cs) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr context(::LIBIMP_::span<element<T>> elems) noexcept
|
||||||
|
: circ_size(static_cast<index_t>(elems.size())) {}
|
||||||
|
|
||||||
constexpr bool valid() const noexcept {
|
constexpr bool valid() const noexcept {
|
||||||
/// @remark circ_size must be a power of two.
|
/// @remark circ_size must be a power of two.
|
||||||
return (circ_size > 1) && ((circ_size & (circ_size - 1)) == 0);
|
return (circ_size > 1) && ((circ_size & (circ_size - 1)) == 0);
|
||||||
|
|||||||
40
include/libimp/nameof.h
Normal file
40
include/libimp/nameof.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* @file libimp/nameof.h
|
||||||
|
* @author mutouyun (orz@orzz.org)
|
||||||
|
* @brief Gets the name string of a type.
|
||||||
|
* @date 2022-11-26
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "libimp/def.h"
|
||||||
|
#include "libimp/span.h"
|
||||||
|
|
||||||
|
LIBIMP_NAMESPACE_BEG_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The conventional way to obtain demangled symbol name.
|
||||||
|
* @see https://www.boost.org/doc/libs/1_80_0/libs/core/doc/html/core/demangle.html
|
||||||
|
*
|
||||||
|
* @param name the mangled name
|
||||||
|
* @return std::string a human-readable demangled type name
|
||||||
|
*/
|
||||||
|
std::string demangle(span<char const> name) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an implementation defined string containing the name of the type.
|
||||||
|
* @see https://en.cppreference.com/w/cpp/types/type_info/name
|
||||||
|
*
|
||||||
|
* @tparam T a type
|
||||||
|
* @return std::string a human-readable demangled type name
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
std::string nameof() noexcept {
|
||||||
|
auto c_str_name = typeid(T).name();
|
||||||
|
return demangle({c_str_name, std::strlen(c_str_name)});
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBIMP_NAMESPACE_END_
|
||||||
7
src/libimp/nameof.cpp
Normal file
7
src/libimp/nameof.cpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
#include "libimp/detect_plat.h"
|
||||||
|
#if defined(LIBIMP_CC_GNUC)
|
||||||
|
# include "libimp/platform/gnuc/demangle.h"
|
||||||
|
#else
|
||||||
|
# include "libimp/platform/win/demangle.h"
|
||||||
|
#endif
|
||||||
41
src/libimp/platform/gnuc/demangle.h
Normal file
41
src/libimp/platform/gnuc/demangle.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* @file libimp/platform/gnuc/demangle.h
|
||||||
|
* @author mutouyun (orz@orzz.org)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cxxabi.h> // abi::__cxa_demangle
|
||||||
|
|
||||||
|
#include <cstdlib> // std::malloc
|
||||||
|
|
||||||
|
#include "libimp/def.h"
|
||||||
|
#include "libimp/nameof.h"
|
||||||
|
#include "libimp/log.h"
|
||||||
|
|
||||||
|
LIBIMP_NAMESPACE_BEG_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The conventional way to obtain demangled symbol name.
|
||||||
|
* @see https://www.boost.org/doc/libs/1_80_0/libs/core/doc/html/core/demangle.html
|
||||||
|
*
|
||||||
|
* @param name the mangled name
|
||||||
|
* @return std::string a human-readable demangled type name
|
||||||
|
*/
|
||||||
|
std::string demangle(span<char const> name) noexcept {
|
||||||
|
LIBIMP_LOG_();
|
||||||
|
/// @see https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html
|
||||||
|
std::size_t sz = name.size() + 1;
|
||||||
|
char *buffer = static_cast<char *>(std::malloc(sz));
|
||||||
|
int status = 0;
|
||||||
|
char *realname = abi::__cxa_demangle(name.data(), buffer, &sz, &status);
|
||||||
|
if (realname == nullptr) {
|
||||||
|
log.error("failed: abi::__cxa_demangle(sz = {}), status = {}", sz, status);
|
||||||
|
std::free(buffer);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::string demangled(realname, sz);
|
||||||
|
std::free(realname);
|
||||||
|
return demangled;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBIMP_NAMESPACE_END_
|
||||||
16
src/libimp/platform/win/demangle.h
Normal file
16
src/libimp/platform/win/demangle.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @file libimp/platform/win/demangle.h
|
||||||
|
* @author mutouyun (orz@orzz.org)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "libimp/def.h"
|
||||||
|
#include "libimp/nameof.h"
|
||||||
|
|
||||||
|
LIBIMP_NAMESPACE_BEG_
|
||||||
|
|
||||||
|
std::string demangle(span<char const> name) noexcept {
|
||||||
|
return std::string(name.data(), name.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBIMP_NAMESPACE_END_
|
||||||
@ -1,12 +1,13 @@
|
|||||||
#include "libimp/detect_plat.h"
|
|
||||||
#if defined(LIBIMP_OS_WIN)
|
|
||||||
#include "libimp/platform/win/system.h"
|
|
||||||
#else
|
|
||||||
#include "libimp/platform/posix/system.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
|
||||||
|
#include "libimp/detect_plat.h"
|
||||||
|
#if defined(LIBIMP_OS_WIN)
|
||||||
|
# include "libimp/platform/win/system.h"
|
||||||
|
#else
|
||||||
|
# include "libimp/platform/posix/system.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
LIBIMP_NAMESPACE_BEG_
|
LIBIMP_NAMESPACE_BEG_
|
||||||
namespace sys {
|
namespace sys {
|
||||||
|
|
||||||
|
|||||||
@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
#include "libimp/detect_plat.h"
|
#include "libimp/detect_plat.h"
|
||||||
#if defined(LIBIMP_OS_WIN)
|
#if defined(LIBIMP_OS_WIN)
|
||||||
#include "libipc/platform/win/shm_impl.h"
|
# include "libipc/platform/win/shm_impl.h"
|
||||||
#else
|
#else
|
||||||
#include "libipc/platform/posix/shm_impl.h"
|
# include "libipc/platform/posix/shm_impl.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LIBIPC_NAMESPACE_BEG_
|
LIBIPC_NAMESPACE_BEG_
|
||||||
|
|||||||
@ -1,10 +1,17 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <atomic>
|
||||||
|
#include <typeinfo>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#include "libconcur/concurrent.h"
|
#include "libconcur/concurrent.h"
|
||||||
|
#include "libimp/countof.h"
|
||||||
|
#include "libimp/log.h"
|
||||||
|
#include "libimp/nameof.h"
|
||||||
|
|
||||||
TEST(concurrent, cache_line_size) {
|
TEST(concurrent, cache_line_size) {
|
||||||
std::cout << concur::cache_line_size << "\n";
|
std::cout << concur::cache_line_size << "\n";
|
||||||
@ -79,4 +86,75 @@ TEST(concurrent, trunc_index) {
|
|||||||
EXPECT_EQ(concur::trunc_index(context{2147483648u}, 16), 16);
|
EXPECT_EQ(concur::trunc_index(context{2147483648u}, 16), 16);
|
||||||
EXPECT_EQ(concur::trunc_index(context{2147483648u}, 111), 111);
|
EXPECT_EQ(concur::trunc_index(context{2147483648u}, 111), 111);
|
||||||
EXPECT_EQ(concur::trunc_index(context{2147483648u}, -1), 2147483647);
|
EXPECT_EQ(concur::trunc_index(context{2147483648u}, -1), 2147483647);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename PC>
|
||||||
|
void test_concur(std::size_t np, std::size_t nc, std::size_t k) {
|
||||||
|
LIBIMP_LOG_();
|
||||||
|
log.info("\n\tStart with: {}, {} producers, {} consumers...", imp::nameof<PC>(), np, nc);
|
||||||
|
|
||||||
|
constexpr static std::uint32_t loop_size = 100'0000;
|
||||||
|
|
||||||
|
concur::element<std::uint64_t> circ[32] {};
|
||||||
|
PC pc;
|
||||||
|
PC::context ctx {imp::make_span(circ)};
|
||||||
|
ASSERT_TRUE(ctx.valid());
|
||||||
|
|
||||||
|
std::atomic<std::uint64_t> sum {0};
|
||||||
|
std::atomic<std::size_t> running {np};
|
||||||
|
|
||||||
|
auto prod_call = [&](std::size_t n) {
|
||||||
|
for (std::uint32_t i = 1; i <= loop_size; ++i) {
|
||||||
|
std::this_thread::yield();
|
||||||
|
while (!pc.enqueue(imp::make_span(circ), ctx, i)) {
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
if (i % (loop_size / 10) == 0) {
|
||||||
|
log.info("[{}] put count: {}", n, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--running;
|
||||||
|
};
|
||||||
|
auto cons_call = [&] {
|
||||||
|
for (;;) {
|
||||||
|
std::this_thread::yield();
|
||||||
|
std::uint64_t i;
|
||||||
|
while (!pc.dequeue(imp::make_span(circ), ctx, i)) {
|
||||||
|
if (running == 0) return;
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
sum += i;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::thread> prods(np);
|
||||||
|
for (std::size_t n = 0; n < np; ++n) prods[n] = std::thread(prod_call, n);
|
||||||
|
std::vector<std::thread> conss(nc);
|
||||||
|
for (auto &c : conss) c = std::thread(cons_call);
|
||||||
|
|
||||||
|
for (auto &p : prods) p.join();
|
||||||
|
for (auto &c : conss) c.join();
|
||||||
|
|
||||||
|
EXPECT_EQ(sum, k * np * (loop_size * std::uint64_t(loop_size + 1)) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(concurrent, prod_cons) {
|
||||||
|
using namespace concur;
|
||||||
|
|
||||||
|
test_concur<prod_cons<trans::unicast, relation::single, relation::single>>(1, 1, 1);
|
||||||
|
test_concur<prod_cons<trans::unicast, relation::single, relation::multi >>(1, 1, 1);
|
||||||
|
test_concur<prod_cons<trans::unicast, relation::multi , relation::single>>(1, 1, 1);
|
||||||
|
test_concur<prod_cons<trans::unicast, relation::multi , relation::multi >>(1, 1, 1);
|
||||||
|
|
||||||
|
test_concur<prod_cons<trans::unicast, relation::multi , relation::single>>(8, 1, 1);
|
||||||
|
test_concur<prod_cons<trans::unicast, relation::multi , relation::multi >>(8, 1, 1);
|
||||||
|
|
||||||
|
test_concur<prod_cons<trans::unicast, relation::single, relation::multi >>(1, 8, 1);
|
||||||
|
test_concur<prod_cons<trans::unicast, relation::multi , relation::multi >>(1, 8, 1);
|
||||||
|
|
||||||
|
test_concur<prod_cons<trans::unicast, relation::multi , relation::multi >>(8, 8, 1);
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user