diff --git a/include/libconcur/concurrent.h b/include/libconcur/concurrent.h index 2e3d900..f759533 100644 --- a/include/libconcur/concurrent.h +++ b/include/libconcur/concurrent.h @@ -95,14 +95,14 @@ struct producer { auto f_ct = elem.get_flag(); /// @remark Verify index. if ((f_ct != state::invalid_value) && - (f_ct != static_cast(w_idx))) { + (f_ct != w_idx)) { return false; // full } /// @remark Get a valid index and iterate backwards. ctx.w_idx += 1; /// @remark Set data & flag. elem.set_data(std::forward(src)); - elem.set_flag(static_cast(~w_idx)); + elem.set_flag(static_cast(~w_idx)); return true; } }; @@ -131,12 +131,12 @@ struct producer { return false; // full } /// @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; } /// @remark Set data & flag. elem.set_data(std::forward(src)); - elem.set_flag(~w_idx); + elem.set_flag(static_cast(~w_idx)); return true; } } @@ -160,7 +160,7 @@ struct consumer { auto &elem = elems[r_cur]; auto f_ct = elem.get_flag(); /// @remark Verify index. - if (f_ct != ~r_idx) { + if (f_ct != static_cast(~r_idx)) { return false; // empty } /// @remark Get a valid index and iterate backwards. @@ -191,11 +191,11 @@ struct consumer { auto &elem = elems[r_cur]; auto f_ct = elem.get_flag(); /// @remark Verify index. - if (f_ct != ~r_idx) { + if (f_ct != static_cast(~r_idx)) { return false; // empty } /// @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; } /// @remark Get data & set flag. @@ -240,7 +240,7 @@ struct consumer { template struct prod_cons : producer , consumer { - + /// @brief Mixing producer and consumer context definitions. struct context : producer::context_impl , consumer::context_impl { @@ -249,6 +249,10 @@ struct prod_cons : producer constexpr context(index_t cs) noexcept : circ_size(cs) {} + template + constexpr context(::LIBIMP_::span> elems) noexcept + : circ_size(static_cast(elems.size())) {} + constexpr bool valid() const noexcept { /// @remark circ_size must be a power of two. return (circ_size > 1) && ((circ_size & (circ_size - 1)) == 0); diff --git a/include/libimp/nameof.h b/include/libimp/nameof.h new file mode 100644 index 0000000..84a1dbc --- /dev/null +++ b/include/libimp/nameof.h @@ -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 +#include +#include + +#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 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 +std::string nameof() noexcept { + auto c_str_name = typeid(T).name(); + return demangle({c_str_name, std::strlen(c_str_name)}); +} + +LIBIMP_NAMESPACE_END_ diff --git a/src/libimp/nameof.cpp b/src/libimp/nameof.cpp new file mode 100644 index 0000000..ef6a452 --- /dev/null +++ b/src/libimp/nameof.cpp @@ -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 diff --git a/src/libimp/platform/gnuc/demangle.h b/src/libimp/platform/gnuc/demangle.h new file mode 100644 index 0000000..e1ef540 --- /dev/null +++ b/src/libimp/platform/gnuc/demangle.h @@ -0,0 +1,41 @@ +/** + * @file libimp/platform/gnuc/demangle.h + * @author mutouyun (orz@orzz.org) + */ +#pragma once + +#include // abi::__cxa_demangle + +#include // 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 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(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_ diff --git a/src/libimp/platform/win/demangle.h b/src/libimp/platform/win/demangle.h new file mode 100644 index 0000000..a8468a6 --- /dev/null +++ b/src/libimp/platform/win/demangle.h @@ -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 name) noexcept { + return std::string(name.data(), name.size()); +} + +LIBIMP_NAMESPACE_END_ diff --git a/src/libimp/system.cpp b/src/libimp/system.cpp index 2c1296b..b5b73dc 100644 --- a/src/libimp/system.cpp +++ b/src/libimp/system.cpp @@ -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 "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_ namespace sys { diff --git a/src/libipc/shm.cpp b/src/libipc/shm.cpp index 5804193..5cc1843 100644 --- a/src/libipc/shm.cpp +++ b/src/libipc/shm.cpp @@ -5,9 +5,9 @@ #include "libimp/detect_plat.h" #if defined(LIBIMP_OS_WIN) -#include "libipc/platform/win/shm_impl.h" +# include "libipc/platform/win/shm_impl.h" #else -#include "libipc/platform/posix/shm_impl.h" +# include "libipc/platform/posix/shm_impl.h" #endif LIBIPC_NAMESPACE_BEG_ diff --git a/test/concur/test_concur_concurrent.cpp b/test/concur/test_concur_concurrent.cpp index e07a3f1..f243f1e 100644 --- a/test/concur/test_concur_concurrent.cpp +++ b/test/concur/test_concur_concurrent.cpp @@ -1,10 +1,17 @@ #include +#include +#include +#include +#include #include #include "gtest/gtest.h" #include "libconcur/concurrent.h" +#include "libimp/countof.h" +#include "libimp/log.h" +#include "libimp/nameof.h" TEST(concurrent, cache_line_size) { 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}, 111), 111); EXPECT_EQ(concur::trunc_index(context{2147483648u}, -1), 2147483647); +} + +namespace { + +template +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(), np, nc); + + constexpr static std::uint32_t loop_size = 100'0000; + + concur::element circ[32] {}; + PC pc; + PC::context ctx {imp::make_span(circ)}; + ASSERT_TRUE(ctx.valid()); + + std::atomic sum {0}; + std::atomic 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 prods(np); + for (std::size_t n = 0; n < np; ++n) prods[n] = std::thread(prod_call, n); + std::vector 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>(1, 1, 1); + test_concur>(1, 1, 1); + test_concur>(1, 1, 1); + test_concur>(1, 1, 1); + + test_concur>(8, 1, 1); + test_concur>(8, 1, 1); + + test_concur>(1, 8, 1); + test_concur>(1, 8, 1); + + test_concur>(8, 8, 1); } \ No newline at end of file