diff --git a/build/src.pro b/build/src.pro index e279c7a..a446988 100644 --- a/build/src.pro +++ b/build/src.pro @@ -26,7 +26,10 @@ HEADERS += \ ../include/tls_pointer.h \ ../src/channel.inc \ ../src/route.inc \ - ../src/id_pool.inc + ../src/id_pool.inc \ + ../src/memory/alloc.hpp \ + ../src/memory/wrapper.hpp \ + ../src/memory/resource.hpp SOURCES += \ ../src/shm.cpp \ diff --git a/include/circ_queue.h b/include/circ_queue.h index ccbd1f0..81e32bc 100644 --- a/include/circ_queue.h +++ b/include/circ_queue.h @@ -7,7 +7,6 @@ #include #include #include -#include #include "def.h" #include "circ_elem_array.h" @@ -98,7 +97,7 @@ public: template static queue* multi_wait_for(F&& upd) noexcept { - for (unsigned k = 0;; ++k) { + while (1) { auto [ques, size] = upd(); for (std::size_t i = 0; i < static_cast(size); ++i) { queue* que = ques[i]; @@ -108,9 +107,7 @@ public: return que; } } - if (k < 1024) std::this_thread::yield(); - // yielding => sleeping - else std::this_thread::sleep_for(std::chrono::milliseconds(1)); + std::this_thread::yield(); } } diff --git a/src/ipc.cpp b/src/ipc.cpp index 409cbe3..399cb19 100644 --- a/src/ipc.cpp +++ b/src/ipc.cpp @@ -1,6 +1,5 @@ #include "ipc.h" -#include #include #include #include @@ -12,6 +11,8 @@ #include "shm.h" #include "tls_pointer.h" +#include "memory/resource.hpp" + namespace { using namespace ipc; @@ -55,7 +56,7 @@ inline auto& recv_cache() { https://developercommunity.visualstudio.com/content/problem/124121/thread-local-variables-fail-to-be-initialized-when.html https://software.intel.com/en-us/forums/intel-c-compiler/topic/684827 */ - static tls::pointer> rc; + static tls::pointer> rc; return *rc.create(); } diff --git a/src/memory/alloc.hpp b/src/memory/alloc.hpp new file mode 100644 index 0000000..fc6a845 --- /dev/null +++ b/src/memory/alloc.hpp @@ -0,0 +1,171 @@ +#pragma once + +#include +#include +#include + +#include "def.h" + +namespace ipc { +namespace memory { + +struct static_alloc { + static constexpr std::size_t remain() { + return (std::numeric_limits::max)(); + } + + static constexpr void clear() {} + + static void* alloc(std::size_t size) { + return size ? std::malloc(size) : nullptr; + } + + static void free(void* p) { + std::free(p); + } + + static void free(void* p, std::size_t /*size*/) { + free(p); + } +}; + +//////////////////////////////////////////////////////////////// +/// Scope allocation -- The destructor will release all allocated blocks. +//////////////////////////////////////////////////////////////// + +template +class scope_alloc { +public: + using alloc_policy = AllocP; + +private: + alloc_policy alloc_; + + struct block_t { + block_t* next_; + }; + block_t* list_ = nullptr; + +public: + scope_alloc() = default; + + scope_alloc(scope_alloc&& rhs) { this->swap(rhs); } + scope_alloc& operator=(scope_alloc&& rhs) { this->swap(rhs); return (*this); } + + ~scope_alloc() { clear(); } + +public: + void swap(scope_alloc& rhs) { + std::swap(this->alloc_, rhs.alloc_); + std::swap(this->list_ , rhs.list_); + } + + std::size_t remain() const { + std::size_t c = 0; + auto curr = list_; + while (curr != nullptr) { + ++c; + curr = curr->next_; + } + return c; + } + + void clear() { + while (list_ != nullptr) { + auto curr = list_; + list_ = list_->next_; + alloc_.free(curr); + } + // now list_ is nullptr + alloc_.clear(); + } + + void* alloc(std::size_t size) { + auto curr = static_cast(alloc_.alloc(sizeof(block_t) + size)); + curr->next_ = list_; + return ((list_ = curr) + 1); + } + + void free(void* /*p*/) {} +}; + +//////////////////////////////////////////////////////////////// +/// Fixed-size blocks allocation +//////////////////////////////////////////////////////////////// + +template > +class fixed_pool { +public: + using alloc_policy = AllocP; + + enum : std::size_t { + block_size = (std::max)(BlockSize, sizeof(void*)) + }; + +private: + alloc_policy alloc_; + std::size_t init_expand_, iter_; + void* cursor_; + + void expand() { + void** p = reinterpret_cast(cursor_ = alloc_.alloc(block_size * iter_)); + for (std::size_t i = 0; i < iter_ - 1; ++i) + p = reinterpret_cast((*p) = reinterpret_cast(p) + block_size); + (*p) = nullptr; + iter_ *= 2; + } + + void init(std::size_t init_expand) { + iter_ = init_expand_ = init_expand; + cursor_ = nullptr; + } + +public: + explicit fixed_pool(std::size_t init_expand = 1) { + init(init_expand); + } + + fixed_pool(fixed_pool&& rhs) { this->swap(rhs); } + fixed_pool& operator=(fixed_pool&& rhs) { this->swap(rhs); return (*this); } + + ~fixed_pool() { clear(); } + +public: + void swap(fixed_pool& rhs) { + std::swap(this->alloc_ , rhs.alloc_); + std::swap(this->init_expand_, rhs.init_expand_); + std::swap(this->iter_ , rhs.iter_); + std::swap(this->cursor_ , rhs.cursor_); + } + + std::size_t remain() const { + std::size_t c = 0; + void* curr = cursor_; + while (curr != nullptr) { + ++c; + curr = *reinterpret_cast(curr); // curr = next + } + return c * block_size; + } + + void clear() { + alloc_.clear(); + init(init_expand_); + } + + void* alloc() { + if (cursor_ == nullptr) expand(); + void* p = cursor_; + cursor_ = *reinterpret_cast(p); + return p; + } + + void free(void* p) { + if (p == nullptr) return; + *reinterpret_cast(p) = cursor_; + cursor_ = p; + } +}; + +} // namespace memory +} // namespace ipc diff --git a/src/memory/resource.hpp b/src/memory/resource.hpp new file mode 100644 index 0000000..43b29bb --- /dev/null +++ b/src/memory/resource.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "tls_pointer.h" + +#include "memory/alloc.hpp" +#include "memory/wrapper.hpp" + +namespace ipc { +namespace memory { + +namespace detail { + +template +void switch_constexpr(std::size_t i, std::index_sequence, F&& f) { + [[maybe_unused]] std::initializer_list expand { + (Comp{}(i, I) && (f(std::integral_constant{}), 0))... + }; +} + +} // namespace detail + +class pool_alloc { +private: + template + static auto& fixed() { + static tls::pointer> fp; + return *fp.create(); + } + + template + static void choose(std::size_t size, F&& f) { + enum : std::size_t { base_size = sizeof(void*) }; + detail::switch_constexpr>(size, std::index_sequence< + base_size , base_size * 2 , + base_size * 4 , base_size * 6 , + base_size * 8 , base_size * 12, + base_size * 16, base_size * 20, + base_size * 24, base_size * 28, + base_size * 32, base_size * 40, + base_size * 48, base_size * 56, + base_size * 64 + >{}, [&f](auto index) { f(fixed()); }); + } + +public: + pool_alloc() = default; + pool_alloc(const pool_alloc&) = default; + pool_alloc& operator=(const pool_alloc&) = default; + pool_alloc(pool_alloc&&) = default; + pool_alloc& operator=(pool_alloc&&) = default; + + static constexpr std::size_t remain() { + return (std::numeric_limits::max)(); + } + + static constexpr void clear() {} + + static void* alloc(std::size_t size) { + void* p; + choose(size, [&p](auto& fp) { p = fp.alloc(); }); + return p; + } + + static void free(void* p, std::size_t size) { + choose(size, [p](auto& fp) { fp.free(p); }); + } +}; + +template +using allocator = allocator_wrapper; + +template +using unordered_map = std::unordered_map< + Key, T//, std::hash, std::equal_to, allocator> +>; + +} // namespace memory +} // namespace ipc diff --git a/src/memory/wrapper.hpp b/src/memory/wrapper.hpp new file mode 100644 index 0000000..f8a07c5 --- /dev/null +++ b/src/memory/wrapper.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include + +namespace ipc { +namespace memory { + +//////////////////////////////////////////////////////////////// +/// The allocator wrapper class +//////////////////////////////////////////////////////////////// + +template +class allocator_wrapper { + + template + friend class allocator_wrapper; + +public: + // type definitions + typedef T value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef std::size_t size_type; + typedef AllocP alloc_policy; + +private: + alloc_policy alloc_; + +public: + allocator_wrapper(void) noexcept = default; + + allocator_wrapper(const allocator_wrapper& rhs) noexcept + : alloc_(rhs.alloc_) + {} + + template + allocator_wrapper(const allocator_wrapper& rhs) noexcept + : alloc_(rhs.alloc_) + {} + + allocator_wrapper(allocator_wrapper&& rhs) noexcept + : alloc_(std::move(rhs.alloc_)) + {} + + template + allocator_wrapper(allocator_wrapper&& rhs) noexcept + : alloc_(std::move(rhs.alloc_)) + {} + + allocator_wrapper(const AllocP& rhs) noexcept + : alloc_(rhs) + {} + + allocator_wrapper(AllocP&& rhs) noexcept + : alloc_(std::move(rhs)) + {} + +public: + // the other type of std_allocator + template + struct rebind { typedef allocator_wrapper other; }; + + constexpr size_type max_size(void) const noexcept { + return (std::numeric_limits::max)() / sizeof(T); + } + +public: + pointer allocate(size_type count) noexcept { + if (count == 0) return nullptr; + if (count > this->max_size()) return nullptr; + return static_cast(alloc_.alloc(count * sizeof(T))); + } + + void deallocate(pointer p, size_type count) { + alloc_.free(p, count * sizeof(T)); + } + + template + static void construct(pointer p, P&&... params) { + ::new (static_cast(p)) T(std::forward

