From 64fc26733eaccee4474253023a78184b8639b4dd Mon Sep 17 00:00:00 2001 From: mutouyun Date: Fri, 28 Dec 2018 14:53:34 +0800 Subject: [PATCH] use tls::pointer instead of thread_local --- README.md | 7 +++---- build/test.pro | 2 +- src/ipc.cpp | 28 ++++++++++++++++++++++------ src/platform/shm_linux.cpp | 6 ++++-- src/platform/shm_win.cpp | 5 +++-- test/test.h | 22 ++++++++++++++++------ test/test_circ.cpp | 19 ++++++------------- test/test_ipc.cpp | 26 ++++++++++++-------------- 8 files changed, 67 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 33e0873..e1bc46b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ -# cpp-ipc +# cpp-ipc - C++ IPC Library [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/mutouyun/cpp-ipc/blob/master/LICENSE) [![Build Status](https://travis-ci.org/mutouyun/cpp-ipc.svg?branch=master)](https://travis-ci.org/mutouyun/cpp-ipc) - -C++ IPC Library: + A high-performance inter-process communication using shared memory on Linux/Windows. 使用共享内存的跨平台(Linux/Windows,x86/x64/ARM)高性能IPC通讯库。 @@ -10,7 +9,7 @@ A high-performance inter-process communication using shared memory on Linux/Wind * 除STL外,无其他依赖 * 无锁(lock-free)或轻量级shared-spin-lock(`ipc::channel::connect`/`disconnect`) * 底层数据结构为循环数组(circular array),无动态内存分配 - * `ipc::route`支持单生产者多消费者(1vN),`ipc::channel`支持多生产者多消费者 + * `ipc::route`支持单生产者多消费者(1vN),`ipc::channel`支持多生产者多消费者(NvM) ## Performance diff --git a/build/test.pro b/build/test.pro index 0923953..5c39326 100644 --- a/build/test.pro +++ b/build/test.pro @@ -8,7 +8,7 @@ CONFIG -= app_bundle DESTDIR = ../output msvc:QMAKE_CXXFLAGS += /std:c++17 -else:QMAKE_CXXFLAGS += -std=gnu++1z +else:QMAKE_CXXFLAGS += -std=gnu++1z -Wno-unused-function INCLUDEPATH += \ ../test \ diff --git a/src/ipc.cpp b/src/ipc.cpp index abb762a..409cbe3 100644 --- a/src/ipc.cpp +++ b/src/ipc.cpp @@ -10,6 +10,7 @@ #include "def.h" #include "circ_queue.h" #include "shm.h" +#include "tls_pointer.h" namespace { @@ -46,11 +47,21 @@ constexpr queue_t* queue_of(handle_t h) { inline auto& recv_cache() { /* - * the performance of tls::pointer is not good enough - * so regardless of the mingw-crash-problem for the moment + thread_local may have some bugs. + See: https://sourceforge.net/p/mingw-w64/bugs/727/ + https://sourceforge.net/p/mingw-w64/bugs/527/ + https://github.com/Alexpux/MINGW-packages/issues/2519 + https://github.com/ChaiScript/ChaiScript/issues/402 + https://developercommunity.visualstudio.com/content/problem/124121/thread-local-variables-fail-to-be-initialized-when.html + https://software.intel.com/en-us/forums/intel-c-compiler/topic/684827 */ - thread_local std::unordered_map rc; - return rc; + static tls::pointer> rc; + return *rc.create(); +} + +inline auto& queues_cache() { + static tls::pointer> qc; + return *qc.create(); } } // internal-linkage @@ -176,7 +187,7 @@ buff_t multi_recv(F&& upd) { } buff_t recv(handle_t const * hs, std::size_t size) { - thread_local std::vector q_arr(size); + auto& q_arr = queues_cache(); q_arr.clear(); // make the size to 0 for (size_t i = 0; i < size; ++i) { auto que = queue_of(hs[i]); @@ -193,7 +204,12 @@ buff_t recv(handle_t const * hs, std::size_t size) { } buff_t recv(handle_t h) { - return recv(&h, 1); + auto que = queue_of(h); + if (que == nullptr) return {}; + que->connect(); // wouldn't connect twice + return multi_recv([&que] { + return std::make_tuple(&que, 1); + }); } } // namespace ipc diff --git a/src/platform/shm_linux.cpp b/src/platform/shm_linux.cpp index 7693b27..fac43d4 100644 --- a/src/platform/shm_linux.cpp +++ b/src/platform/shm_linux.cpp @@ -10,6 +10,8 @@ #include #include +#include "tls_pointer.h" + namespace { using acc_t = std::atomic_size_t; @@ -23,8 +25,8 @@ constexpr void* mem_of(void* mem) { } inline auto& m2h() { - thread_local std::unordered_map cache; - return cache; + static ipc::tls::pointer> cache; + return *cache.create(); } } // internal-linkage diff --git a/src/platform/shm_win.cpp b/src/platform/shm_win.cpp index f0684e2..b555842 100644 --- a/src/platform/shm_win.cpp +++ b/src/platform/shm_win.cpp @@ -10,6 +10,7 @@ #include #include "def.h" +#include "tls_pointer.h" namespace { @@ -27,8 +28,8 @@ constexpr auto to_tchar(std::string && str) -> IsSame { } inline auto& m2h() { - thread_local std::unordered_map cache; - return cache; + static ipc::tls::pointer> cache; + return *cache.create(); } } // internal-linkage diff --git a/test/test.h b/test/test.h index 270190a..3d0b381 100644 --- a/test/test.h +++ b/test/test.h @@ -6,13 +6,15 @@ #include #include #include -#include +#include +#include #if defined(__GNUC__) # include // abi::__cxa_demangle #endif/*__GNUC__*/ #include "stopwatch.hpp" +#include "spin_lock.hpp" class TestSuite : public QObject { @@ -87,6 +89,8 @@ void benchmark_prod_cons(T* cq) { test_stopwatch sw; test_verify vf { M }; + capo::spin_lock lc; + int cid = 0; for (auto& t : consumers) { t = std::thread{[&, cid] { @@ -95,15 +99,20 @@ void benchmark_prod_cons(T* cq) { int i = 0; tcq.recv(cn, [&](auto&& msg) { // if (i % ((Loops * N) / 10) == 0) { -// std::printf("%d-recving: %d%%\n", cid, (i * 100) / (Loops * N)); +// std::unique_lock guard { lc }; +// std::cout << cid << "-recving: " << (i * 100) / (Loops * N) << "%" << std::endl; // } vf.push_data(cid, msg); ++i; }); -// std::printf("%d-consumer-disconnect\n", cid); +// { +// std::unique_lock guard { lc }; +// std::cout << cid << "-consumer-disconnect" << std::endl; +// } tcq.disconnect(cn); if (++fini_c != std::extent::value) { -// std::printf("%d-consumer-end\n", cid); +// std::unique_lock guard { lc }; +// std::cout << cid << "-consumer-end" << std::endl; return; } sw.print_elapsed(N, M, Loops); @@ -121,10 +130,11 @@ void benchmark_prod_cons(T* cq) { auto cn = tcq.connect_send(); sw.start(); for (int i = 0; i < Loops; ++i) { - tcq.send(cn, { pid, i }); // if (i % (Loops / 10) == 0) { -// std::printf("%d-sending: %d%%\n", pid, i * 100 / Loops); +// std::unique_lock guard { lc }; +// std::cout << pid << "-sending: " << (i * 100 / Loops) << "%" << std::endl; // } + tcq.send(cn, { pid, i }); } if (++fini_p != std::extent::value) return; // quit diff --git a/test/test_circ.cpp b/test/test_circ.cpp index 6b0eb0a..ae7065b 100644 --- a/test/test_circ.cpp +++ b/test/test_circ.cpp @@ -28,18 +28,11 @@ bool operator==(msg_t const & m1, msg_t const & m2) { template <> struct test_verify { - std::unordered_map>* list_; - int lcount_; + std::vector>> list_; - test_verify(int M) { - list_ = new std::remove_reference_t[ - static_cast(lcount_ = M) - ]; - } - - ~test_verify() { - delete [] list_; - } + test_verify(int M) + : list_(static_cast(M)) + {} void prepare(void* pt) { std::cout << "start consumer: " << pt << std::endl; @@ -51,8 +44,8 @@ struct test_verify { void verify(int N, int Loops) { std::cout << "verifying..." << std::endl; - for (int m = 0; m < lcount_; ++m) { - auto& cons_vec = list_[m]; + for (auto& c_dats : list_) { + auto& cons_vec = c_dats; for (int n = 0; n < N; ++n) { auto& vec = cons_vec[n]; QCOMPARE(vec.size(), static_cast(Loops)); diff --git a/test/test_ipc.cpp b/test/test_ipc.cpp index 50b48f0..c427cbe 100644 --- a/test/test_ipc.cpp +++ b/test/test_ipc.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include "stopwatch.hpp" #include "spin_lock.hpp" @@ -35,14 +34,13 @@ constexpr int LoopCount = 100000; template struct test_verify { - std::unordered_map> list_; - int lcount_; + std::vector> list_; - test_verify(int M) : lcount_{ M } {} + test_verify(int M) + : list_(static_cast(M)) + {} - void prepare(void* pt) { - std::cout << "start consumer: " << pt << std::endl; - } + void prepare(void* /*pt*/) {} void push_data(int cid, ipc::buff_t const & msg) { list_[cid].emplace_back(std::move(msg)); @@ -50,8 +48,8 @@ struct test_verify { void verify(int /*N*/, int /*Loops*/) { std::cout << "verifying..." << std::endl; - for (int m = 0; m < lcount_; ++m) { - QCOMPARE(datas__, list_[m]); + for (auto& c_dats : list_) { + QCOMPARE(datas__, c_dats); } } }; @@ -101,9 +99,9 @@ struct test_cq { void send(cn_t& cn, const std::array& info) { int n = info[1]; if (n < 0) { - cn.send(ipc::buff_t { '\0' }); + QVERIFY(cn.send(ipc::buff_t { '\0' })); } - else cn.send(datas__[static_cast(n)]); + else QVERIFY(cn.send(datas__[static_cast(n)])); } }; @@ -158,15 +156,15 @@ struct test_cq { void send(cn_t* cn, const std::array& info) { thread_local struct s_dummy { s_dummy(cn_t* cn, int m) { - cn->wait_for_recv(m); + cn->wait_for_recv(static_cast(m)); // std::printf("start to send: %d.\n", m); } } _(cn, m_); int n = info[1]; if (n < 0) { - cn->send(ipc::buff_t { '\0' }); + QVERIFY(cn->send(ipc::buff_t { '\0' })); } - else cn->send(datas__[static_cast(n)]); + else QVERIFY(cn->send(datas__[static_cast(n)])); } };