From 99f281c4c925fbdd1b37d770150314320ed8086f Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sat, 28 Nov 2020 22:26:07 +0800 Subject: [PATCH 01/19] update IPC_CONCEPT_ --- src/libipc/memory/alloc.h | 11 +++--- src/libipc/memory/wrapper.h | 17 +++++---- src/libipc/platform/tls_pointer_win.cpp | 1 - src/libipc/platform/to_tchar.h | 15 +++----- src/libipc/utility/concept.h | 47 ++++++++++++++++--------- src/libipc/utility/pimpl.h | 17 +++++---- 6 files changed, 62 insertions(+), 46 deletions(-) diff --git a/src/libipc/memory/alloc.h b/src/libipc/memory/alloc.h index 16bebc9..820361a 100755 --- a/src/libipc/memory/alloc.h +++ b/src/libipc/memory/alloc.h @@ -44,7 +44,8 @@ constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { return ( (size - 1) & ~(alignment - 1) ) + alignment; } -IPC_CONCEPT_(has_take, take(std::move(std::declval()))); +template +IPC_CONCEPT_(has_take, require([](auto && t)->decltype(t.take(std::move(t))) {})); class scope_alloc_base { protected: @@ -116,13 +117,13 @@ public: } template - auto take(scope_alloc && rhs) -> ipc::require::value> { + auto take(scope_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } template - auto take(scope_alloc && rhs) -> ipc::require::value> { + auto take(scope_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); } @@ -255,7 +256,7 @@ public: } template - auto take(fixed_alloc && rhs) -> ipc::require::value> { + auto take(fixed_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } @@ -394,7 +395,7 @@ public: } template - auto take(variable_alloc && rhs) -> ipc::require::value> { + auto take(variable_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } diff --git a/src/libipc/memory/wrapper.h b/src/libipc/memory/wrapper.h index 5e76f88..7f84838 100755 --- a/src/libipc/memory/wrapper.h +++ b/src/libipc/memory/wrapper.h @@ -27,7 +27,8 @@ namespace mem { namespace detail { -IPC_CONCEPT_(is_comparable, operator<(std::declval())); +template +IPC_CONCEPT_(is_comparable, require([](auto && t)->decltype(t < t) {})); } // namespace detail @@ -71,8 +72,10 @@ public: template class default_recycler : public limited_recycler { - IPC_CONCEPT_(has_remain, remain()); - IPC_CONCEPT_(has_empty , empty()); + template + IPC_CONCEPT_(has_remain, require([](auto && t)->decltype(t.remain()) {})); + template + IPC_CONCEPT_(has_empty, require([](auto && t)->decltype(t.empty()) {})); template void try_fill(A & alc) { @@ -86,28 +89,28 @@ public: template auto try_replenish(alloc_policy & alc, std::size_t size) - -> ipc::require::value && has_remain::value> { + -> std::enable_if_t::value && has_remain::value> { if (alc.remain() >= size) return; this->try_fill(alc); } template auto try_replenish(alloc_policy & alc, std::size_t /*size*/) - -> ipc::require::value && !has_remain::value && has_empty::value> { + -> std::enable_if_t::value && !has_remain::value && has_empty::value> { if (!alc.empty()) return; this->try_fill(alc); } template auto try_replenish(alloc_policy & alc, std::size_t /*size*/) - -> ipc::require::value && has_empty::value> { + -> std::enable_if_t::value && has_empty::value> { if (!alc.empty()) return; this->try_recover(alc); } template IPC_CONSTEXPR_ auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) noexcept - -> ipc::require<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { + -> std::enable_if_t<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { // Do Nothing. } }; diff --git a/src/libipc/platform/tls_pointer_win.cpp b/src/libipc/platform/tls_pointer_win.cpp index cdbe208..3d56909 100755 --- a/src/libipc/platform/tls_pointer_win.cpp +++ b/src/libipc/platform/tls_pointer_win.cpp @@ -7,7 +7,6 @@ * @remarks * Windows doesn't support a per-thread destructor with its TLS primitives. * So, here will build it manually by inserting a function to be called on each thread's exit. - * * @see * - https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way * - https://src.chromium.org/viewvc/chrome/trunk/src/base/threading/thread_local_storage_win.cc diff --git a/src/libipc/platform/to_tchar.h b/src/libipc/platform/to_tchar.h index df67b2d..2598b83 100755 --- a/src/libipc/platform/to_tchar.h +++ b/src/libipc/platform/to_tchar.h @@ -15,19 +15,14 @@ namespace ipc { namespace detail { -struct has_value_type_ { - template static std::true_type check(typename T::value_type *); - template static std::false_type check(...); -}; - -template (nullptr))> -struct is_same_char : std::is_same {}; - template -struct is_same_char : std::is_same {}; +IPC_CONCEPT_(has_same_char, + require([]()->std::enable_if_t::value> {}) || + require([]()->std::enable_if_t::value> {}) +); template -using IsSameChar = ipc::require::value, R>; +using IsSameChar = std::enable_if_t::value, R>; //////////////////////////////////////////////////////////////// /// to_tchar implementation diff --git a/src/libipc/utility/concept.h b/src/libipc/utility/concept.h index 9325ddc..5192c07 100755 --- a/src/libipc/utility/concept.h +++ b/src/libipc/utility/concept.h @@ -1,29 +1,42 @@ #pragma once -#include // std::enable_if +#include // std::declval namespace ipc { -// concept helpers +/** + * @remarks + * <> Concepts TS Improve on C++17 + * @see + * - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0726r0.html + * - https://www.youtube.com/watch?v=TorW5ekkL_w +*/ +namespace detail { -template -using require = typename std::enable_if::type; +template ()(std::declval()...))> +constexpr bool require_impl(int) { return true; } + +template +constexpr bool require_impl(...) { return false; } + +} // namespace detail + +template +constexpr bool require(F&&) { + return detail::require_impl(int{}); +} + +} // namespace ipc + +/// concept helpers #ifdef IPC_CONCEPT_ # error "IPC_CONCEPT_ has been defined." #endif -#define IPC_CONCEPT_(NAME, WHAT) \ -template \ -class NAME { \ -private: \ - template \ - static std::true_type check(decltype(std::declval().WHAT)*); \ - template \ - static std::false_type check(...); \ -public: \ - using type = decltype(check(nullptr)); \ - constexpr static auto value = type::value; \ +#define IPC_CONCEPT_($$name, $$what) \ +class $$name { \ +public: \ + constexpr static bool value = $$what; \ } - -} // namespace ipc diff --git a/src/libipc/utility/pimpl.h b/src/libipc/utility/pimpl.h index 25074e7..fbb5ea2 100755 --- a/src/libipc/utility/pimpl.h +++ b/src/libipc/utility/pimpl.h @@ -8,14 +8,19 @@ namespace ipc { +template +IPC_CONCEPT_(is_impl_comfortable, + require([](auto && t)->std::enable_if_t<(sizeof(t) <= sizeof(T*))> {}) +); + +template +using IsImplComfortable = std::enable_if_t::value, R>; + +template +using IsImplUncomfortable = std::enable_if_t::value, R>; + // pimpl small object optimization helpers -template -using IsImplComfortable = ipc::require<(sizeof(T) <= sizeof(T*)), R>; - -template -using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>; - template constexpr auto make_impl(P&&... params) -> IsImplComfortable { T* buf {}; From 1e5547e6dfd0605fa62be67899c6c893aa61f9fc Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sat, 28 Nov 2020 22:26:07 +0800 Subject: [PATCH 02/19] update IPC_CONCEPT_ --- src/libipc/memory/alloc.h | 11 +++--- src/libipc/memory/wrapper.h | 17 +++++---- src/libipc/platform/tls_pointer_win.cpp | 1 - src/libipc/platform/to_tchar.h | 15 +++----- src/libipc/utility/concept.h | 47 ++++++++++++++++--------- src/libipc/utility/pimpl.h | 17 +++++---- 6 files changed, 62 insertions(+), 46 deletions(-) diff --git a/src/libipc/memory/alloc.h b/src/libipc/memory/alloc.h index 16bebc9..820361a 100755 --- a/src/libipc/memory/alloc.h +++ b/src/libipc/memory/alloc.h @@ -44,7 +44,8 @@ constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { return ( (size - 1) & ~(alignment - 1) ) + alignment; } -IPC_CONCEPT_(has_take, take(std::move(std::declval()))); +template +IPC_CONCEPT_(has_take, require([](auto && t)->decltype(t.take(std::move(t))) {})); class scope_alloc_base { protected: @@ -116,13 +117,13 @@ public: } template - auto take(scope_alloc && rhs) -> ipc::require::value> { + auto take(scope_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } template - auto take(scope_alloc && rhs) -> ipc::require::value> { + auto take(scope_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); } @@ -255,7 +256,7 @@ public: } template - auto take(fixed_alloc && rhs) -> ipc::require::value> { + auto take(fixed_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } @@ -394,7 +395,7 @@ public: } template - auto take(variable_alloc && rhs) -> ipc::require::value> { + auto take(variable_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } diff --git a/src/libipc/memory/wrapper.h b/src/libipc/memory/wrapper.h index 5e76f88..7f84838 100755 --- a/src/libipc/memory/wrapper.h +++ b/src/libipc/memory/wrapper.h @@ -27,7 +27,8 @@ namespace mem { namespace detail { -IPC_CONCEPT_(is_comparable, operator<(std::declval())); +template +IPC_CONCEPT_(is_comparable, require([](auto && t)->decltype(t < t) {})); } // namespace detail @@ -71,8 +72,10 @@ public: template class default_recycler : public limited_recycler { - IPC_CONCEPT_(has_remain, remain()); - IPC_CONCEPT_(has_empty , empty()); + template + IPC_CONCEPT_(has_remain, require([](auto && t)->decltype(t.remain()) {})); + template + IPC_CONCEPT_(has_empty, require([](auto && t)->decltype(t.empty()) {})); template void try_fill(A & alc) { @@ -86,28 +89,28 @@ public: template auto try_replenish(alloc_policy & alc, std::size_t size) - -> ipc::require::value && has_remain::value> { + -> std::enable_if_t::value && has_remain::value> { if (alc.remain() >= size) return; this->try_fill(alc); } template auto try_replenish(alloc_policy & alc, std::size_t /*size*/) - -> ipc::require::value && !has_remain::value && has_empty::value> { + -> std::enable_if_t::value && !has_remain::value && has_empty::value> { if (!alc.empty()) return; this->try_fill(alc); } template auto try_replenish(alloc_policy & alc, std::size_t /*size*/) - -> ipc::require::value && has_empty::value> { + -> std::enable_if_t::value && has_empty::value> { if (!alc.empty()) return; this->try_recover(alc); } template IPC_CONSTEXPR_ auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) noexcept - -> ipc::require<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { + -> std::enable_if_t<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { // Do Nothing. } }; diff --git a/src/libipc/platform/tls_pointer_win.cpp b/src/libipc/platform/tls_pointer_win.cpp index cdbe208..3d56909 100755 --- a/src/libipc/platform/tls_pointer_win.cpp +++ b/src/libipc/platform/tls_pointer_win.cpp @@ -7,7 +7,6 @@ * @remarks * Windows doesn't support a per-thread destructor with its TLS primitives. * So, here will build it manually by inserting a function to be called on each thread's exit. - * * @see * - https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way * - https://src.chromium.org/viewvc/chrome/trunk/src/base/threading/thread_local_storage_win.cc diff --git a/src/libipc/platform/to_tchar.h b/src/libipc/platform/to_tchar.h index df67b2d..2598b83 100755 --- a/src/libipc/platform/to_tchar.h +++ b/src/libipc/platform/to_tchar.h @@ -15,19 +15,14 @@ namespace ipc { namespace detail { -struct has_value_type_ { - template static std::true_type check(typename T::value_type *); - template static std::false_type check(...); -}; - -template (nullptr))> -struct is_same_char : std::is_same {}; - template -struct is_same_char : std::is_same {}; +IPC_CONCEPT_(has_same_char, + require([]()->std::enable_if_t::value> {}) || + require([]()->std::enable_if_t::value> {}) +); template -using IsSameChar = ipc::require::value, R>; +using IsSameChar = std::enable_if_t::value, R>; //////////////////////////////////////////////////////////////// /// to_tchar implementation diff --git a/src/libipc/utility/concept.h b/src/libipc/utility/concept.h index 9325ddc..5192c07 100755 --- a/src/libipc/utility/concept.h +++ b/src/libipc/utility/concept.h @@ -1,29 +1,42 @@ #pragma once -#include // std::enable_if +#include // std::declval namespace ipc { -// concept helpers +/** + * @remarks + * <> Concepts TS Improve on C++17 + * @see + * - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0726r0.html + * - https://www.youtube.com/watch?v=TorW5ekkL_w +*/ +namespace detail { -template -using require = typename std::enable_if::type; +template ()(std::declval()...))> +constexpr bool require_impl(int) { return true; } + +template +constexpr bool require_impl(...) { return false; } + +} // namespace detail + +template +constexpr bool require(F&&) { + return detail::require_impl(int{}); +} + +} // namespace ipc + +/// concept helpers #ifdef IPC_CONCEPT_ # error "IPC_CONCEPT_ has been defined." #endif -#define IPC_CONCEPT_(NAME, WHAT) \ -template \ -class NAME { \ -private: \ - template \ - static std::true_type check(decltype(std::declval().WHAT)*); \ - template \ - static std::false_type check(...); \ -public: \ - using type = decltype(check(nullptr)); \ - constexpr static auto value = type::value; \ +#define IPC_CONCEPT_($$name, $$what) \ +class $$name { \ +public: \ + constexpr static bool value = $$what; \ } - -} // namespace ipc diff --git a/src/libipc/utility/pimpl.h b/src/libipc/utility/pimpl.h index 25074e7..fbb5ea2 100755 --- a/src/libipc/utility/pimpl.h +++ b/src/libipc/utility/pimpl.h @@ -8,14 +8,19 @@ namespace ipc { +template +IPC_CONCEPT_(is_impl_comfortable, + require([](auto && t)->std::enable_if_t<(sizeof(t) <= sizeof(T*))> {}) +); + +template +using IsImplComfortable = std::enable_if_t::value, R>; + +template +using IsImplUncomfortable = std::enable_if_t::value, R>; + // pimpl small object optimization helpers -template -using IsImplComfortable = ipc::require<(sizeof(T) <= sizeof(T*)), R>; - -template -using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>; - template constexpr auto make_impl(P&&... params) -> IsImplComfortable { T* buf {}; From 55e75d4ed6da31b5c34fb03b4dcbf6e5f85e61a3 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Fri, 1 Jan 2021 11:13:30 +0800 Subject: [PATCH 03/19] add profiler from adah1972 --- test/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4b695eb..07b0a34 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,7 +15,9 @@ include_directories( ${LIBIPC_PROJECT_DIR}/3rdparty ${LIBIPC_PROJECT_DIR}/3rdparty/gtest/include) -file(GLOB SRC_FILES ${LIBIPC_PROJECT_DIR}/test/*.cpp) +file(GLOB SRC_FILES + ${LIBIPC_PROJECT_DIR}/test/*.cpp + ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp) file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/*.h) add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEAD_FILES}) From 54bc3386dd434c9c68e911b0bd039dc5c39c9e4a Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 6 Jun 2021 19:35:39 +0800 Subject: [PATCH 04/19] test/profiler --- test/profiler/README.md | 11 ++++++ test/profiler/profiler.cpp | 77 ++++++++++++++++++++++++++++++++++++++ test/profiler/profiler.h | 35 +++++++++++++++++ test/profiler/rdtsc.h | 52 +++++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 test/profiler/README.md create mode 100644 test/profiler/profiler.cpp create mode 100644 test/profiler/profiler.h create mode 100644 test/profiler/rdtsc.h diff --git a/test/profiler/README.md b/test/profiler/README.md new file mode 100644 index 0000000..d4a6bea --- /dev/null +++ b/test/profiler/README.md @@ -0,0 +1,11 @@ +# A Quick Introduction to C++ Performance Tuning +(From: https://github.com/adah1972/cpp_summit_2020.git) + +This repository contains the presentation file and example code for my +presentation at the C++ Summit 2020 held in Shenzhen, China on 4–5 December +2020. + +The presentation content is shared under a [Creative Commons Attribution-Share +Alike 2.5 Licence](http://creativecommons.org/licenses/by-sa/2.5/). The code +is put in the public domain (i.e. do whatever you like with it), though an +acknowledgement will be appreciated (but not required). diff --git a/test/profiler/profiler.cpp b/test/profiler/profiler.cpp new file mode 100644 index 0000000..d8fd7bc --- /dev/null +++ b/test/profiler/profiler.cpp @@ -0,0 +1,77 @@ +#include "profiler.h" +#include +#include +#include + +namespace { + +struct profiling_data { + int number; + int call_count{}; + uint64_t call_duration{}; +}; + +class profiler { +public: + profiler(); + ~profiler(); + + void add_data(int number, uint64_t duration); + +private: + std::vector data_; +}; + +profiler::profiler() +{ + size_t len = 0; + for (;;) { + if (name_map[len].name == NULL) { + break; + } + ++len; + } + data_.resize(len); + int i = 0; + for (auto& item : data_) { + assert(i == name_map[i].number); + item.number = i; + ++i; + } +} + +profiler::~profiler() +{ +#ifndef NDEBUG + for (auto& item : data_) { + if (item.call_count == 0) { + continue; + } + std::cout << item.number << " " << name_map[item.number].name + << ":\n"; + std::cout << " Call count: " << item.call_count << '\n'; + std::cout << " Call duration: " << item.call_duration << '\n'; + std::cout << " Average duration: " + << item.call_duration * 1.0 / + (item.call_count != 0 ? item.call_count : 1) + << '\n'; + } +#endif +} + +void profiler::add_data(int number, uint64_t duration) +{ + assert(number >= 0 && number < static_cast(data_.size())); + data_[number].call_count++; + data_[number].call_duration += duration; +} + +profiler profiler_instance; + +} // unnamed namespace + +profiling_checker::~profiling_checker() +{ + auto end_time = rdtsc(); + profiler_instance.add_data(number_, end_time - start_time_); +} diff --git a/test/profiler/profiler.h b/test/profiler/profiler.h new file mode 100644 index 0000000..d04264a --- /dev/null +++ b/test/profiler/profiler.h @@ -0,0 +1,35 @@ +#ifndef PROFILER_H +#define PROFILER_H + +#include "rdtsc.h" + +struct name_mapper { + int number; + const char* name; +}; + +extern name_mapper name_map[]; + +class profiling_checker { +public: + profiling_checker(int number); + ~profiling_checker(); + +private: + int number_; + uint64_t start_time_; +}; + +inline profiling_checker::profiling_checker(int number) + : number_(number) +{ + start_time_ = rdtsc(); +} + +#ifdef NDEBUG +#define PROFILE_CHECK(func_number) (void)0 +#else +#define PROFILE_CHECK(func_number) profiling_checker _checker(func_number) +#endif + +#endif // PROFILER_H diff --git a/test/profiler/rdtsc.h b/test/profiler/rdtsc.h new file mode 100644 index 0000000..80e35c7 --- /dev/null +++ b/test/profiler/rdtsc.h @@ -0,0 +1,52 @@ +#ifndef RDTSC_H +#define RDTSC_H + +#include // uint64_t + +#if defined(_M_X64) || defined(_M_IX86) || defined(__x86_64) || defined(__i386) +# ifdef _WIN32 +# include // __rdtsc +# else +# include // __rdtsc +# endif +# define HAS_HW_RDTSC 1 +#else +# include // std::chrono::high_resolution_clock +# define HAS_HW_RDTSC 0 +#endif + +inline uint64_t rdtsc() +{ +#if HAS_HW_RDTSC + // _mm_lfence() might be used to serialize the instruction stream, + // and it would guarantee that RDTSC will not be reordered with + // other instructions. However, measurements show that the overhead + // may be too big (easily 15 to 30 CPU cycles) for profiling + // purposes: if reordering matters, the overhead matters too! + + // Forbid the compiler from reordering instructions +# ifdef _MSC_VER + _ReadWriteBarrier(); +# else + __asm__ __volatile__("" : : : "memory"); +# endif + + uint64_t result = __rdtsc(); + + // Forbid the compiler from reordering instructions +# ifdef _MSC_VER + _ReadWriteBarrier(); +# else + __asm__ __volatile__("" : : : "memory"); +# endif + + return result; +#else + auto now = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast( + now.time_since_epoch()) + .count(); +#endif +} + +#endif // RDTSC_H From ff488e002f75573fdc66cfca50859105a2efb5cd Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 6 Jun 2021 19:40:54 +0800 Subject: [PATCH 05/19] Revert "update IPC_CONCEPT_" This reverts commit 1e5547e6dfd0605fa62be67899c6c893aa61f9fc. --- src/libipc/memory/alloc.h | 11 +++--- src/libipc/memory/wrapper.h | 17 ++++----- src/libipc/platform/tls_pointer_win.cpp | 1 + src/libipc/platform/to_tchar.h | 15 +++++--- src/libipc/utility/concept.h | 47 +++++++++---------------- src/libipc/utility/pimpl.h | 17 ++++----- 6 files changed, 46 insertions(+), 62 deletions(-) diff --git a/src/libipc/memory/alloc.h b/src/libipc/memory/alloc.h index 820361a..16bebc9 100755 --- a/src/libipc/memory/alloc.h +++ b/src/libipc/memory/alloc.h @@ -44,8 +44,7 @@ constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { return ( (size - 1) & ~(alignment - 1) ) + alignment; } -template -IPC_CONCEPT_(has_take, require([](auto && t)->decltype(t.take(std::move(t))) {})); +IPC_CONCEPT_(has_take, take(std::move(std::declval()))); class scope_alloc_base { protected: @@ -117,13 +116,13 @@ public: } template - auto take(scope_alloc && rhs) -> std::enable_if_t::value> { + auto take(scope_alloc && rhs) -> ipc::require::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } template - auto take(scope_alloc && rhs) -> std::enable_if_t::value> { + auto take(scope_alloc && rhs) -> ipc::require::value> { base_t::take(std::move(rhs)); } @@ -256,7 +255,7 @@ public: } template - auto take(fixed_alloc && rhs) -> std::enable_if_t::value> { + auto take(fixed_alloc && rhs) -> ipc::require::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } @@ -395,7 +394,7 @@ public: } template - auto take(variable_alloc && rhs) -> std::enable_if_t::value> { + auto take(variable_alloc && rhs) -> ipc::require::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } diff --git a/src/libipc/memory/wrapper.h b/src/libipc/memory/wrapper.h index 7f84838..5e76f88 100755 --- a/src/libipc/memory/wrapper.h +++ b/src/libipc/memory/wrapper.h @@ -27,8 +27,7 @@ namespace mem { namespace detail { -template -IPC_CONCEPT_(is_comparable, require([](auto && t)->decltype(t < t) {})); +IPC_CONCEPT_(is_comparable, operator<(std::declval())); } // namespace detail @@ -72,10 +71,8 @@ public: template class default_recycler : public limited_recycler { - template - IPC_CONCEPT_(has_remain, require([](auto && t)->decltype(t.remain()) {})); - template - IPC_CONCEPT_(has_empty, require([](auto && t)->decltype(t.empty()) {})); + IPC_CONCEPT_(has_remain, remain()); + IPC_CONCEPT_(has_empty , empty()); template void try_fill(A & alc) { @@ -89,28 +86,28 @@ public: template auto try_replenish(alloc_policy & alc, std::size_t size) - -> std::enable_if_t::value && has_remain::value> { + -> ipc::require::value && has_remain::value> { if (alc.remain() >= size) return; this->try_fill(alc); } template auto try_replenish(alloc_policy & alc, std::size_t /*size*/) - -> std::enable_if_t::value && !has_remain::value && has_empty::value> { + -> ipc::require::value && !has_remain::value && has_empty::value> { if (!alc.empty()) return; this->try_fill(alc); } template auto try_replenish(alloc_policy & alc, std::size_t /*size*/) - -> std::enable_if_t::value && has_empty::value> { + -> ipc::require::value && has_empty::value> { if (!alc.empty()) return; this->try_recover(alc); } template IPC_CONSTEXPR_ auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) noexcept - -> std::enable_if_t<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { + -> ipc::require<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { // Do Nothing. } }; diff --git a/src/libipc/platform/tls_pointer_win.cpp b/src/libipc/platform/tls_pointer_win.cpp index 3d56909..cdbe208 100755 --- a/src/libipc/platform/tls_pointer_win.cpp +++ b/src/libipc/platform/tls_pointer_win.cpp @@ -7,6 +7,7 @@ * @remarks * Windows doesn't support a per-thread destructor with its TLS primitives. * So, here will build it manually by inserting a function to be called on each thread's exit. + * * @see * - https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way * - https://src.chromium.org/viewvc/chrome/trunk/src/base/threading/thread_local_storage_win.cc diff --git a/src/libipc/platform/to_tchar.h b/src/libipc/platform/to_tchar.h index 2598b83..df67b2d 100755 --- a/src/libipc/platform/to_tchar.h +++ b/src/libipc/platform/to_tchar.h @@ -15,14 +15,19 @@ namespace ipc { namespace detail { +struct has_value_type_ { + template static std::true_type check(typename T::value_type *); + template static std::false_type check(...); +}; + +template (nullptr))> +struct is_same_char : std::is_same {}; + template -IPC_CONCEPT_(has_same_char, - require([]()->std::enable_if_t::value> {}) || - require([]()->std::enable_if_t::value> {}) -); +struct is_same_char : std::is_same {}; template -using IsSameChar = std::enable_if_t::value, R>; +using IsSameChar = ipc::require::value, R>; //////////////////////////////////////////////////////////////// /// to_tchar implementation diff --git a/src/libipc/utility/concept.h b/src/libipc/utility/concept.h index 5192c07..9325ddc 100755 --- a/src/libipc/utility/concept.h +++ b/src/libipc/utility/concept.h @@ -1,42 +1,29 @@ #pragma once -#include // std::declval +#include // std::enable_if namespace ipc { -/** - * @remarks - * <> Concepts TS Improve on C++17 - * @see - * - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0726r0.html - * - https://www.youtube.com/watch?v=TorW5ekkL_w -*/ -namespace detail { +// concept helpers -template ()(std::declval()...))> -constexpr bool require_impl(int) { return true; } - -template -constexpr bool require_impl(...) { return false; } - -} // namespace detail - -template -constexpr bool require(F&&) { - return detail::require_impl(int{}); -} - -} // namespace ipc - -/// concept helpers +template +using require = typename std::enable_if::type; #ifdef IPC_CONCEPT_ # error "IPC_CONCEPT_ has been defined." #endif -#define IPC_CONCEPT_($$name, $$what) \ -class $$name { \ -public: \ - constexpr static bool value = $$what; \ +#define IPC_CONCEPT_(NAME, WHAT) \ +template \ +class NAME { \ +private: \ + template \ + static std::true_type check(decltype(std::declval().WHAT)*); \ + template \ + static std::false_type check(...); \ +public: \ + using type = decltype(check(nullptr)); \ + constexpr static auto value = type::value; \ } + +} // namespace ipc diff --git a/src/libipc/utility/pimpl.h b/src/libipc/utility/pimpl.h index fbb5ea2..25074e7 100755 --- a/src/libipc/utility/pimpl.h +++ b/src/libipc/utility/pimpl.h @@ -8,19 +8,14 @@ namespace ipc { -template -IPC_CONCEPT_(is_impl_comfortable, - require([](auto && t)->std::enable_if_t<(sizeof(t) <= sizeof(T*))> {}) -); - -template -using IsImplComfortable = std::enable_if_t::value, R>; - -template -using IsImplUncomfortable = std::enable_if_t::value, R>; - // pimpl small object optimization helpers +template +using IsImplComfortable = ipc::require<(sizeof(T) <= sizeof(T*)), R>; + +template +using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>; + template constexpr auto make_impl(P&&... params) -> IsImplComfortable { T* buf {}; From e20dd7d5e3553ffafa1ad6a2fb39bf9c9aaf9385 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 20 Jun 2021 15:23:44 +0800 Subject: [PATCH 06/19] add sync::mutex for windows/linux --- .gitignore | 1 + include/libipc/def.h | 7 +- include/libipc/ipc.h | 26 ++-- include/libipc/mutex.h | 38 ++++++ include/libipc/shm.h | 13 +- include/libipc/waiter.h | 4 +- src/ipc.cpp | 18 +-- src/libipc/platform/detail.h | 12 ++ src/libipc/platform/get_wait_time.h | 39 ++++++ src/libipc/platform/mutex_linux.h | 167 +++++++++++++++++++++++ src/libipc/platform/mutex_win.h | 100 ++++++++++++++ src/libipc/platform/shm_linux.cpp | 29 +++- src/libipc/platform/shm_win.cpp | 8 ++ src/libipc/platform/waiter_linux.h | 17 ++- src/libipc/platform/waiter_win.h | 10 +- src/libipc/platform/waiter_wrapper.h | 19 ++- src/libipc/waiter_helper.h | 2 +- src/mutex.cpp | 70 ++++++++++ src/shm.cpp | 14 +- src/waiter.cpp | 4 +- test/CMakeLists.txt | 3 +- test/{test_pthread.cpp => test_sync.cpp} | 132 +++++++++++------- 22 files changed, 622 insertions(+), 111 deletions(-) create mode 100644 include/libipc/mutex.h create mode 100644 src/libipc/platform/get_wait_time.h create mode 100644 src/libipc/platform/mutex_linux.h create mode 100644 src/libipc/platform/mutex_win.h create mode 100644 src/mutex.cpp rename test/{test_pthread.cpp => test_sync.cpp} (59%) mode change 100755 => 100644 diff --git a/.gitignore b/.gitignore index 5ea6006..c83c97d 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ CMakeLists.txt.user* # My output files build +.vscode \ No newline at end of file diff --git a/include/libipc/def.h b/include/libipc/def.h index 9be5206..d02ff43 100755 --- a/include/libipc/def.h +++ b/include/libipc/def.h @@ -25,13 +25,16 @@ using uint_t = typename uint::type; // constants +enum : std::uint32_t { + invalid_value = (std::numeric_limits::max)(), + default_timeout = 100, // ms +}; + enum : std::size_t { - invalid_value = (std::numeric_limits::max)(), data_length = 64, large_msg_limit = data_length, large_msg_align = 512, large_msg_cache = 32, - default_timeout = 100 // ms }; enum class relat { // multiplicity of the relationship diff --git a/include/libipc/ipc.h b/include/libipc/ipc.h index f6380ae..64b262c 100755 --- a/include/libipc/ipc.h +++ b/include/libipc/ipc.h @@ -27,12 +27,12 @@ struct IPC_EXPORT chan_impl { static char const * name(ipc::handle_t h); static std::size_t recv_count(ipc::handle_t h); - static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm); + static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm); - static bool send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm); - static buff_t recv(ipc::handle_t h, std::size_t tm); + static bool send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm); + static buff_t recv(ipc::handle_t h, std::uint64_t tm); - static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm); + static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm); static buff_t try_recv(ipc::handle_t h); }; @@ -120,41 +120,41 @@ public: return detail_t::recv_count(h_); } - bool wait_for_recv(std::size_t r_count, std::size_t tm = invalid_value) const { + bool wait_for_recv(std::size_t r_count, std::uint64_t tm = invalid_value) const { return detail_t::wait_for_recv(h_, r_count, tm); } - static bool wait_for_recv(char const * name, std::size_t r_count, std::size_t tm = invalid_value) { + static bool wait_for_recv(char const * name, std::size_t r_count, std::uint64_t tm = invalid_value) { return chan_wrapper(name).wait_for_recv(r_count, tm); } /** * If timeout, this function would call 'force_push' to send the data forcibly. */ - bool send(void const * data, std::size_t size, std::size_t tm = default_timeout) { + bool send(void const * data, std::size_t size, std::uint64_t tm = default_timeout) { return detail_t::send(h_, data, size, tm); } - bool send(buff_t const & buff, std::size_t tm = default_timeout) { + bool send(buff_t const & buff, std::uint64_t tm = default_timeout) { return this->send(buff.data(), buff.size(), tm); } - bool send(std::string const & str, std::size_t tm = default_timeout) { + bool send(std::string const & str, std::uint64_t tm = default_timeout) { return this->send(str.c_str(), str.size() + 1, tm); } /** * If timeout, this function would just return false. */ - bool try_send(void const * data, std::size_t size, std::size_t tm = default_timeout) { + bool try_send(void const * data, std::size_t size, std::uint64_t tm = default_timeout) { return detail_t::try_send(h_, data, size, tm); } - bool try_send(buff_t const & buff, std::size_t tm = default_timeout) { + bool try_send(buff_t const & buff, std::uint64_t tm = default_timeout) { return this->try_send(buff.data(), buff.size(), tm); } - bool try_send(std::string const & str, std::size_t tm = default_timeout) { + bool try_send(std::string const & str, std::uint64_t tm = default_timeout) { return this->try_send(str.c_str(), str.size() + 1, tm); } - buff_t recv(std::size_t tm = invalid_value) { + buff_t recv(std::uint64_t tm = invalid_value) { return detail_t::recv(h_, tm); } diff --git a/include/libipc/mutex.h b/include/libipc/mutex.h new file mode 100644 index 0000000..23f7b53 --- /dev/null +++ b/include/libipc/mutex.h @@ -0,0 +1,38 @@ +#pragma once + +#include // std::uint64_t +#include + +#include "libipc/export.h" +#include "libipc/def.h" + +namespace ipc { +namespace sync { + +class IPC_EXPORT mutex { + mutex(mutex const &) = delete; + mutex &operator=(mutex const &) = delete; + +public: + mutex(); + explicit mutex(char const *name); + ~mutex(); + + void const *native() const noexcept; + void *native() noexcept; + + bool valid() const noexcept; + + bool open(char const *name) noexcept; + void close() noexcept; + bool lock(std::uint64_t tm = ipc::invalid_value) noexcept; + bool try_lock() noexcept(false); // std::system_error + bool unlock() noexcept; + +private: + class mutex_; + mutex_* p_; +}; + +} // namespace sync +} // namespace ipc diff --git a/include/libipc/shm.h b/include/libipc/shm.h index 91a68e4..395b6c8 100755 --- a/include/libipc/shm.h +++ b/include/libipc/shm.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "libipc/export.h" @@ -20,6 +21,9 @@ IPC_EXPORT void release(id_t id); IPC_EXPORT void remove (id_t id); IPC_EXPORT void remove (char const * name); +IPC_EXPORT std::uint32_t get_ref(id_t id); +IPC_EXPORT void sub_ref(id_t id); + class IPC_EXPORT handle { public: handle(); @@ -31,9 +35,12 @@ public: void swap(handle& rhs); handle& operator=(handle rhs); - bool valid() const; - std::size_t size () const; - char const * name () const; + bool valid() const noexcept; + std::size_t size () const noexcept; + char const * name () const noexcept; + + std::uint32_t ref() const noexcept; + void sub_ref() noexcept; bool acquire(char const * name, std::size_t size, unsigned mode = create | open); void release(); diff --git a/include/libipc/waiter.h b/include/libipc/waiter.h index a4c3c09..37197e0 100755 --- a/include/libipc/waiter.h +++ b/include/libipc/waiter.h @@ -54,7 +54,7 @@ public: bool open (char const * name, long count = 0); void close(); - bool wait(std::size_t tm = invalid_value); + bool wait(std::uint64_t tm = invalid_value); bool post(long count = 1); private: @@ -81,7 +81,7 @@ public: bool open (char const * name); void close(); - bool wait(mutex&, std::size_t tm = invalid_value); + bool wait(mutex&, std::uint64_t tm = invalid_value); bool notify(); bool broadcast(); diff --git a/src/ipc.cpp b/src/ipc.cpp index 12a629b..bfa1376 100755 --- a/src/ipc.cpp +++ b/src/ipc.cpp @@ -291,7 +291,7 @@ struct conn_info_head { }; template -bool wait_for(W& waiter, F&& pred, std::size_t tm) { +bool wait_for(W& waiter, F&& pred, std::uint64_t tm) { if (tm == 0) return !pred(); for (unsigned k = 0; pred();) { bool loop = true, ret = true; @@ -403,7 +403,7 @@ static std::size_t recv_count(ipc::handle_t h) noexcept { return que->conn_count(); } -static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) { +static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm) { auto que = queue_of(h); if (que == nullptr) { return false; @@ -475,7 +475,7 @@ static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t s return true; } -static bool send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm) { +static bool send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) { return send([tm](auto info, auto que, auto msg_id) { return [tm, info, que, msg_id](std::int32_t remain, void const * data, std::size_t size) { if (!wait_for(info->wt_waiter_, [&] { @@ -500,7 +500,7 @@ static bool send(ipc::handle_t h, void const * data, std::size_t size, std::size }, h, data, size); } -static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm) { +static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) { return send([tm](auto info, auto que, auto msg_id) { return [tm, info, que, msg_id](std::int32_t remain, void const * data, std::size_t size) { if (!wait_for(info->wt_waiter_, [&] { @@ -514,7 +514,7 @@ static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std:: }, h, data, size); } -static ipc::buff_t recv(ipc::handle_t h, std::size_t tm) { +static ipc::buff_t recv(ipc::handle_t h, std::uint64_t tm) { auto que = queue_of(h); if (que == nullptr) { ipc::error("fail: recv, queue_of(h) == nullptr\n"); @@ -630,22 +630,22 @@ std::size_t chan_impl::recv_count(ipc::handle_t h) { } template -bool chan_impl::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) { +bool chan_impl::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm) { return detail_impl>::wait_for_recv(h, r_count, tm); } template -bool chan_impl::send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm) { +bool chan_impl::send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) { return detail_impl>::send(h, data, size, tm); } template -buff_t chan_impl::recv(ipc::handle_t h, std::size_t tm) { +buff_t chan_impl::recv(ipc::handle_t h, std::uint64_t tm) { return detail_impl>::recv(h, tm); } template -bool chan_impl::try_send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm) { +bool chan_impl::try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) { return detail_impl>::try_send(h, data, size, tm); } diff --git a/src/libipc/platform/detail.h b/src/libipc/platform/detail.h index fbf539a..97bbd12 100755 --- a/src/libipc/platform/detail.h +++ b/src/libipc/platform/detail.h @@ -22,6 +22,18 @@ # error "IPC_CONSTEXPR_ has been defined." #endif +// detect platform + +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ + defined(WINCE) || defined(_WIN32_WCE) +# define IPC_OS_WINDOWS_ +#endif/*WIN*/ + +#if defined(__linux__) || defined(__linux) +# define IPC_OS_LINUX_ +#endif/*linux*/ + #if __cplusplus >= 201703L #define IPC_UNUSED_ [[maybe_unused]] diff --git a/src/libipc/platform/get_wait_time.h b/src/libipc/platform/get_wait_time.h new file mode 100644 index 0000000..785cd75 --- /dev/null +++ b/src/libipc/platform/get_wait_time.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include "libipc/utility/log.h" + +namespace ipc { +namespace detail { + +inline bool calc_wait_time(timespec &ts, std::uint64_t tm /*ms*/) noexcept { + timeval now; + int eno = ::gettimeofday(&now, NULL); + if (eno != 0) { + ipc::error("fail gettimeofday [%d]\n", eno); + return false; + } + ts.tv_nsec = (now.tv_usec + (tm % 1000) * 1000) * 1000; + ts.tv_sec = now.tv_sec + (tm / 1000) + (ts.tv_nsec / 1000000000l); + ts.tv_nsec %= 1000000000l; + return true; +} + +inline timespec make_timespec(std::uint64_t tm /*ms*/) noexcept(false) { + timespec ts {}; + if (!calc_wait_time(ts, tm)) { + ipc::error("fail calc_wait_time: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n", + tm, ts.tv_sec, ts.tv_nsec); + throw std::system_error{static_cast(errno), std::system_category()}; + } + return ts; +} + +} // namespace detail +} // namespace ipc diff --git a/src/libipc/platform/mutex_linux.h b/src/libipc/platform/mutex_linux.h new file mode 100644 index 0000000..370a297 --- /dev/null +++ b/src/libipc/platform/mutex_linux.h @@ -0,0 +1,167 @@ +#pragma once + +#include +#include +#include + +#include + +#include "libipc/platform/get_wait_time.h" +#include "libipc/platform/detail.h" +#include "libipc/utility/log.h" +#include "libipc/utility/scope_guard.h" +#include "libipc/shm.h" + +namespace ipc { +namespace detail { +namespace sync { + +class mutex { + ipc::shm::handle shm_; + pthread_mutex_t *mutex_ = nullptr; + +public: + mutex() noexcept = default; + explicit mutex(char const *name) noexcept { + open(name); + } + + ~mutex() noexcept = default; + + pthread_mutex_t const *native() const noexcept { + return mutex_; + } + + pthread_mutex_t *native() noexcept { + return mutex_; + } + + bool valid() const noexcept { + static const tmp[sizeof pthread_mutex_t] {}; + return shm_.valid() + && (mutex_ != nullptr) + && (std::memcmp(tmp, mutex_, sizeof pthread_mutex_t) != 0); + } + + bool open(char const *name) noexcept { + close(); + if (!shm_.acquire(name, sizeof pthread_mutex_t)) { + ipc::error("fail shm.acquire: %s\n", name); + return false; + } + mutex_ = static_cast(shm_.get()); + assert(mutex_ != nullptr); + if ((shm_.ref() == 1) && valid()/*it means mutex has been inited*/) { + ::pthread_mutex_destroy(mutex_); + } + auto finally = ipc::guard([this] { close(); }); // close when failed + // init mutex + int eno; + pthread_mutexattr_t mutex_attr; + if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) { + ipc::error("fail pthread_mutexattr_init[%d]\n", eno); + return false; + } + IPC_UNUSED_ auto guard_mutex_attr = unique_ptr(&mutex_attr, ::pthread_mutexattr_destroy); + if ((eno = ::pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0) { + ipc::error("fail pthread_mutexattr_setpshared[%d]\n", eno); + return false; + } + if ((eno = ::pthread_mutexattr_setrobust(&mutex_attr, PTHREAD_MUTEX_ROBUST)) != 0) { + ipc::error("fail pthread_mutexattr_setrobust[%d]\n", eno); + return false; + } + *mutex_ = PTHREAD_MUTEX_INITIALIZER; + if ((eno = ::pthread_mutex_init(mutex_, &mutex_attr)) != 0) { + ipc::error("fail pthread_mutex_init[%d]\n", eno); + return false; + } + finally.dismiss(); + return valid(); + } + + void close() noexcept { + if (shm_.ref() == 1) { + int eno; + if ((eno = ::pthread_mutex_destroy(mutex_)) != 0) { + ipc::error("fail pthread_mutex_destroy[%d]\n", eno); + } + } + shm_.release(); + mutex_ = nullptr; + } + + bool lock(std::uint64_t tm) noexcept { + for (;;) { + int eno = (tm == invalid_value) + ? ::pthread_mutex_lock(mutex_) + : ::pthread_mutex_timedlock(mutex_, detail::make_timespec(tm)); + switch (eno) { + case 0: + return true; + case ETIMEDOUT: + return false; + case EOWNERDEAD: + if (shm_.ref() > 1) { + shm_.sub_ref(); + } + int eno2 = ::pthread_mutex_consistent(mutex_); + if (eno2 != 0) { + ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); + return false; + } + int eno3 = ::pthread_mutex_unlock(mutex_); + if (eno3 != 0) { + ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_unlock[%d]\n", eno, eno3); + return false; + } + break; // loop again + default: + ipc::error("fail pthread_mutex_lock[%d]\n", eno); + return false; + } + } + } + + bool try_lock() noexcept(false) { + int eno = ::pthread_mutex_timedlock(mutex_, detail::make_timespec(0)); + switch (eno) { + case 0: + return true; + case ETIMEDOUT: + return false; + case EOWNERDEAD: + if (shm_.ref() > 1) { + shm_.sub_ref(); + } + int eno2 = ::pthread_mutex_consistent(mutex_); + if (eno2 != 0) { + ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); + break; + } + int eno3 = ::pthread_mutex_unlock(mutex_); + if (eno3 != 0) { + ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_unlock[%d]\n", eno, eno3); + break; + } + break; + default: + ipc::error("fail pthread_mutex_timedlock[%d]\n", eno); + break; + } + throw std::system_error{eno, std::system_category()}; + } + + bool unlock() noexcept { + int eno; + if ((eno = ::pthread_mutex_unlock(mutex_)) != 0) { + ipc::error("fail pthread_mutex_unlock[%d]\n", eno); + return false; + } + return true; + } +}; + +} // namespace sync +} // namespace detail +} // namespace ipc diff --git a/src/libipc/platform/mutex_win.h b/src/libipc/platform/mutex_win.h new file mode 100644 index 0000000..cac5d82 --- /dev/null +++ b/src/libipc/platform/mutex_win.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +#include + +#include "libipc/utility/log.h" + +#include "libipc/platform/to_tchar.h" +#include "libipc/platform/get_sa.h" + +namespace ipc { +namespace detail { +namespace sync { + +class mutex { + HANDLE h_ = NULL; + +public: + mutex() noexcept = default; + explicit mutex(char const *name) noexcept { + open(name); + } + + ~mutex() noexcept = default; + + HANDLE native() const noexcept { + return h_; + } + + bool valid() const noexcept { + return h_ != NULL; + } + + bool open(char const *name) noexcept { + close(); + h_ = ::CreateMutex(detail::get_sa(), FALSE, ipc::detail::to_tchar(name).c_str()); + if (h_ == NULL) { + ipc::error("fail CreateMutex[%lu]: %s\n", ::GetLastError(), name); + return false; + } + return true; + } + + void close() noexcept { + if (!valid()) return; + ::CloseHandle(h_); + h_ = NULL; + } + + bool lock(std::uint64_t tm) noexcept { + DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast(tm); + for(;;) { + switch ((ret = ::WaitForSingleObject(h_, ms))) { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + case WAIT_ABANDONED: + ipc::log("fail WaitForSingleObject[%lu]: WAIT_ABANDONED, try again.\n", ::GetLastError()); + if (!unlock()) { + return false; + } + break; // loop again + default: + ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret); + return false; + } + } + } + + bool try_lock() noexcept(false) { + DWORD ret = ::WaitForSingleObject(h_, 0); + switch (ret) { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + case WAIT_ABANDONED: + unlock(); + IPC_FALLTHROUGH_; + default: + ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret); + throw std::system_error{static_cast(ret), std::system_category()}; + } + } + + bool unlock() noexcept { + if (!::ReleaseMutex(h_)) { + ipc::error("fail ReleaseMutex[%lu]\n", ::GetLastError()); + return false; + } + return true; + } +}; + +} // namespace sync +} // namespace detail +} // namespace ipc diff --git a/src/libipc/platform/shm_linux.cpp b/src/libipc/platform/shm_linux.cpp index 9b523d6..f3396b7 100755 --- a/src/libipc/platform/shm_linux.cpp +++ b/src/libipc/platform/shm_linux.cpp @@ -22,7 +22,7 @@ namespace { struct info_t { - std::atomic_size_t acc_; + std::atomic acc_; }; struct id_info_t { @@ -81,6 +81,31 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) { return ii; } +std::uint32_t get_ref(id_t id) { + if (id == nullptr) { + ipc::error("fail get_ref: invalid id (null)\n"); + return 0; + } + auto ii = static_cast(id); + if (ii->mem_ == nullptr || ii->size_ == 0) { + return 0; + } + return acc_of(mem, ii->size_).load(std::memory_order_acquire); +} + +void sub_ref(id_t id) { + if (id == nullptr) { + ipc::error("fail sub_ref: invalid id (null)\n"); + return; + } + auto ii = static_cast(id); + if (ii->mem_ == nullptr || ii->size_ == 0) { + ipc::error("fail sub_ref: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_); + return; + } + acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acq_rel); +} + void * get_mem(id_t id, std::size_t * size) { if (id == nullptr) { ipc::error("fail get_mem: invalid id (null)\n"); @@ -137,7 +162,7 @@ void release(id_t id) { if (ii->mem_ == nullptr || ii->size_ == 0) { ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_); } - else if (acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acquire) == 1) { + else if (acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acq_rel) == 1) { ::munmap(ii->mem_, ii->size_); if (!ii->name_.empty()) { ::shm_unlink(ii->name_.c_str()); diff --git a/src/libipc/platform/shm_win.cpp b/src/libipc/platform/shm_win.cpp index 389372d..858a1e5 100755 --- a/src/libipc/platform/shm_win.cpp +++ b/src/libipc/platform/shm_win.cpp @@ -58,6 +58,14 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) { return ii; } +std::uint32_t get_ref(id_t) { + return 0; +} + +void sub_ref(id_t) { + // Do Nothing. +} + void * get_mem(id_t id, std::size_t * size) { if (id == nullptr) { ipc::error("fail get_mem: invalid id (null)\n"); diff --git a/src/libipc/platform/waiter_linux.h b/src/libipc/platform/waiter_linux.h index eb9a3b1..006ba6a 100755 --- a/src/libipc/platform/waiter_linux.h +++ b/src/libipc/platform/waiter_linux.h @@ -22,7 +22,7 @@ namespace ipc { namespace detail { -inline static bool calc_wait_time(timespec& ts, std::size_t tm /*ms*/) { +inline static bool calc_wait_time(timespec& ts, std::uint64_t tm /*ms*/) { timeval now; int eno = ::gettimeofday(&now, NULL); if (eno != 0) { @@ -89,8 +89,7 @@ public: return true; case EOWNERDEAD: if (::pthread_mutex_consistent(&mutex_) == 0) { - ::pthread_mutex_unlock(&mutex_); - break; + return true; } IPC_FALLTHROUGH_; case ENOTRECOVERABLE: @@ -138,7 +137,7 @@ public: IPC_PTHREAD_FUNC_(pthread_cond_destroy, &cond_); } - bool wait(mutex& mtx, std::size_t tm = invalid_value) { + bool wait(mutex& mtx, std::uint64_t tm = invalid_value) { switch (tm) { case 0: return true; @@ -221,7 +220,7 @@ public: return true; } - static bool wait(handle_t h, std::size_t tm = invalid_value) { + static bool wait(handle_t h, std::uint64_t tm = invalid_value) { if (h == invalid()) return false; switch (tm) { case 0: @@ -289,7 +288,7 @@ private: return ipc::detail::unique_lock(me_->lock_); } - bool sema_wait(std::size_t tm) { + bool sema_wait(std::uint64_t tm) { return sem_helper::wait(std::get<1>(h_), tm); } @@ -297,7 +296,7 @@ private: return sem_helper::post(std::get<1>(h_), count); } - bool handshake_wait(std::size_t tm) { + bool handshake_wait(std::uint64_t tm) { return sem_helper::wait(std::get<2>(h_), tm); } @@ -339,7 +338,7 @@ public: } template - bool wait_if(handle_t const & h, wait_flags * flags, F&& pred, std::size_t tm = invalid_value) { + bool wait_if(handle_t const & h, wait_flags * flags, F&& pred, std::uint64_t tm = invalid_value) { assert(flags != nullptr); contrl ctrl { this, flags, h }; @@ -400,7 +399,7 @@ public: } template - bool wait_if(handle_t h, waiter_helper::wait_flags * flags, F && pred, std::size_t tm = invalid_value) { + bool wait_if(handle_t h, waiter_helper::wait_flags * flags, F && pred, std::uint64_t tm = invalid_value) { if (h == invalid()) return false; return helper_.wait_if(h, flags, std::forward(pred), tm); } diff --git a/src/libipc/platform/waiter_win.h b/src/libipc/platform/waiter_win.h index 4f3d080..9f5f888 100755 --- a/src/libipc/platform/waiter_win.h +++ b/src/libipc/platform/waiter_win.h @@ -40,7 +40,7 @@ public: ::CloseHandle(h_); } - bool wait(std::size_t tm = invalid_value) { + bool wait(std::uint64_t tm = invalid_value) { DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast(tm); switch ((ret = ::WaitForSingleObject(h_, ms))) { case WAIT_OBJECT_0: @@ -102,7 +102,7 @@ class condition { return ipc::detail::unique_lock(me_->lock_); } - bool sema_wait(std::size_t tm) { + bool sema_wait(std::uint64_t tm) { return me_->sema_.wait(tm); } @@ -110,7 +110,7 @@ class condition { return me_->sema_.post(count); } - bool handshake_wait(std::size_t tm) { + bool handshake_wait(std::uint64_t tm) { return me_->handshake_.wait(tm); } @@ -151,7 +151,7 @@ public: } template - bool wait_if(Mutex & mtx, wait_flags * flags, F && pred, std::size_t tm = invalid_value) { + bool wait_if(Mutex & mtx, wait_flags * flags, F && pred, std::uint64_t tm = invalid_value) { assert(flags != nullptr); contrl ctrl { this, flags }; return waiter_helper::wait_if(ctrl, mtx, std::forward(pred), tm); @@ -201,7 +201,7 @@ public: } template - bool wait_if(handle_t& h, waiter_helper::wait_flags * flags, F&& pred, std::size_t tm = invalid_value) { + bool wait_if(handle_t& h, waiter_helper::wait_flags * flags, F&& pred, std::uint64_t tm = invalid_value) { if (h == invalid()) return false; class non_mutex { diff --git a/src/libipc/platform/waiter_wrapper.h b/src/libipc/platform/waiter_wrapper.h index 553d2e1..18c06e0 100755 --- a/src/libipc/platform/waiter_wrapper.h +++ b/src/libipc/platform/waiter_wrapper.h @@ -8,9 +8,7 @@ #include "libipc/memory/resource.h" #include "libipc/platform/detail.h" -#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ - defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ - defined(WINCE) || defined(_WIN32_WCE) +#if defined(IPC_OS_WINDOWS_) #include "libipc/platform/waiter_win.h" @@ -52,7 +50,7 @@ public: cnt_h_.release(); } - bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) { + bool wait(mutex_impl& mtx, std::uint64_t tm = invalid_value) { return base_t::wait_if(mtx, &flags_, [] { return true; }, tm); } }; @@ -60,7 +58,7 @@ public: } // namespace detail } // namespace ipc -#else /*!WIN*/ +#elif defined(IPC_OS_LINUX_) #include "libipc/platform/waiter_linux.h" @@ -123,7 +121,7 @@ public: class condition_impl : public object_impl { public: - bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) { + bool wait(mutex_impl& mtx, std::uint64_t tm = invalid_value) { return object().wait(mtx.object(), tm); } @@ -168,7 +166,7 @@ public: opened_.release(); } - bool wait(std::size_t tm = invalid_value) { + bool wait(std::uint64_t tm = invalid_value) { return sem_helper::wait(h_, tm); } @@ -179,8 +177,9 @@ public: } // namespace detail } // namespace ipc - -#endif/*!WIN*/ +#else/*linux*/ +# error "Unsupported platform." +#endif namespace ipc { namespace detail { @@ -235,7 +234,7 @@ public: } template - bool wait_if(F && pred, std::size_t tm = invalid_value) { + bool wait_if(F && pred, std::uint64_t tm = invalid_value) { if (!valid()) return false; return w_->wait_if(h_, &flags_, std::forward(pred), tm); } diff --git a/src/libipc/waiter_helper.h b/src/libipc/waiter_helper.h index a32035b..7bc6a08 100644 --- a/src/libipc/waiter_helper.h +++ b/src/libipc/waiter_helper.h @@ -24,7 +24,7 @@ struct waiter_helper { }; template - static bool wait_if(Ctrl & ctrl, Mutex & mtx, F && pred, std::size_t tm) { + static bool wait_if(Ctrl & ctrl, Mutex & mtx, F && pred, std::uint64_t tm) { auto & flags = ctrl.flags(); if (flags.is_closed_.load(std::memory_order_acquire)) { return false; diff --git a/src/mutex.cpp b/src/mutex.cpp new file mode 100644 index 0000000..813e334 --- /dev/null +++ b/src/mutex.cpp @@ -0,0 +1,70 @@ + +#include "libipc/mutex.h" + +#include "libipc/utility/pimpl.h" +#include "libipc/memory/resource.h" +#include "libipc/platform/detail.h" +#if defined(IPC_OS_WINDOWS_) +#include "libipc/platform/mutex_win.h" +#elif defined(IPC_OS_LINUX_) +#include "libipc/platform/mutex_linux.h" +#else/*linux*/ +# error "Unsupported platform." +#endif + +namespace ipc { +namespace sync { + +class mutex::mutex_ : public ipc::pimpl { +public: + ipc::detail::sync::mutex lock_; +}; + +mutex::mutex() + : p_(p_->make()) { +} + +mutex::mutex(char const * name) + : mutex() { + open(name); +} + +mutex::~mutex() { + close(); + p_->clear(); +} + +void const *mutex::native() const noexcept { + return impl(p_)->lock_.native(); +} + +void *mutex::native() noexcept { + return impl(p_)->lock_.native(); +} + +bool mutex::valid() const noexcept { + return impl(p_)->lock_.valid(); +} + +bool mutex::open(char const *name) noexcept { + return impl(p_)->lock_.open(name); +} + +void mutex::close() noexcept { + impl(p_)->lock_.close(); +} + +bool mutex::lock(std::uint64_t tm) noexcept { + return impl(p_)->lock_.lock(tm); +} + +bool mutex::try_lock() noexcept(false) { + return impl(p_)->lock_.try_lock(); +} + +bool mutex::unlock() noexcept { + return impl(p_)->lock_.unlock(); +} + +} // namespace sync +} // namespace ipc diff --git a/src/shm.cpp b/src/shm.cpp index a2eed93..24cb377 100755 --- a/src/shm.cpp +++ b/src/shm.cpp @@ -47,18 +47,26 @@ handle& handle::operator=(handle rhs) { return *this; } -bool handle::valid() const { +bool handle::valid() const noexcept { return impl(p_)->m_ != nullptr; } -std::size_t handle::size() const { +std::size_t handle::size() const noexcept { return impl(p_)->s_; } -char const * handle::name() const { +char const * handle::name() const noexcept { return impl(p_)->n_.c_str(); } +std::uint32_t handle::ref() const noexcept { + return shm::get_ref(impl(p_)->id_); +} + +void handle::sub_ref() noexcept { + shm::sub_ref(impl(p_)->id_); +} + bool handle::acquire(char const * name, std::size_t size, unsigned mode) { release(); impl(p_)->id_ = shm::acquire((impl(p_)->n_ = name).c_str(), size, mode); diff --git a/src/waiter.cpp b/src/waiter.cpp index 24a1382..2ac32c0 100755 --- a/src/waiter.cpp +++ b/src/waiter.cpp @@ -44,7 +44,7 @@ bool mutex::unlock() { #include "libipc/waiter_template.inc" -bool semaphore::wait(std::size_t tm) { +bool semaphore::wait(std::uint64_t tm) { return impl(p_)->h_.wait(tm); } @@ -62,7 +62,7 @@ bool semaphore::post(long count) { #include "libipc/waiter_template.inc" -bool condition::wait(mutex& mtx, std::size_t tm) { +bool condition::wait(mutex& mtx, std::uint64_t tm) { return impl(p_)->h_.wait(impl(mtx.p_)->h_, tm); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 07b0a34..9398aa3 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,7 +17,8 @@ include_directories( file(GLOB SRC_FILES ${LIBIPC_PROJECT_DIR}/test/*.cpp - ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp) + # ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp + ) file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/*.h) add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEAD_FILES}) diff --git a/test/test_pthread.cpp b/test/test_sync.cpp old mode 100755 new mode 100644 similarity index 59% rename from test/test_pthread.cpp rename to test/test_sync.cpp index 36d0acf..d2f78bf --- a/test/test_pthread.cpp +++ b/test/test_sync.cpp @@ -1,49 +1,83 @@ - -#include -#include - -#include "test.h" - -#if defined(__linux__) || defined(__linux) -#include -#include - -TEST(PThread, Robust) { - pthread_mutexattr_t ma; - pthread_mutexattr_init(&ma); - pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST); - pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_init(&mutex, &ma); - - std::thread{[&mutex] { - pthread_mutex_lock(&mutex); - // pthread_mutex_unlock(&mutex); - }}.join(); - - struct timespec tout; - clock_gettime(CLOCK_REALTIME, &tout); - int r = pthread_mutex_timedlock(&mutex, &tout); - EXPECT_EQ(r, EOWNERDEAD); - - pthread_mutex_consistent(&mutex); - pthread_mutex_unlock(&mutex); - pthread_mutex_destroy(&mutex); -} -#elif defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ - defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) -#include -#include - -TEST(PThread, Robust) { - HANDLE lock = CreateMutex(NULL, FALSE, _T("test-robust")); - std::thread{[] { - HANDLE lock = CreateMutex(NULL, FALSE, _T("test-robust")); - WaitForSingleObject(lock, 0); - }}.join(); - - DWORD r = WaitForSingleObject(lock, 0); - EXPECT_EQ(r, WAIT_ABANDONED); - - CloseHandle(lock); -} -#endif // !__linux__ \ No newline at end of file + +#include +#include +#include +#include + +#include "test.h" + +#if defined(__linux__) || defined(__linux) +#include +#include + +TEST(PThread, Robust) { + pthread_mutexattr_t ma; + pthread_mutexattr_init(&ma); + pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST); + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_init(&mutex, &ma); + + std::thread{[&mutex] { + pthread_mutex_lock(&mutex); + // pthread_mutex_unlock(&mutex); + }}.join(); + + struct timespec tout; + clock_gettime(CLOCK_REALTIME, &tout); + int r = pthread_mutex_timedlock(&mutex, &tout); + EXPECT_EQ(r, EOWNERDEAD); + + pthread_mutex_consistent(&mutex); + pthread_mutex_unlock(&mutex); + pthread_mutex_destroy(&mutex); +} +#elif defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) +#include +#include + +TEST(PThread, Robust) { + HANDLE lock = CreateMutex(NULL, FALSE, _T("test-robust")); + std::thread{[] { + HANDLE lock = CreateMutex(NULL, FALSE, _T("test-robust")); + WaitForSingleObject(lock, 0); + }}.join(); + + DWORD r = WaitForSingleObject(lock, 0); + EXPECT_EQ(r, WAIT_ABANDONED); + + CloseHandle(lock); +} +#endif // !__linux__ + +#include "libipc/mutex.h" + +TEST(Sync, Mutex) { + ipc::sync::mutex lock; + EXPECT_TRUE(lock.open("test-mutex-robust")); + + std::thread{[] { + ipc::sync::mutex lock{"test-mutex-robust"}; + EXPECT_TRUE(lock.valid()); + EXPECT_TRUE(lock.lock()); + }}.join(); + + EXPECT_THROW(lock.try_lock(), std::system_error); + + int i = 0; + EXPECT_TRUE(lock.lock()); + i = 100; + auto t2 = std::thread{[&i] { + ipc::sync::mutex lock{"test-mutex-robust"}; + EXPECT_TRUE(lock.valid()); + EXPECT_FALSE(lock.try_lock()); + EXPECT_TRUE(lock.lock()); + i += i; + EXPECT_TRUE(lock.unlock()); + }}; + std::this_thread::sleep_for(std::chrono::seconds(1)); + EXPECT_EQ(i, 100); + EXPECT_TRUE(lock.unlock()); + t2.join(); + EXPECT_EQ(i, 200); +} \ No newline at end of file From e5bf3b7c841de392ca9abc806d778bd3f9644d01 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 20 Jun 2021 15:32:54 +0800 Subject: [PATCH 07/19] for vs2015 error C3256 --- src/libipc/utility/pimpl.h | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/libipc/utility/pimpl.h b/src/libipc/utility/pimpl.h index 25074e7..ca0edd3 100755 --- a/src/libipc/utility/pimpl.h +++ b/src/libipc/utility/pimpl.h @@ -3,6 +3,7 @@ #include #include +#include "libipc/platform/detail.h" #include "libipc/utility/concept.h" #include "libipc/pool_alloc.h" @@ -17,49 +18,45 @@ template using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>; template -constexpr auto make_impl(P&&... params) -> IsImplComfortable { +IPC_CONSTEXPR_ auto make_impl(P&&... params) -> IsImplComfortable { T* buf {}; ::new (&buf) T { std::forward

(params)... }; return buf; } template -constexpr auto impl(T* const (& p)) -> IsImplComfortable { +IPC_CONSTEXPR_ auto impl(T* const (& p)) -> IsImplComfortable { return reinterpret_cast(&const_cast(reinterpret_cast(p))); } template -constexpr auto clear_impl(T* p) -> IsImplComfortable { +IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplComfortable { if (p != nullptr) impl(p)->~T(); } template -constexpr auto make_impl(P&&... params) -> IsImplUncomfortable { +IPC_CONSTEXPR_ auto make_impl(P&&... params) -> IsImplUncomfortable { return mem::alloc(std::forward

(params)...); } template -constexpr auto clear_impl(T* p) -> IsImplUncomfortable { +IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplUncomfortable { mem::free(p); } template -constexpr auto impl(T* const (& p)) -> IsImplUncomfortable { +IPC_CONSTEXPR_ auto impl(T* const (& p)) -> IsImplUncomfortable { return p; } template struct pimpl { template - constexpr static T* make(P&&... params) { + IPC_CONSTEXPR_ static T* make(P&&... params) { return make_impl(std::forward

(params)...); } -#if __cplusplus >= 201703L - constexpr void clear() { -#else /*__cplusplus < 201703L*/ - void clear() { -#endif/*__cplusplus < 201703L*/ + IPC_CONSTEXPR_ void clear() { clear_impl(static_cast(const_cast(this))); } }; From 3cf7d5bcd5eba1e776c3454dceb48a274534a381 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 20 Jun 2021 15:49:38 +0800 Subject: [PATCH 08/19] using IPC_OS_* in test --- test/test_sync.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_sync.cpp b/test/test_sync.cpp index d2f78bf..d413376 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -6,7 +6,8 @@ #include "test.h" -#if defined(__linux__) || defined(__linux) +#include "libipc/platform/detail.h" +#if defined(IPC_OS_LINUX_) #include #include @@ -31,8 +32,7 @@ TEST(PThread, Robust) { pthread_mutex_unlock(&mutex); pthread_mutex_destroy(&mutex); } -#elif defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ - defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) +#elif defined(IPC_OS_WINDOWS_) #include #include @@ -48,7 +48,7 @@ TEST(PThread, Robust) { CloseHandle(lock); } -#endif // !__linux__ +#endif // OS #include "libipc/mutex.h" From 8cd2a40bfd056d0b7466cc1c23f55f8a1d3c67db Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sat, 28 Nov 2020 22:26:07 +0800 Subject: [PATCH 09/19] update IPC_CONCEPT_ --- src/libipc/memory/alloc.h | 11 +++--- src/libipc/memory/wrapper.h | 17 +++++---- src/libipc/platform/tls_pointer_win.cpp | 1 - src/libipc/platform/to_tchar.h | 15 +++----- src/libipc/utility/concept.h | 47 ++++++++++++++++--------- src/libipc/utility/pimpl.h | 17 +++++---- 6 files changed, 62 insertions(+), 46 deletions(-) diff --git a/src/libipc/memory/alloc.h b/src/libipc/memory/alloc.h index 16bebc9..820361a 100755 --- a/src/libipc/memory/alloc.h +++ b/src/libipc/memory/alloc.h @@ -44,7 +44,8 @@ constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { return ( (size - 1) & ~(alignment - 1) ) + alignment; } -IPC_CONCEPT_(has_take, take(std::move(std::declval()))); +template +IPC_CONCEPT_(has_take, require([](auto && t)->decltype(t.take(std::move(t))) {})); class scope_alloc_base { protected: @@ -116,13 +117,13 @@ public: } template - auto take(scope_alloc && rhs) -> ipc::require::value> { + auto take(scope_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } template - auto take(scope_alloc && rhs) -> ipc::require::value> { + auto take(scope_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); } @@ -255,7 +256,7 @@ public: } template - auto take(fixed_alloc && rhs) -> ipc::require::value> { + auto take(fixed_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } @@ -394,7 +395,7 @@ public: } template - auto take(variable_alloc && rhs) -> ipc::require::value> { + auto take(variable_alloc && rhs) -> std::enable_if_t::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } diff --git a/src/libipc/memory/wrapper.h b/src/libipc/memory/wrapper.h index 5e76f88..7f84838 100755 --- a/src/libipc/memory/wrapper.h +++ b/src/libipc/memory/wrapper.h @@ -27,7 +27,8 @@ namespace mem { namespace detail { -IPC_CONCEPT_(is_comparable, operator<(std::declval())); +template +IPC_CONCEPT_(is_comparable, require([](auto && t)->decltype(t < t) {})); } // namespace detail @@ -71,8 +72,10 @@ public: template class default_recycler : public limited_recycler { - IPC_CONCEPT_(has_remain, remain()); - IPC_CONCEPT_(has_empty , empty()); + template + IPC_CONCEPT_(has_remain, require([](auto && t)->decltype(t.remain()) {})); + template + IPC_CONCEPT_(has_empty, require([](auto && t)->decltype(t.empty()) {})); template void try_fill(A & alc) { @@ -86,28 +89,28 @@ public: template auto try_replenish(alloc_policy & alc, std::size_t size) - -> ipc::require::value && has_remain::value> { + -> std::enable_if_t::value && has_remain::value> { if (alc.remain() >= size) return; this->try_fill(alc); } template auto try_replenish(alloc_policy & alc, std::size_t /*size*/) - -> ipc::require::value && !has_remain::value && has_empty::value> { + -> std::enable_if_t::value && !has_remain::value && has_empty::value> { if (!alc.empty()) return; this->try_fill(alc); } template auto try_replenish(alloc_policy & alc, std::size_t /*size*/) - -> ipc::require::value && has_empty::value> { + -> std::enable_if_t::value && has_empty::value> { if (!alc.empty()) return; this->try_recover(alc); } template IPC_CONSTEXPR_ auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) noexcept - -> ipc::require<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { + -> std::enable_if_t<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { // Do Nothing. } }; diff --git a/src/libipc/platform/tls_pointer_win.cpp b/src/libipc/platform/tls_pointer_win.cpp index cdbe208..3d56909 100755 --- a/src/libipc/platform/tls_pointer_win.cpp +++ b/src/libipc/platform/tls_pointer_win.cpp @@ -7,7 +7,6 @@ * @remarks * Windows doesn't support a per-thread destructor with its TLS primitives. * So, here will build it manually by inserting a function to be called on each thread's exit. - * * @see * - https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way * - https://src.chromium.org/viewvc/chrome/trunk/src/base/threading/thread_local_storage_win.cc diff --git a/src/libipc/platform/to_tchar.h b/src/libipc/platform/to_tchar.h index df67b2d..2598b83 100755 --- a/src/libipc/platform/to_tchar.h +++ b/src/libipc/platform/to_tchar.h @@ -15,19 +15,14 @@ namespace ipc { namespace detail { -struct has_value_type_ { - template static std::true_type check(typename T::value_type *); - template static std::false_type check(...); -}; - -template (nullptr))> -struct is_same_char : std::is_same {}; - template -struct is_same_char : std::is_same {}; +IPC_CONCEPT_(has_same_char, + require([]()->std::enable_if_t::value> {}) || + require([]()->std::enable_if_t::value> {}) +); template -using IsSameChar = ipc::require::value, R>; +using IsSameChar = std::enable_if_t::value, R>; //////////////////////////////////////////////////////////////// /// to_tchar implementation diff --git a/src/libipc/utility/concept.h b/src/libipc/utility/concept.h index 9325ddc..5192c07 100755 --- a/src/libipc/utility/concept.h +++ b/src/libipc/utility/concept.h @@ -1,29 +1,42 @@ #pragma once -#include // std::enable_if +#include // std::declval namespace ipc { -// concept helpers +/** + * @remarks + * <> Concepts TS Improve on C++17 + * @see + * - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0726r0.html + * - https://www.youtube.com/watch?v=TorW5ekkL_w +*/ +namespace detail { -template -using require = typename std::enable_if::type; +template ()(std::declval()...))> +constexpr bool require_impl(int) { return true; } + +template +constexpr bool require_impl(...) { return false; } + +} // namespace detail + +template +constexpr bool require(F&&) { + return detail::require_impl(int{}); +} + +} // namespace ipc + +/// concept helpers #ifdef IPC_CONCEPT_ # error "IPC_CONCEPT_ has been defined." #endif -#define IPC_CONCEPT_(NAME, WHAT) \ -template \ -class NAME { \ -private: \ - template \ - static std::true_type check(decltype(std::declval().WHAT)*); \ - template \ - static std::false_type check(...); \ -public: \ - using type = decltype(check(nullptr)); \ - constexpr static auto value = type::value; \ +#define IPC_CONCEPT_($$name, $$what) \ +class $$name { \ +public: \ + constexpr static bool value = $$what; \ } - -} // namespace ipc diff --git a/src/libipc/utility/pimpl.h b/src/libipc/utility/pimpl.h index 25074e7..fbb5ea2 100755 --- a/src/libipc/utility/pimpl.h +++ b/src/libipc/utility/pimpl.h @@ -8,14 +8,19 @@ namespace ipc { +template +IPC_CONCEPT_(is_impl_comfortable, + require([](auto && t)->std::enable_if_t<(sizeof(t) <= sizeof(T*))> {}) +); + +template +using IsImplComfortable = std::enable_if_t::value, R>; + +template +using IsImplUncomfortable = std::enable_if_t::value, R>; + // pimpl small object optimization helpers -template -using IsImplComfortable = ipc::require<(sizeof(T) <= sizeof(T*)), R>; - -template -using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>; - template constexpr auto make_impl(P&&... params) -> IsImplComfortable { T* buf {}; From 563aabfe4b945448142fc68d907b3725ffd70edf Mon Sep 17 00:00:00 2001 From: mutouyun Date: Fri, 1 Jan 2021 11:13:30 +0800 Subject: [PATCH 10/19] add profiler from adah1972 --- test/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4b695eb..07b0a34 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,7 +15,9 @@ include_directories( ${LIBIPC_PROJECT_DIR}/3rdparty ${LIBIPC_PROJECT_DIR}/3rdparty/gtest/include) -file(GLOB SRC_FILES ${LIBIPC_PROJECT_DIR}/test/*.cpp) +file(GLOB SRC_FILES + ${LIBIPC_PROJECT_DIR}/test/*.cpp + ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp) file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/*.h) add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEAD_FILES}) From 85342dcaa6ac30ec7893c099df9260bfc392077a Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 6 Jun 2021 19:35:39 +0800 Subject: [PATCH 11/19] test/profiler --- test/profiler/README.md | 11 ++++++ test/profiler/profiler.cpp | 77 ++++++++++++++++++++++++++++++++++++++ test/profiler/profiler.h | 35 +++++++++++++++++ test/profiler/rdtsc.h | 52 +++++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 test/profiler/README.md create mode 100644 test/profiler/profiler.cpp create mode 100644 test/profiler/profiler.h create mode 100644 test/profiler/rdtsc.h diff --git a/test/profiler/README.md b/test/profiler/README.md new file mode 100644 index 0000000..d4a6bea --- /dev/null +++ b/test/profiler/README.md @@ -0,0 +1,11 @@ +# A Quick Introduction to C++ Performance Tuning +(From: https://github.com/adah1972/cpp_summit_2020.git) + +This repository contains the presentation file and example code for my +presentation at the C++ Summit 2020 held in Shenzhen, China on 4–5 December +2020. + +The presentation content is shared under a [Creative Commons Attribution-Share +Alike 2.5 Licence](http://creativecommons.org/licenses/by-sa/2.5/). The code +is put in the public domain (i.e. do whatever you like with it), though an +acknowledgement will be appreciated (but not required). diff --git a/test/profiler/profiler.cpp b/test/profiler/profiler.cpp new file mode 100644 index 0000000..d8fd7bc --- /dev/null +++ b/test/profiler/profiler.cpp @@ -0,0 +1,77 @@ +#include "profiler.h" +#include +#include +#include + +namespace { + +struct profiling_data { + int number; + int call_count{}; + uint64_t call_duration{}; +}; + +class profiler { +public: + profiler(); + ~profiler(); + + void add_data(int number, uint64_t duration); + +private: + std::vector data_; +}; + +profiler::profiler() +{ + size_t len = 0; + for (;;) { + if (name_map[len].name == NULL) { + break; + } + ++len; + } + data_.resize(len); + int i = 0; + for (auto& item : data_) { + assert(i == name_map[i].number); + item.number = i; + ++i; + } +} + +profiler::~profiler() +{ +#ifndef NDEBUG + for (auto& item : data_) { + if (item.call_count == 0) { + continue; + } + std::cout << item.number << " " << name_map[item.number].name + << ":\n"; + std::cout << " Call count: " << item.call_count << '\n'; + std::cout << " Call duration: " << item.call_duration << '\n'; + std::cout << " Average duration: " + << item.call_duration * 1.0 / + (item.call_count != 0 ? item.call_count : 1) + << '\n'; + } +#endif +} + +void profiler::add_data(int number, uint64_t duration) +{ + assert(number >= 0 && number < static_cast(data_.size())); + data_[number].call_count++; + data_[number].call_duration += duration; +} + +profiler profiler_instance; + +} // unnamed namespace + +profiling_checker::~profiling_checker() +{ + auto end_time = rdtsc(); + profiler_instance.add_data(number_, end_time - start_time_); +} diff --git a/test/profiler/profiler.h b/test/profiler/profiler.h new file mode 100644 index 0000000..d04264a --- /dev/null +++ b/test/profiler/profiler.h @@ -0,0 +1,35 @@ +#ifndef PROFILER_H +#define PROFILER_H + +#include "rdtsc.h" + +struct name_mapper { + int number; + const char* name; +}; + +extern name_mapper name_map[]; + +class profiling_checker { +public: + profiling_checker(int number); + ~profiling_checker(); + +private: + int number_; + uint64_t start_time_; +}; + +inline profiling_checker::profiling_checker(int number) + : number_(number) +{ + start_time_ = rdtsc(); +} + +#ifdef NDEBUG +#define PROFILE_CHECK(func_number) (void)0 +#else +#define PROFILE_CHECK(func_number) profiling_checker _checker(func_number) +#endif + +#endif // PROFILER_H diff --git a/test/profiler/rdtsc.h b/test/profiler/rdtsc.h new file mode 100644 index 0000000..80e35c7 --- /dev/null +++ b/test/profiler/rdtsc.h @@ -0,0 +1,52 @@ +#ifndef RDTSC_H +#define RDTSC_H + +#include // uint64_t + +#if defined(_M_X64) || defined(_M_IX86) || defined(__x86_64) || defined(__i386) +# ifdef _WIN32 +# include // __rdtsc +# else +# include // __rdtsc +# endif +# define HAS_HW_RDTSC 1 +#else +# include // std::chrono::high_resolution_clock +# define HAS_HW_RDTSC 0 +#endif + +inline uint64_t rdtsc() +{ +#if HAS_HW_RDTSC + // _mm_lfence() might be used to serialize the instruction stream, + // and it would guarantee that RDTSC will not be reordered with + // other instructions. However, measurements show that the overhead + // may be too big (easily 15 to 30 CPU cycles) for profiling + // purposes: if reordering matters, the overhead matters too! + + // Forbid the compiler from reordering instructions +# ifdef _MSC_VER + _ReadWriteBarrier(); +# else + __asm__ __volatile__("" : : : "memory"); +# endif + + uint64_t result = __rdtsc(); + + // Forbid the compiler from reordering instructions +# ifdef _MSC_VER + _ReadWriteBarrier(); +# else + __asm__ __volatile__("" : : : "memory"); +# endif + + return result; +#else + auto now = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast( + now.time_since_epoch()) + .count(); +#endif +} + +#endif // RDTSC_H From 12944502a1dbcecdd2024d7bed0d2d36d83d388c Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 6 Jun 2021 19:40:54 +0800 Subject: [PATCH 12/19] Revert "update IPC_CONCEPT_" This reverts commit 1e5547e6dfd0605fa62be67899c6c893aa61f9fc. --- src/libipc/memory/alloc.h | 11 +++--- src/libipc/memory/wrapper.h | 17 ++++----- src/libipc/platform/tls_pointer_win.cpp | 1 + src/libipc/platform/to_tchar.h | 15 +++++--- src/libipc/utility/concept.h | 47 +++++++++---------------- src/libipc/utility/pimpl.h | 17 ++++----- 6 files changed, 46 insertions(+), 62 deletions(-) diff --git a/src/libipc/memory/alloc.h b/src/libipc/memory/alloc.h index 820361a..16bebc9 100755 --- a/src/libipc/memory/alloc.h +++ b/src/libipc/memory/alloc.h @@ -44,8 +44,7 @@ constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { return ( (size - 1) & ~(alignment - 1) ) + alignment; } -template -IPC_CONCEPT_(has_take, require([](auto && t)->decltype(t.take(std::move(t))) {})); +IPC_CONCEPT_(has_take, take(std::move(std::declval()))); class scope_alloc_base { protected: @@ -117,13 +116,13 @@ public: } template - auto take(scope_alloc && rhs) -> std::enable_if_t::value> { + auto take(scope_alloc && rhs) -> ipc::require::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } template - auto take(scope_alloc && rhs) -> std::enable_if_t::value> { + auto take(scope_alloc && rhs) -> ipc::require::value> { base_t::take(std::move(rhs)); } @@ -256,7 +255,7 @@ public: } template - auto take(fixed_alloc && rhs) -> std::enable_if_t::value> { + auto take(fixed_alloc && rhs) -> ipc::require::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } @@ -395,7 +394,7 @@ public: } template - auto take(variable_alloc && rhs) -> std::enable_if_t::value> { + auto take(variable_alloc && rhs) -> ipc::require::value> { base_t::take(std::move(rhs)); alloc_.take(std::move(rhs.alloc_)); } diff --git a/src/libipc/memory/wrapper.h b/src/libipc/memory/wrapper.h index 7f84838..5e76f88 100755 --- a/src/libipc/memory/wrapper.h +++ b/src/libipc/memory/wrapper.h @@ -27,8 +27,7 @@ namespace mem { namespace detail { -template -IPC_CONCEPT_(is_comparable, require([](auto && t)->decltype(t < t) {})); +IPC_CONCEPT_(is_comparable, operator<(std::declval())); } // namespace detail @@ -72,10 +71,8 @@ public: template class default_recycler : public limited_recycler { - template - IPC_CONCEPT_(has_remain, require([](auto && t)->decltype(t.remain()) {})); - template - IPC_CONCEPT_(has_empty, require([](auto && t)->decltype(t.empty()) {})); + IPC_CONCEPT_(has_remain, remain()); + IPC_CONCEPT_(has_empty , empty()); template void try_fill(A & alc) { @@ -89,28 +86,28 @@ public: template auto try_replenish(alloc_policy & alc, std::size_t size) - -> std::enable_if_t::value && has_remain::value> { + -> ipc::require::value && has_remain::value> { if (alc.remain() >= size) return; this->try_fill(alc); } template auto try_replenish(alloc_policy & alc, std::size_t /*size*/) - -> std::enable_if_t::value && !has_remain::value && has_empty::value> { + -> ipc::require::value && !has_remain::value && has_empty::value> { if (!alc.empty()) return; this->try_fill(alc); } template auto try_replenish(alloc_policy & alc, std::size_t /*size*/) - -> std::enable_if_t::value && has_empty::value> { + -> ipc::require::value && has_empty::value> { if (!alc.empty()) return; this->try_recover(alc); } template IPC_CONSTEXPR_ auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) noexcept - -> std::enable_if_t<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { + -> ipc::require<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { // Do Nothing. } }; diff --git a/src/libipc/platform/tls_pointer_win.cpp b/src/libipc/platform/tls_pointer_win.cpp index 3d56909..cdbe208 100755 --- a/src/libipc/platform/tls_pointer_win.cpp +++ b/src/libipc/platform/tls_pointer_win.cpp @@ -7,6 +7,7 @@ * @remarks * Windows doesn't support a per-thread destructor with its TLS primitives. * So, here will build it manually by inserting a function to be called on each thread's exit. + * * @see * - https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way * - https://src.chromium.org/viewvc/chrome/trunk/src/base/threading/thread_local_storage_win.cc diff --git a/src/libipc/platform/to_tchar.h b/src/libipc/platform/to_tchar.h index 2598b83..df67b2d 100755 --- a/src/libipc/platform/to_tchar.h +++ b/src/libipc/platform/to_tchar.h @@ -15,14 +15,19 @@ namespace ipc { namespace detail { +struct has_value_type_ { + template static std::true_type check(typename T::value_type *); + template static std::false_type check(...); +}; + +template (nullptr))> +struct is_same_char : std::is_same {}; + template -IPC_CONCEPT_(has_same_char, - require([]()->std::enable_if_t::value> {}) || - require([]()->std::enable_if_t::value> {}) -); +struct is_same_char : std::is_same {}; template -using IsSameChar = std::enable_if_t::value, R>; +using IsSameChar = ipc::require::value, R>; //////////////////////////////////////////////////////////////// /// to_tchar implementation diff --git a/src/libipc/utility/concept.h b/src/libipc/utility/concept.h index 5192c07..9325ddc 100755 --- a/src/libipc/utility/concept.h +++ b/src/libipc/utility/concept.h @@ -1,42 +1,29 @@ #pragma once -#include // std::declval +#include // std::enable_if namespace ipc { -/** - * @remarks - * <> Concepts TS Improve on C++17 - * @see - * - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0726r0.html - * - https://www.youtube.com/watch?v=TorW5ekkL_w -*/ -namespace detail { +// concept helpers -template ()(std::declval()...))> -constexpr bool require_impl(int) { return true; } - -template -constexpr bool require_impl(...) { return false; } - -} // namespace detail - -template -constexpr bool require(F&&) { - return detail::require_impl(int{}); -} - -} // namespace ipc - -/// concept helpers +template +using require = typename std::enable_if::type; #ifdef IPC_CONCEPT_ # error "IPC_CONCEPT_ has been defined." #endif -#define IPC_CONCEPT_($$name, $$what) \ -class $$name { \ -public: \ - constexpr static bool value = $$what; \ +#define IPC_CONCEPT_(NAME, WHAT) \ +template \ +class NAME { \ +private: \ + template \ + static std::true_type check(decltype(std::declval().WHAT)*); \ + template \ + static std::false_type check(...); \ +public: \ + using type = decltype(check(nullptr)); \ + constexpr static auto value = type::value; \ } + +} // namespace ipc diff --git a/src/libipc/utility/pimpl.h b/src/libipc/utility/pimpl.h index fbb5ea2..25074e7 100755 --- a/src/libipc/utility/pimpl.h +++ b/src/libipc/utility/pimpl.h @@ -8,19 +8,14 @@ namespace ipc { -template -IPC_CONCEPT_(is_impl_comfortable, - require([](auto && t)->std::enable_if_t<(sizeof(t) <= sizeof(T*))> {}) -); - -template -using IsImplComfortable = std::enable_if_t::value, R>; - -template -using IsImplUncomfortable = std::enable_if_t::value, R>; - // pimpl small object optimization helpers +template +using IsImplComfortable = ipc::require<(sizeof(T) <= sizeof(T*)), R>; + +template +using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>; + template constexpr auto make_impl(P&&... params) -> IsImplComfortable { T* buf {}; From 455c0b479d99c06b5bd578301d22f9b4b393f62c Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 20 Jun 2021 15:23:44 +0800 Subject: [PATCH 13/19] add sync::mutex for windows/linux --- .gitignore | 1 + include/libipc/def.h | 7 +- include/libipc/ipc.h | 26 ++-- include/libipc/mutex.h | 38 ++++++ include/libipc/shm.h | 13 +- include/libipc/waiter.h | 4 +- src/ipc.cpp | 18 +-- src/libipc/platform/detail.h | 12 ++ src/libipc/platform/get_wait_time.h | 39 ++++++ src/libipc/platform/mutex_linux.h | 167 +++++++++++++++++++++++ src/libipc/platform/mutex_win.h | 100 ++++++++++++++ src/libipc/platform/shm_linux.cpp | 29 +++- src/libipc/platform/shm_win.cpp | 8 ++ src/libipc/platform/waiter_linux.h | 17 ++- src/libipc/platform/waiter_win.h | 10 +- src/libipc/platform/waiter_wrapper.h | 19 ++- src/libipc/waiter_helper.h | 2 +- src/mutex.cpp | 70 ++++++++++ src/shm.cpp | 14 +- src/waiter.cpp | 4 +- test/CMakeLists.txt | 3 +- test/{test_pthread.cpp => test_sync.cpp} | 132 +++++++++++------- 22 files changed, 622 insertions(+), 111 deletions(-) create mode 100644 include/libipc/mutex.h create mode 100644 src/libipc/platform/get_wait_time.h create mode 100644 src/libipc/platform/mutex_linux.h create mode 100644 src/libipc/platform/mutex_win.h create mode 100644 src/mutex.cpp rename test/{test_pthread.cpp => test_sync.cpp} (59%) mode change 100755 => 100644 diff --git a/.gitignore b/.gitignore index 5ea6006..c83c97d 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ CMakeLists.txt.user* # My output files build +.vscode \ No newline at end of file diff --git a/include/libipc/def.h b/include/libipc/def.h index 9be5206..d02ff43 100755 --- a/include/libipc/def.h +++ b/include/libipc/def.h @@ -25,13 +25,16 @@ using uint_t = typename uint::type; // constants +enum : std::uint32_t { + invalid_value = (std::numeric_limits::max)(), + default_timeout = 100, // ms +}; + enum : std::size_t { - invalid_value = (std::numeric_limits::max)(), data_length = 64, large_msg_limit = data_length, large_msg_align = 512, large_msg_cache = 32, - default_timeout = 100 // ms }; enum class relat { // multiplicity of the relationship diff --git a/include/libipc/ipc.h b/include/libipc/ipc.h index f6380ae..64b262c 100755 --- a/include/libipc/ipc.h +++ b/include/libipc/ipc.h @@ -27,12 +27,12 @@ struct IPC_EXPORT chan_impl { static char const * name(ipc::handle_t h); static std::size_t recv_count(ipc::handle_t h); - static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm); + static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm); - static bool send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm); - static buff_t recv(ipc::handle_t h, std::size_t tm); + static bool send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm); + static buff_t recv(ipc::handle_t h, std::uint64_t tm); - static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm); + static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm); static buff_t try_recv(ipc::handle_t h); }; @@ -120,41 +120,41 @@ public: return detail_t::recv_count(h_); } - bool wait_for_recv(std::size_t r_count, std::size_t tm = invalid_value) const { + bool wait_for_recv(std::size_t r_count, std::uint64_t tm = invalid_value) const { return detail_t::wait_for_recv(h_, r_count, tm); } - static bool wait_for_recv(char const * name, std::size_t r_count, std::size_t tm = invalid_value) { + static bool wait_for_recv(char const * name, std::size_t r_count, std::uint64_t tm = invalid_value) { return chan_wrapper(name).wait_for_recv(r_count, tm); } /** * If timeout, this function would call 'force_push' to send the data forcibly. */ - bool send(void const * data, std::size_t size, std::size_t tm = default_timeout) { + bool send(void const * data, std::size_t size, std::uint64_t tm = default_timeout) { return detail_t::send(h_, data, size, tm); } - bool send(buff_t const & buff, std::size_t tm = default_timeout) { + bool send(buff_t const & buff, std::uint64_t tm = default_timeout) { return this->send(buff.data(), buff.size(), tm); } - bool send(std::string const & str, std::size_t tm = default_timeout) { + bool send(std::string const & str, std::uint64_t tm = default_timeout) { return this->send(str.c_str(), str.size() + 1, tm); } /** * If timeout, this function would just return false. */ - bool try_send(void const * data, std::size_t size, std::size_t tm = default_timeout) { + bool try_send(void const * data, std::size_t size, std::uint64_t tm = default_timeout) { return detail_t::try_send(h_, data, size, tm); } - bool try_send(buff_t const & buff, std::size_t tm = default_timeout) { + bool try_send(buff_t const & buff, std::uint64_t tm = default_timeout) { return this->try_send(buff.data(), buff.size(), tm); } - bool try_send(std::string const & str, std::size_t tm = default_timeout) { + bool try_send(std::string const & str, std::uint64_t tm = default_timeout) { return this->try_send(str.c_str(), str.size() + 1, tm); } - buff_t recv(std::size_t tm = invalid_value) { + buff_t recv(std::uint64_t tm = invalid_value) { return detail_t::recv(h_, tm); } diff --git a/include/libipc/mutex.h b/include/libipc/mutex.h new file mode 100644 index 0000000..23f7b53 --- /dev/null +++ b/include/libipc/mutex.h @@ -0,0 +1,38 @@ +#pragma once + +#include // std::uint64_t +#include + +#include "libipc/export.h" +#include "libipc/def.h" + +namespace ipc { +namespace sync { + +class IPC_EXPORT mutex { + mutex(mutex const &) = delete; + mutex &operator=(mutex const &) = delete; + +public: + mutex(); + explicit mutex(char const *name); + ~mutex(); + + void const *native() const noexcept; + void *native() noexcept; + + bool valid() const noexcept; + + bool open(char const *name) noexcept; + void close() noexcept; + bool lock(std::uint64_t tm = ipc::invalid_value) noexcept; + bool try_lock() noexcept(false); // std::system_error + bool unlock() noexcept; + +private: + class mutex_; + mutex_* p_; +}; + +} // namespace sync +} // namespace ipc diff --git a/include/libipc/shm.h b/include/libipc/shm.h index 91a68e4..395b6c8 100755 --- a/include/libipc/shm.h +++ b/include/libipc/shm.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "libipc/export.h" @@ -20,6 +21,9 @@ IPC_EXPORT void release(id_t id); IPC_EXPORT void remove (id_t id); IPC_EXPORT void remove (char const * name); +IPC_EXPORT std::uint32_t get_ref(id_t id); +IPC_EXPORT void sub_ref(id_t id); + class IPC_EXPORT handle { public: handle(); @@ -31,9 +35,12 @@ public: void swap(handle& rhs); handle& operator=(handle rhs); - bool valid() const; - std::size_t size () const; - char const * name () const; + bool valid() const noexcept; + std::size_t size () const noexcept; + char const * name () const noexcept; + + std::uint32_t ref() const noexcept; + void sub_ref() noexcept; bool acquire(char const * name, std::size_t size, unsigned mode = create | open); void release(); diff --git a/include/libipc/waiter.h b/include/libipc/waiter.h index a4c3c09..37197e0 100755 --- a/include/libipc/waiter.h +++ b/include/libipc/waiter.h @@ -54,7 +54,7 @@ public: bool open (char const * name, long count = 0); void close(); - bool wait(std::size_t tm = invalid_value); + bool wait(std::uint64_t tm = invalid_value); bool post(long count = 1); private: @@ -81,7 +81,7 @@ public: bool open (char const * name); void close(); - bool wait(mutex&, std::size_t tm = invalid_value); + bool wait(mutex&, std::uint64_t tm = invalid_value); bool notify(); bool broadcast(); diff --git a/src/ipc.cpp b/src/ipc.cpp index 12a629b..bfa1376 100755 --- a/src/ipc.cpp +++ b/src/ipc.cpp @@ -291,7 +291,7 @@ struct conn_info_head { }; template -bool wait_for(W& waiter, F&& pred, std::size_t tm) { +bool wait_for(W& waiter, F&& pred, std::uint64_t tm) { if (tm == 0) return !pred(); for (unsigned k = 0; pred();) { bool loop = true, ret = true; @@ -403,7 +403,7 @@ static std::size_t recv_count(ipc::handle_t h) noexcept { return que->conn_count(); } -static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) { +static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm) { auto que = queue_of(h); if (que == nullptr) { return false; @@ -475,7 +475,7 @@ static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t s return true; } -static bool send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm) { +static bool send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) { return send([tm](auto info, auto que, auto msg_id) { return [tm, info, que, msg_id](std::int32_t remain, void const * data, std::size_t size) { if (!wait_for(info->wt_waiter_, [&] { @@ -500,7 +500,7 @@ static bool send(ipc::handle_t h, void const * data, std::size_t size, std::size }, h, data, size); } -static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm) { +static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) { return send([tm](auto info, auto que, auto msg_id) { return [tm, info, que, msg_id](std::int32_t remain, void const * data, std::size_t size) { if (!wait_for(info->wt_waiter_, [&] { @@ -514,7 +514,7 @@ static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std:: }, h, data, size); } -static ipc::buff_t recv(ipc::handle_t h, std::size_t tm) { +static ipc::buff_t recv(ipc::handle_t h, std::uint64_t tm) { auto que = queue_of(h); if (que == nullptr) { ipc::error("fail: recv, queue_of(h) == nullptr\n"); @@ -630,22 +630,22 @@ std::size_t chan_impl::recv_count(ipc::handle_t h) { } template -bool chan_impl::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) { +bool chan_impl::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm) { return detail_impl>::wait_for_recv(h, r_count, tm); } template -bool chan_impl::send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm) { +bool chan_impl::send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) { return detail_impl>::send(h, data, size, tm); } template -buff_t chan_impl::recv(ipc::handle_t h, std::size_t tm) { +buff_t chan_impl::recv(ipc::handle_t h, std::uint64_t tm) { return detail_impl>::recv(h, tm); } template -bool chan_impl::try_send(ipc::handle_t h, void const * data, std::size_t size, std::size_t tm) { +bool chan_impl::try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) { return detail_impl>::try_send(h, data, size, tm); } diff --git a/src/libipc/platform/detail.h b/src/libipc/platform/detail.h index fbf539a..97bbd12 100755 --- a/src/libipc/platform/detail.h +++ b/src/libipc/platform/detail.h @@ -22,6 +22,18 @@ # error "IPC_CONSTEXPR_ has been defined." #endif +// detect platform + +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ + defined(WINCE) || defined(_WIN32_WCE) +# define IPC_OS_WINDOWS_ +#endif/*WIN*/ + +#if defined(__linux__) || defined(__linux) +# define IPC_OS_LINUX_ +#endif/*linux*/ + #if __cplusplus >= 201703L #define IPC_UNUSED_ [[maybe_unused]] diff --git a/src/libipc/platform/get_wait_time.h b/src/libipc/platform/get_wait_time.h new file mode 100644 index 0000000..785cd75 --- /dev/null +++ b/src/libipc/platform/get_wait_time.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include "libipc/utility/log.h" + +namespace ipc { +namespace detail { + +inline bool calc_wait_time(timespec &ts, std::uint64_t tm /*ms*/) noexcept { + timeval now; + int eno = ::gettimeofday(&now, NULL); + if (eno != 0) { + ipc::error("fail gettimeofday [%d]\n", eno); + return false; + } + ts.tv_nsec = (now.tv_usec + (tm % 1000) * 1000) * 1000; + ts.tv_sec = now.tv_sec + (tm / 1000) + (ts.tv_nsec / 1000000000l); + ts.tv_nsec %= 1000000000l; + return true; +} + +inline timespec make_timespec(std::uint64_t tm /*ms*/) noexcept(false) { + timespec ts {}; + if (!calc_wait_time(ts, tm)) { + ipc::error("fail calc_wait_time: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n", + tm, ts.tv_sec, ts.tv_nsec); + throw std::system_error{static_cast(errno), std::system_category()}; + } + return ts; +} + +} // namespace detail +} // namespace ipc diff --git a/src/libipc/platform/mutex_linux.h b/src/libipc/platform/mutex_linux.h new file mode 100644 index 0000000..370a297 --- /dev/null +++ b/src/libipc/platform/mutex_linux.h @@ -0,0 +1,167 @@ +#pragma once + +#include +#include +#include + +#include + +#include "libipc/platform/get_wait_time.h" +#include "libipc/platform/detail.h" +#include "libipc/utility/log.h" +#include "libipc/utility/scope_guard.h" +#include "libipc/shm.h" + +namespace ipc { +namespace detail { +namespace sync { + +class mutex { + ipc::shm::handle shm_; + pthread_mutex_t *mutex_ = nullptr; + +public: + mutex() noexcept = default; + explicit mutex(char const *name) noexcept { + open(name); + } + + ~mutex() noexcept = default; + + pthread_mutex_t const *native() const noexcept { + return mutex_; + } + + pthread_mutex_t *native() noexcept { + return mutex_; + } + + bool valid() const noexcept { + static const tmp[sizeof pthread_mutex_t] {}; + return shm_.valid() + && (mutex_ != nullptr) + && (std::memcmp(tmp, mutex_, sizeof pthread_mutex_t) != 0); + } + + bool open(char const *name) noexcept { + close(); + if (!shm_.acquire(name, sizeof pthread_mutex_t)) { + ipc::error("fail shm.acquire: %s\n", name); + return false; + } + mutex_ = static_cast(shm_.get()); + assert(mutex_ != nullptr); + if ((shm_.ref() == 1) && valid()/*it means mutex has been inited*/) { + ::pthread_mutex_destroy(mutex_); + } + auto finally = ipc::guard([this] { close(); }); // close when failed + // init mutex + int eno; + pthread_mutexattr_t mutex_attr; + if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) { + ipc::error("fail pthread_mutexattr_init[%d]\n", eno); + return false; + } + IPC_UNUSED_ auto guard_mutex_attr = unique_ptr(&mutex_attr, ::pthread_mutexattr_destroy); + if ((eno = ::pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0) { + ipc::error("fail pthread_mutexattr_setpshared[%d]\n", eno); + return false; + } + if ((eno = ::pthread_mutexattr_setrobust(&mutex_attr, PTHREAD_MUTEX_ROBUST)) != 0) { + ipc::error("fail pthread_mutexattr_setrobust[%d]\n", eno); + return false; + } + *mutex_ = PTHREAD_MUTEX_INITIALIZER; + if ((eno = ::pthread_mutex_init(mutex_, &mutex_attr)) != 0) { + ipc::error("fail pthread_mutex_init[%d]\n", eno); + return false; + } + finally.dismiss(); + return valid(); + } + + void close() noexcept { + if (shm_.ref() == 1) { + int eno; + if ((eno = ::pthread_mutex_destroy(mutex_)) != 0) { + ipc::error("fail pthread_mutex_destroy[%d]\n", eno); + } + } + shm_.release(); + mutex_ = nullptr; + } + + bool lock(std::uint64_t tm) noexcept { + for (;;) { + int eno = (tm == invalid_value) + ? ::pthread_mutex_lock(mutex_) + : ::pthread_mutex_timedlock(mutex_, detail::make_timespec(tm)); + switch (eno) { + case 0: + return true; + case ETIMEDOUT: + return false; + case EOWNERDEAD: + if (shm_.ref() > 1) { + shm_.sub_ref(); + } + int eno2 = ::pthread_mutex_consistent(mutex_); + if (eno2 != 0) { + ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); + return false; + } + int eno3 = ::pthread_mutex_unlock(mutex_); + if (eno3 != 0) { + ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_unlock[%d]\n", eno, eno3); + return false; + } + break; // loop again + default: + ipc::error("fail pthread_mutex_lock[%d]\n", eno); + return false; + } + } + } + + bool try_lock() noexcept(false) { + int eno = ::pthread_mutex_timedlock(mutex_, detail::make_timespec(0)); + switch (eno) { + case 0: + return true; + case ETIMEDOUT: + return false; + case EOWNERDEAD: + if (shm_.ref() > 1) { + shm_.sub_ref(); + } + int eno2 = ::pthread_mutex_consistent(mutex_); + if (eno2 != 0) { + ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); + break; + } + int eno3 = ::pthread_mutex_unlock(mutex_); + if (eno3 != 0) { + ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_unlock[%d]\n", eno, eno3); + break; + } + break; + default: + ipc::error("fail pthread_mutex_timedlock[%d]\n", eno); + break; + } + throw std::system_error{eno, std::system_category()}; + } + + bool unlock() noexcept { + int eno; + if ((eno = ::pthread_mutex_unlock(mutex_)) != 0) { + ipc::error("fail pthread_mutex_unlock[%d]\n", eno); + return false; + } + return true; + } +}; + +} // namespace sync +} // namespace detail +} // namespace ipc diff --git a/src/libipc/platform/mutex_win.h b/src/libipc/platform/mutex_win.h new file mode 100644 index 0000000..cac5d82 --- /dev/null +++ b/src/libipc/platform/mutex_win.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +#include + +#include "libipc/utility/log.h" + +#include "libipc/platform/to_tchar.h" +#include "libipc/platform/get_sa.h" + +namespace ipc { +namespace detail { +namespace sync { + +class mutex { + HANDLE h_ = NULL; + +public: + mutex() noexcept = default; + explicit mutex(char const *name) noexcept { + open(name); + } + + ~mutex() noexcept = default; + + HANDLE native() const noexcept { + return h_; + } + + bool valid() const noexcept { + return h_ != NULL; + } + + bool open(char const *name) noexcept { + close(); + h_ = ::CreateMutex(detail::get_sa(), FALSE, ipc::detail::to_tchar(name).c_str()); + if (h_ == NULL) { + ipc::error("fail CreateMutex[%lu]: %s\n", ::GetLastError(), name); + return false; + } + return true; + } + + void close() noexcept { + if (!valid()) return; + ::CloseHandle(h_); + h_ = NULL; + } + + bool lock(std::uint64_t tm) noexcept { + DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast(tm); + for(;;) { + switch ((ret = ::WaitForSingleObject(h_, ms))) { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + case WAIT_ABANDONED: + ipc::log("fail WaitForSingleObject[%lu]: WAIT_ABANDONED, try again.\n", ::GetLastError()); + if (!unlock()) { + return false; + } + break; // loop again + default: + ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret); + return false; + } + } + } + + bool try_lock() noexcept(false) { + DWORD ret = ::WaitForSingleObject(h_, 0); + switch (ret) { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + case WAIT_ABANDONED: + unlock(); + IPC_FALLTHROUGH_; + default: + ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret); + throw std::system_error{static_cast(ret), std::system_category()}; + } + } + + bool unlock() noexcept { + if (!::ReleaseMutex(h_)) { + ipc::error("fail ReleaseMutex[%lu]\n", ::GetLastError()); + return false; + } + return true; + } +}; + +} // namespace sync +} // namespace detail +} // namespace ipc diff --git a/src/libipc/platform/shm_linux.cpp b/src/libipc/platform/shm_linux.cpp index 9b523d6..f3396b7 100755 --- a/src/libipc/platform/shm_linux.cpp +++ b/src/libipc/platform/shm_linux.cpp @@ -22,7 +22,7 @@ namespace { struct info_t { - std::atomic_size_t acc_; + std::atomic acc_; }; struct id_info_t { @@ -81,6 +81,31 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) { return ii; } +std::uint32_t get_ref(id_t id) { + if (id == nullptr) { + ipc::error("fail get_ref: invalid id (null)\n"); + return 0; + } + auto ii = static_cast(id); + if (ii->mem_ == nullptr || ii->size_ == 0) { + return 0; + } + return acc_of(mem, ii->size_).load(std::memory_order_acquire); +} + +void sub_ref(id_t id) { + if (id == nullptr) { + ipc::error("fail sub_ref: invalid id (null)\n"); + return; + } + auto ii = static_cast(id); + if (ii->mem_ == nullptr || ii->size_ == 0) { + ipc::error("fail sub_ref: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_); + return; + } + acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acq_rel); +} + void * get_mem(id_t id, std::size_t * size) { if (id == nullptr) { ipc::error("fail get_mem: invalid id (null)\n"); @@ -137,7 +162,7 @@ void release(id_t id) { if (ii->mem_ == nullptr || ii->size_ == 0) { ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_); } - else if (acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acquire) == 1) { + else if (acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acq_rel) == 1) { ::munmap(ii->mem_, ii->size_); if (!ii->name_.empty()) { ::shm_unlink(ii->name_.c_str()); diff --git a/src/libipc/platform/shm_win.cpp b/src/libipc/platform/shm_win.cpp index 389372d..858a1e5 100755 --- a/src/libipc/platform/shm_win.cpp +++ b/src/libipc/platform/shm_win.cpp @@ -58,6 +58,14 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) { return ii; } +std::uint32_t get_ref(id_t) { + return 0; +} + +void sub_ref(id_t) { + // Do Nothing. +} + void * get_mem(id_t id, std::size_t * size) { if (id == nullptr) { ipc::error("fail get_mem: invalid id (null)\n"); diff --git a/src/libipc/platform/waiter_linux.h b/src/libipc/platform/waiter_linux.h index eb9a3b1..006ba6a 100755 --- a/src/libipc/platform/waiter_linux.h +++ b/src/libipc/platform/waiter_linux.h @@ -22,7 +22,7 @@ namespace ipc { namespace detail { -inline static bool calc_wait_time(timespec& ts, std::size_t tm /*ms*/) { +inline static bool calc_wait_time(timespec& ts, std::uint64_t tm /*ms*/) { timeval now; int eno = ::gettimeofday(&now, NULL); if (eno != 0) { @@ -89,8 +89,7 @@ public: return true; case EOWNERDEAD: if (::pthread_mutex_consistent(&mutex_) == 0) { - ::pthread_mutex_unlock(&mutex_); - break; + return true; } IPC_FALLTHROUGH_; case ENOTRECOVERABLE: @@ -138,7 +137,7 @@ public: IPC_PTHREAD_FUNC_(pthread_cond_destroy, &cond_); } - bool wait(mutex& mtx, std::size_t tm = invalid_value) { + bool wait(mutex& mtx, std::uint64_t tm = invalid_value) { switch (tm) { case 0: return true; @@ -221,7 +220,7 @@ public: return true; } - static bool wait(handle_t h, std::size_t tm = invalid_value) { + static bool wait(handle_t h, std::uint64_t tm = invalid_value) { if (h == invalid()) return false; switch (tm) { case 0: @@ -289,7 +288,7 @@ private: return ipc::detail::unique_lock(me_->lock_); } - bool sema_wait(std::size_t tm) { + bool sema_wait(std::uint64_t tm) { return sem_helper::wait(std::get<1>(h_), tm); } @@ -297,7 +296,7 @@ private: return sem_helper::post(std::get<1>(h_), count); } - bool handshake_wait(std::size_t tm) { + bool handshake_wait(std::uint64_t tm) { return sem_helper::wait(std::get<2>(h_), tm); } @@ -339,7 +338,7 @@ public: } template - bool wait_if(handle_t const & h, wait_flags * flags, F&& pred, std::size_t tm = invalid_value) { + bool wait_if(handle_t const & h, wait_flags * flags, F&& pred, std::uint64_t tm = invalid_value) { assert(flags != nullptr); contrl ctrl { this, flags, h }; @@ -400,7 +399,7 @@ public: } template - bool wait_if(handle_t h, waiter_helper::wait_flags * flags, F && pred, std::size_t tm = invalid_value) { + bool wait_if(handle_t h, waiter_helper::wait_flags * flags, F && pred, std::uint64_t tm = invalid_value) { if (h == invalid()) return false; return helper_.wait_if(h, flags, std::forward(pred), tm); } diff --git a/src/libipc/platform/waiter_win.h b/src/libipc/platform/waiter_win.h index 4f3d080..9f5f888 100755 --- a/src/libipc/platform/waiter_win.h +++ b/src/libipc/platform/waiter_win.h @@ -40,7 +40,7 @@ public: ::CloseHandle(h_); } - bool wait(std::size_t tm = invalid_value) { + bool wait(std::uint64_t tm = invalid_value) { DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast(tm); switch ((ret = ::WaitForSingleObject(h_, ms))) { case WAIT_OBJECT_0: @@ -102,7 +102,7 @@ class condition { return ipc::detail::unique_lock(me_->lock_); } - bool sema_wait(std::size_t tm) { + bool sema_wait(std::uint64_t tm) { return me_->sema_.wait(tm); } @@ -110,7 +110,7 @@ class condition { return me_->sema_.post(count); } - bool handshake_wait(std::size_t tm) { + bool handshake_wait(std::uint64_t tm) { return me_->handshake_.wait(tm); } @@ -151,7 +151,7 @@ public: } template - bool wait_if(Mutex & mtx, wait_flags * flags, F && pred, std::size_t tm = invalid_value) { + bool wait_if(Mutex & mtx, wait_flags * flags, F && pred, std::uint64_t tm = invalid_value) { assert(flags != nullptr); contrl ctrl { this, flags }; return waiter_helper::wait_if(ctrl, mtx, std::forward(pred), tm); @@ -201,7 +201,7 @@ public: } template - bool wait_if(handle_t& h, waiter_helper::wait_flags * flags, F&& pred, std::size_t tm = invalid_value) { + bool wait_if(handle_t& h, waiter_helper::wait_flags * flags, F&& pred, std::uint64_t tm = invalid_value) { if (h == invalid()) return false; class non_mutex { diff --git a/src/libipc/platform/waiter_wrapper.h b/src/libipc/platform/waiter_wrapper.h index 553d2e1..18c06e0 100755 --- a/src/libipc/platform/waiter_wrapper.h +++ b/src/libipc/platform/waiter_wrapper.h @@ -8,9 +8,7 @@ #include "libipc/memory/resource.h" #include "libipc/platform/detail.h" -#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ - defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ - defined(WINCE) || defined(_WIN32_WCE) +#if defined(IPC_OS_WINDOWS_) #include "libipc/platform/waiter_win.h" @@ -52,7 +50,7 @@ public: cnt_h_.release(); } - bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) { + bool wait(mutex_impl& mtx, std::uint64_t tm = invalid_value) { return base_t::wait_if(mtx, &flags_, [] { return true; }, tm); } }; @@ -60,7 +58,7 @@ public: } // namespace detail } // namespace ipc -#else /*!WIN*/ +#elif defined(IPC_OS_LINUX_) #include "libipc/platform/waiter_linux.h" @@ -123,7 +121,7 @@ public: class condition_impl : public object_impl { public: - bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) { + bool wait(mutex_impl& mtx, std::uint64_t tm = invalid_value) { return object().wait(mtx.object(), tm); } @@ -168,7 +166,7 @@ public: opened_.release(); } - bool wait(std::size_t tm = invalid_value) { + bool wait(std::uint64_t tm = invalid_value) { return sem_helper::wait(h_, tm); } @@ -179,8 +177,9 @@ public: } // namespace detail } // namespace ipc - -#endif/*!WIN*/ +#else/*linux*/ +# error "Unsupported platform." +#endif namespace ipc { namespace detail { @@ -235,7 +234,7 @@ public: } template - bool wait_if(F && pred, std::size_t tm = invalid_value) { + bool wait_if(F && pred, std::uint64_t tm = invalid_value) { if (!valid()) return false; return w_->wait_if(h_, &flags_, std::forward(pred), tm); } diff --git a/src/libipc/waiter_helper.h b/src/libipc/waiter_helper.h index a32035b..7bc6a08 100644 --- a/src/libipc/waiter_helper.h +++ b/src/libipc/waiter_helper.h @@ -24,7 +24,7 @@ struct waiter_helper { }; template - static bool wait_if(Ctrl & ctrl, Mutex & mtx, F && pred, std::size_t tm) { + static bool wait_if(Ctrl & ctrl, Mutex & mtx, F && pred, std::uint64_t tm) { auto & flags = ctrl.flags(); if (flags.is_closed_.load(std::memory_order_acquire)) { return false; diff --git a/src/mutex.cpp b/src/mutex.cpp new file mode 100644 index 0000000..813e334 --- /dev/null +++ b/src/mutex.cpp @@ -0,0 +1,70 @@ + +#include "libipc/mutex.h" + +#include "libipc/utility/pimpl.h" +#include "libipc/memory/resource.h" +#include "libipc/platform/detail.h" +#if defined(IPC_OS_WINDOWS_) +#include "libipc/platform/mutex_win.h" +#elif defined(IPC_OS_LINUX_) +#include "libipc/platform/mutex_linux.h" +#else/*linux*/ +# error "Unsupported platform." +#endif + +namespace ipc { +namespace sync { + +class mutex::mutex_ : public ipc::pimpl { +public: + ipc::detail::sync::mutex lock_; +}; + +mutex::mutex() + : p_(p_->make()) { +} + +mutex::mutex(char const * name) + : mutex() { + open(name); +} + +mutex::~mutex() { + close(); + p_->clear(); +} + +void const *mutex::native() const noexcept { + return impl(p_)->lock_.native(); +} + +void *mutex::native() noexcept { + return impl(p_)->lock_.native(); +} + +bool mutex::valid() const noexcept { + return impl(p_)->lock_.valid(); +} + +bool mutex::open(char const *name) noexcept { + return impl(p_)->lock_.open(name); +} + +void mutex::close() noexcept { + impl(p_)->lock_.close(); +} + +bool mutex::lock(std::uint64_t tm) noexcept { + return impl(p_)->lock_.lock(tm); +} + +bool mutex::try_lock() noexcept(false) { + return impl(p_)->lock_.try_lock(); +} + +bool mutex::unlock() noexcept { + return impl(p_)->lock_.unlock(); +} + +} // namespace sync +} // namespace ipc diff --git a/src/shm.cpp b/src/shm.cpp index a2eed93..24cb377 100755 --- a/src/shm.cpp +++ b/src/shm.cpp @@ -47,18 +47,26 @@ handle& handle::operator=(handle rhs) { return *this; } -bool handle::valid() const { +bool handle::valid() const noexcept { return impl(p_)->m_ != nullptr; } -std::size_t handle::size() const { +std::size_t handle::size() const noexcept { return impl(p_)->s_; } -char const * handle::name() const { +char const * handle::name() const noexcept { return impl(p_)->n_.c_str(); } +std::uint32_t handle::ref() const noexcept { + return shm::get_ref(impl(p_)->id_); +} + +void handle::sub_ref() noexcept { + shm::sub_ref(impl(p_)->id_); +} + bool handle::acquire(char const * name, std::size_t size, unsigned mode) { release(); impl(p_)->id_ = shm::acquire((impl(p_)->n_ = name).c_str(), size, mode); diff --git a/src/waiter.cpp b/src/waiter.cpp index 24a1382..2ac32c0 100755 --- a/src/waiter.cpp +++ b/src/waiter.cpp @@ -44,7 +44,7 @@ bool mutex::unlock() { #include "libipc/waiter_template.inc" -bool semaphore::wait(std::size_t tm) { +bool semaphore::wait(std::uint64_t tm) { return impl(p_)->h_.wait(tm); } @@ -62,7 +62,7 @@ bool semaphore::post(long count) { #include "libipc/waiter_template.inc" -bool condition::wait(mutex& mtx, std::size_t tm) { +bool condition::wait(mutex& mtx, std::uint64_t tm) { return impl(p_)->h_.wait(impl(mtx.p_)->h_, tm); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 07b0a34..9398aa3 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,7 +17,8 @@ include_directories( file(GLOB SRC_FILES ${LIBIPC_PROJECT_DIR}/test/*.cpp - ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp) + # ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp + ) file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/*.h) add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEAD_FILES}) diff --git a/test/test_pthread.cpp b/test/test_sync.cpp old mode 100755 new mode 100644 similarity index 59% rename from test/test_pthread.cpp rename to test/test_sync.cpp index 36d0acf..d2f78bf --- a/test/test_pthread.cpp +++ b/test/test_sync.cpp @@ -1,49 +1,83 @@ - -#include -#include - -#include "test.h" - -#if defined(__linux__) || defined(__linux) -#include -#include - -TEST(PThread, Robust) { - pthread_mutexattr_t ma; - pthread_mutexattr_init(&ma); - pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST); - pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_init(&mutex, &ma); - - std::thread{[&mutex] { - pthread_mutex_lock(&mutex); - // pthread_mutex_unlock(&mutex); - }}.join(); - - struct timespec tout; - clock_gettime(CLOCK_REALTIME, &tout); - int r = pthread_mutex_timedlock(&mutex, &tout); - EXPECT_EQ(r, EOWNERDEAD); - - pthread_mutex_consistent(&mutex); - pthread_mutex_unlock(&mutex); - pthread_mutex_destroy(&mutex); -} -#elif defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ - defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) -#include -#include - -TEST(PThread, Robust) { - HANDLE lock = CreateMutex(NULL, FALSE, _T("test-robust")); - std::thread{[] { - HANDLE lock = CreateMutex(NULL, FALSE, _T("test-robust")); - WaitForSingleObject(lock, 0); - }}.join(); - - DWORD r = WaitForSingleObject(lock, 0); - EXPECT_EQ(r, WAIT_ABANDONED); - - CloseHandle(lock); -} -#endif // !__linux__ \ No newline at end of file + +#include +#include +#include +#include + +#include "test.h" + +#if defined(__linux__) || defined(__linux) +#include +#include + +TEST(PThread, Robust) { + pthread_mutexattr_t ma; + pthread_mutexattr_init(&ma); + pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST); + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_init(&mutex, &ma); + + std::thread{[&mutex] { + pthread_mutex_lock(&mutex); + // pthread_mutex_unlock(&mutex); + }}.join(); + + struct timespec tout; + clock_gettime(CLOCK_REALTIME, &tout); + int r = pthread_mutex_timedlock(&mutex, &tout); + EXPECT_EQ(r, EOWNERDEAD); + + pthread_mutex_consistent(&mutex); + pthread_mutex_unlock(&mutex); + pthread_mutex_destroy(&mutex); +} +#elif defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) +#include +#include + +TEST(PThread, Robust) { + HANDLE lock = CreateMutex(NULL, FALSE, _T("test-robust")); + std::thread{[] { + HANDLE lock = CreateMutex(NULL, FALSE, _T("test-robust")); + WaitForSingleObject(lock, 0); + }}.join(); + + DWORD r = WaitForSingleObject(lock, 0); + EXPECT_EQ(r, WAIT_ABANDONED); + + CloseHandle(lock); +} +#endif // !__linux__ + +#include "libipc/mutex.h" + +TEST(Sync, Mutex) { + ipc::sync::mutex lock; + EXPECT_TRUE(lock.open("test-mutex-robust")); + + std::thread{[] { + ipc::sync::mutex lock{"test-mutex-robust"}; + EXPECT_TRUE(lock.valid()); + EXPECT_TRUE(lock.lock()); + }}.join(); + + EXPECT_THROW(lock.try_lock(), std::system_error); + + int i = 0; + EXPECT_TRUE(lock.lock()); + i = 100; + auto t2 = std::thread{[&i] { + ipc::sync::mutex lock{"test-mutex-robust"}; + EXPECT_TRUE(lock.valid()); + EXPECT_FALSE(lock.try_lock()); + EXPECT_TRUE(lock.lock()); + i += i; + EXPECT_TRUE(lock.unlock()); + }}; + std::this_thread::sleep_for(std::chrono::seconds(1)); + EXPECT_EQ(i, 100); + EXPECT_TRUE(lock.unlock()); + t2.join(); + EXPECT_EQ(i, 200); +} \ No newline at end of file From d974641a0779bb08edb751110f155dd5c891cc0b Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 20 Jun 2021 15:32:54 +0800 Subject: [PATCH 14/19] for vs2015 error C3256 --- src/libipc/utility/pimpl.h | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/libipc/utility/pimpl.h b/src/libipc/utility/pimpl.h index 25074e7..ca0edd3 100755 --- a/src/libipc/utility/pimpl.h +++ b/src/libipc/utility/pimpl.h @@ -3,6 +3,7 @@ #include #include +#include "libipc/platform/detail.h" #include "libipc/utility/concept.h" #include "libipc/pool_alloc.h" @@ -17,49 +18,45 @@ template using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>; template -constexpr auto make_impl(P&&... params) -> IsImplComfortable { +IPC_CONSTEXPR_ auto make_impl(P&&... params) -> IsImplComfortable { T* buf {}; ::new (&buf) T { std::forward

(params)... }; return buf; } template -constexpr auto impl(T* const (& p)) -> IsImplComfortable { +IPC_CONSTEXPR_ auto impl(T* const (& p)) -> IsImplComfortable { return reinterpret_cast(&const_cast(reinterpret_cast(p))); } template -constexpr auto clear_impl(T* p) -> IsImplComfortable { +IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplComfortable { if (p != nullptr) impl(p)->~T(); } template -constexpr auto make_impl(P&&... params) -> IsImplUncomfortable { +IPC_CONSTEXPR_ auto make_impl(P&&... params) -> IsImplUncomfortable { return mem::alloc(std::forward

(params)...); } template -constexpr auto clear_impl(T* p) -> IsImplUncomfortable { +IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplUncomfortable { mem::free(p); } template -constexpr auto impl(T* const (& p)) -> IsImplUncomfortable { +IPC_CONSTEXPR_ auto impl(T* const (& p)) -> IsImplUncomfortable { return p; } template struct pimpl { template - constexpr static T* make(P&&... params) { + IPC_CONSTEXPR_ static T* make(P&&... params) { return make_impl(std::forward

(params)...); } -#if __cplusplus >= 201703L - constexpr void clear() { -#else /*__cplusplus < 201703L*/ - void clear() { -#endif/*__cplusplus < 201703L*/ + IPC_CONSTEXPR_ void clear() { clear_impl(static_cast(const_cast(this))); } }; From a970ace446077e3b58f1bc3bcdb3198b1b9dc0d5 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 20 Jun 2021 15:49:38 +0800 Subject: [PATCH 15/19] using IPC_OS_* in test --- test/test_sync.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_sync.cpp b/test/test_sync.cpp index d2f78bf..d413376 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -6,7 +6,8 @@ #include "test.h" -#if defined(__linux__) || defined(__linux) +#include "libipc/platform/detail.h" +#if defined(IPC_OS_LINUX_) #include #include @@ -31,8 +32,7 @@ TEST(PThread, Robust) { pthread_mutex_unlock(&mutex); pthread_mutex_destroy(&mutex); } -#elif defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ - defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) +#elif defined(IPC_OS_WINDOWS_) #include #include @@ -48,7 +48,7 @@ TEST(PThread, Robust) { CloseHandle(lock); } -#endif // !__linux__ +#endif // OS #include "libipc/mutex.h" From 0e0bbd729c07190107d52924423767f225977030 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 20 Jun 2021 23:52:22 +0800 Subject: [PATCH 16/19] remove .travis.yml --- .travis.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100755 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100755 index 310edf1..0000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: cpp -os: linux -dist: xenial - -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-8 - -matrix: - include: - - compiler: gcc - env: - - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" - - compiler: clang - -before_install: - - eval "${MATRIX_EVAL}" - -script: - - mkdir -p ./build && cd ./build - - cmake -DCMAKE_BUILD_TYPE=Release -DLIBIPC_BUILD_TESTS=ON .. - - make -j`nproc` - - export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH && ./bin/test-ipc - -notifications: - slack: - on_success: never - on_failure: never From 40eafcfd2af94124e3819e46d22d977644abe081 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Mon, 21 Jun 2021 00:00:11 +0800 Subject: [PATCH 17/19] fix errors --- src/libipc/platform/mutex_linux.h | 66 ++++++++++++++++--------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/src/libipc/platform/mutex_linux.h b/src/libipc/platform/mutex_linux.h index 370a297..2c18939 100644 --- a/src/libipc/platform/mutex_linux.h +++ b/src/libipc/platform/mutex_linux.h @@ -37,15 +37,15 @@ public: } bool valid() const noexcept { - static const tmp[sizeof pthread_mutex_t] {}; + static const tmp[sizeof(pthread_mutex_t)] {}; return shm_.valid() && (mutex_ != nullptr) - && (std::memcmp(tmp, mutex_, sizeof pthread_mutex_t) != 0); + && (std::memcmp(tmp, mutex_, sizeof(pthread_mutex_t)) != 0); } bool open(char const *name) noexcept { close(); - if (!shm_.acquire(name, sizeof pthread_mutex_t)) { + if (!shm_.acquire(name, sizeof(pthread_mutex_t))) { ipc::error("fail shm.acquire: %s\n", name); return false; } @@ -93,27 +93,29 @@ public: bool lock(std::uint64_t tm) noexcept { for (;;) { + auto ts = detail::make_timespec(tm); int eno = (tm == invalid_value) ? ::pthread_mutex_lock(mutex_) - : ::pthread_mutex_timedlock(mutex_, detail::make_timespec(tm)); + : ::pthread_mutex_timedlock(mutex_, &ts); switch (eno) { case 0: return true; case ETIMEDOUT: return false; - case EOWNERDEAD: - if (shm_.ref() > 1) { - shm_.sub_ref(); - } - int eno2 = ::pthread_mutex_consistent(mutex_); - if (eno2 != 0) { - ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); - return false; - } - int eno3 = ::pthread_mutex_unlock(mutex_); - if (eno3 != 0) { - ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_unlock[%d]\n", eno, eno3); - return false; + case EOWNERDEAD: { + if (shm_.ref() > 1) { + shm_.sub_ref(); + } + int eno2 = ::pthread_mutex_consistent(mutex_); + if (eno2 != 0) { + ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); + return false; + } + int eno3 = ::pthread_mutex_unlock(mutex_); + if (eno3 != 0) { + ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_unlock[%d]\n", eno, eno3); + return false; + } } break; // loop again default: @@ -124,25 +126,27 @@ public: } bool try_lock() noexcept(false) { - int eno = ::pthread_mutex_timedlock(mutex_, detail::make_timespec(0)); + auto ts = detail::make_timespec(0); + int eno = ::pthread_mutex_timedlock(mutex_, &ts); switch (eno) { case 0: return true; case ETIMEDOUT: return false; - case EOWNERDEAD: - if (shm_.ref() > 1) { - shm_.sub_ref(); - } - int eno2 = ::pthread_mutex_consistent(mutex_); - if (eno2 != 0) { - ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); - break; - } - int eno3 = ::pthread_mutex_unlock(mutex_); - if (eno3 != 0) { - ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_unlock[%d]\n", eno, eno3); - break; + case EOWNERDEAD: { + if (shm_.ref() > 1) { + shm_.sub_ref(); + } + int eno2 = ::pthread_mutex_consistent(mutex_); + if (eno2 != 0) { + ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); + break; + } + int eno3 = ::pthread_mutex_unlock(mutex_); + if (eno3 != 0) { + ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_unlock[%d]\n", eno, eno3); + break; + } } break; default: From dd29ed5d1ffaa7d851d820fc7ca0775f3f38d6ea Mon Sep 17 00:00:00 2001 From: mutouyun Date: Mon, 21 Jun 2021 00:02:48 +0800 Subject: [PATCH 18/19] fix errors --- src/libipc/platform/mutex_linux.h | 2 +- src/libipc/platform/shm_linux.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libipc/platform/mutex_linux.h b/src/libipc/platform/mutex_linux.h index 2c18939..f4bb819 100644 --- a/src/libipc/platform/mutex_linux.h +++ b/src/libipc/platform/mutex_linux.h @@ -37,7 +37,7 @@ public: } bool valid() const noexcept { - static const tmp[sizeof(pthread_mutex_t)] {}; + static const char tmp[sizeof(pthread_mutex_t)] {}; return shm_.valid() && (mutex_ != nullptr) && (std::memcmp(tmp, mutex_, sizeof(pthread_mutex_t)) != 0); diff --git a/src/libipc/platform/shm_linux.cpp b/src/libipc/platform/shm_linux.cpp index f3396b7..da1ec9c 100755 --- a/src/libipc/platform/shm_linux.cpp +++ b/src/libipc/platform/shm_linux.cpp @@ -90,7 +90,7 @@ std::uint32_t get_ref(id_t id) { if (ii->mem_ == nullptr || ii->size_ == 0) { return 0; } - return acc_of(mem, ii->size_).load(std::memory_order_acquire); + return acc_of(ii->mem_, ii->size_).load(std::memory_order_acquire); } void sub_ref(id_t id) { From d74f4c560921f078dbc8928f1f15770a937b8fec Mon Sep 17 00:00:00 2001 From: mutouyun Date: Mon, 21 Jun 2021 00:06:17 +0800 Subject: [PATCH 19/19] fix: mutex() noexcept is implicitly deleted --- src/libipc/platform/mutex_linux.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libipc/platform/mutex_linux.h b/src/libipc/platform/mutex_linux.h index f4bb819..30c04e4 100644 --- a/src/libipc/platform/mutex_linux.h +++ b/src/libipc/platform/mutex_linux.h @@ -21,12 +21,12 @@ class mutex { pthread_mutex_t *mutex_ = nullptr; public: - mutex() noexcept = default; + mutex() = default; explicit mutex(char const *name) noexcept { open(name); } - ~mutex() noexcept = default; + ~mutex() = default; pthread_mutex_t const *native() const noexcept { return mutex_;