(params)...); + } + + static void destroy(pointer p) { + p->~T(); + } +}; + +template +class allocator_wrapper { +public: + typedef void value_type; + typedef AllocP alloc_policy; +}; + +template +constexpr bool operator==(const allocator_wrapper&, const allocator_wrapper&) noexcept { + return true; +} + +template +constexpr bool operator!=(const allocator_wrapper&, const allocator_wrapper&) noexcept { + return false; +} + +} // namespace memory +} // namespace ipc diff --git a/src/platform/shm_linux.cpp b/src/platform/shm_linux.cpp index fac43d4..13d47e5 100644 --- a/src/platform/shm_linux.cpp +++ b/src/platform/shm_linux.cpp @@ -6,11 +6,11 @@ #include #include -#include #include #include #include "tls_pointer.h" +#include "memory/resource.hpp" namespace { @@ -25,7 +25,7 @@ constexpr void* mem_of(void* mem) { } inline auto& m2h() { - static ipc::tls::pointer> cache; + static ipc::tls::pointer> cache; return *cache.create(); } diff --git a/src/platform/shm_win.cpp b/src/platform/shm_win.cpp index b555842..130102f 100644 --- a/src/platform/shm_win.cpp +++ b/src/platform/shm_win.cpp @@ -7,10 +7,10 @@ #include #include #include -#include #include "def.h" #include "tls_pointer.h" +#include "memory/resource.hpp" namespace { @@ -28,7 +28,7 @@ constexpr auto to_tchar(std::string && str) -> IsSame { } inline auto& m2h() { - static ipc::tls::pointer> cache; + static ipc::tls::pointer> cache; return *cache.create(); } diff --git a/test/test_ipc.cpp b/test/test_ipc.cpp index 77480fc..0447fd5 100644 --- a/test/test_ipc.cpp +++ b/test/test_ipc.cpp @@ -304,10 +304,10 @@ void test_lock_performance() { } void Unit::test_rw_lock() { - test_lock_performance<1, 1>(); - test_lock_performance<4, 4>(); - test_lock_performance<1, 8>(); - test_lock_performance<8, 1>(); +// test_lock_performance<1, 1>(); +// test_lock_performance<4, 4>(); +// test_lock_performance<1, 8>(); +// test_lock_performance<8, 1>(); } void Unit::test_send_recv() {