diff --git a/build/ipc/ipc.pro b/build/ipc/ipc.pro index 534e272..6610884 100644 --- a/build/ipc/ipc.pro +++ b/build/ipc/ipc.pro @@ -33,7 +33,9 @@ HEADERS += \ ../../src/policy.h \ ../../src/queue.h \ ../../src/log.h \ - ../../src/id_pool.h + ../../src/id_pool.h \ + ../../src/pimpl.h \ + ../../src/concept.h SOURCES += \ ../../src/shm.cpp \ diff --git a/src/concept.h b/src/concept.h index da29f09..649c25b 100644 --- a/src/concept.h +++ b/src/concept.h @@ -6,7 +6,24 @@ namespace ipc { // concept helpers -template -using Requires = std::enable_if_t; +template +using require = std::enable_if_t; + +#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; \ +} } // namespace ipc diff --git a/src/memory/alloc.h b/src/memory/alloc.h index a2a53fc..f5df02b 100644 --- a/src/memory/alloc.h +++ b/src/memory/alloc.h @@ -3,9 +3,12 @@ #include #include #include +#include +#include #include "def.h" #include "rw_lock.h" +#include "concept.h" #include "platform/detail.h" @@ -40,12 +43,14 @@ constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { return ((size - 1) & ~(alignment - 1)) + alignment; } +IPC_CONCEPT_(has_take, take(Type{})); + class scope_alloc_base { protected: struct block_t { block_t * next_; std::size_t size_; - } * list_ = nullptr; + } * head_ = nullptr, * tail_ = nullptr; enum : std::size_t { aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t)) @@ -53,7 +58,23 @@ protected: public: void swap(scope_alloc_base & rhs) { - std::swap(this->list_, rhs.list_); + std::swap(head_, rhs.head_); + std::swap(tail_, rhs.tail_); + } + + bool empty() const noexcept { + return head_ == nullptr; + } + + void take(scope_alloc_base && rhs) { + if (rhs.empty()) return; + if (empty()) swap(rhs); + else { + std::swap(tail_->next_, rhs.head_); + // rhs.head_ should be nullptr here + tail_ = rhs.tail_; + rhs.tail_ = nullptr; + } } void free(void* /*p*/) {} @@ -72,19 +93,19 @@ private: alloc_policy alloc_; void free_all() { - while (list_ != nullptr) { - auto curr = list_; - list_ = list_->next_; + while (!empty()) { + auto curr = head_; + head_ = head_->next_; alloc_.free(curr, curr->size_); } - // now list_ is nullptr + // now head_ is 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(scope_alloc&& rhs) { swap(rhs); } + scope_alloc& operator=(scope_alloc&& rhs) { swap(rhs); return (*this); } ~scope_alloc() { free_all(); } @@ -98,16 +119,32 @@ public: base_t::swap(rhs); } + template + 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) -> ipc::require::value> { + base_t::take(std::move(rhs)); + } + void clear() { free_all(); + tail_ = nullptr; alloc_.~alloc_policy(); } void* alloc(std::size_t size) { auto curr = static_cast(alloc_.alloc(size += aligned_block_size)); - curr->next_ = list_; + curr->next_ = head_; curr->size_ = size; - return (reinterpret_cast(list_ = curr) + aligned_block_size); + head_ = curr; + if (tail_ == nullptr) { + tail_ = curr; + } + return (reinterpret_cast(curr) + aligned_block_size); } }; @@ -137,8 +174,30 @@ protected: public: void swap(fixed_alloc_base& rhs) { - std::swap(this->init_expand_, rhs.init_expand_); - std::swap(this->cursor_ , rhs.cursor_); + std::swap(init_expand_, rhs.init_expand_); + std::swap(cursor_ , rhs.cursor_); + } + + bool empty() const noexcept { + return cursor_ == nullptr; + } + + void take(fixed_alloc_base && rhs) { + init_expand_ = (ipc::detail::max)(init_expand_, rhs.init_expand_); + if (rhs.empty()) return; + auto curr = cursor_; + if (curr != nullptr) while (1) { + auto next_cur = next(curr); + if (next_cur == nullptr) { + std::swap(next(curr), rhs.cursor_); + return; + } + // next_cur != nullptr + else curr = next_cur; + } + // curr == nullptr, means cursor_ == nullptr + else std::swap(cursor_, rhs.cursor_); + // rhs.cursor_ must be nullptr } void free(void* p) { @@ -191,24 +250,23 @@ private: alloc_policy alloc_; void* try_expand() { - if (this->cursor_ != nullptr) { - return this->cursor_; + if (empty()) { + auto size = ExpandP::template next(init_expand_); + auto p = node_p(cursor_ = alloc_.alloc(size)); + for (std::size_t i = 0; i < (size / block_size) - 1; ++i) + p = node_p((*p) = reinterpret_cast(p) + block_size); + (*p) = nullptr; } - auto size = ExpandP::template next(this->init_expand_); - auto p = this->node_p(this->cursor_ = alloc_.alloc(size)); - for (std::size_t i = 0; i < (size / block_size) - 1; ++i) - p = this->node_p((*p) = reinterpret_cast(p) + block_size); - (*p) = nullptr; - return this->cursor_; + return cursor_; } public: explicit fixed_alloc(std::size_t init_expand = 1) { - this->init(init_expand); + init(init_expand); } - fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { this->swap(rhs); } - fixed_alloc& operator=(fixed_alloc&& rhs) { this->swap(rhs); return (*this); } + fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { swap(rhs); } + fixed_alloc& operator=(fixed_alloc&& rhs) { swap(rhs); return (*this); } template void set_allocator(A && alc) { @@ -220,15 +278,26 @@ public: base_t::swap(rhs); } + template + auto take(fixed_alloc && rhs) -> ipc::require::value> { + base_t::take(std::move(rhs)); + alloc_.take(std::move(rhs.alloc_)); + } + + template + auto take(fixed_alloc && rhs) -> ipc::require::value> { + base_t::take(std::move(rhs)); + } + void clear() { - ExpandP::prev(this->init_expand_); - this->cursor_ = nullptr; + ExpandP::prev(init_expand_); + cursor_ = nullptr; alloc_.~alloc_policy(); } void* alloc() { void* p = try_expand(); - this->cursor_ = this->next(p); + cursor_ = next(p); return p; } @@ -246,22 +315,43 @@ namespace detail { class variable_alloc_base { protected: struct head_t { - head_t * next_; - size_t size_; - size_t free_; + std::size_t free_; } * head_ = nullptr; + std::map> reserves_; + enum : std::size_t { aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t)) }; static byte_t * buffer(head_t* p) { - return reinterpret_cast(p) + aligned_head_size; + return reinterpret_cast(p) + aligned_head_size + p->free_; + } + + std::size_t remain() const noexcept { + return (head_ == nullptr) ? 0 : head_->free_; } public: void swap(variable_alloc_base& rhs) { - std::swap(this->head_, rhs.head_); + std::swap(head_, rhs.head_); + } + + bool empty() const noexcept { + return remain() == 0; + } + + void take(variable_alloc_base && rhs) { + if (rhs.remain() > remain()) { + if (!empty()) { + reserves_.emplace(head_->free_, head_); + } + head_ = rhs.head_; + } + else if (!rhs.empty()) { + reserves_.emplace(rhs.head_->free_, rhs.head_); + } + rhs.head_ = nullptr; } void free(void* /*p*/) {} @@ -270,7 +360,7 @@ public: } // namespace detail -template +template > class variable_alloc : public detail::variable_alloc_base { public: using base_t = detail::variable_alloc_base; @@ -280,41 +370,11 @@ public: private: alloc_policy alloc_; - head_t* alloc_head(std::size_t size) { - size = (ipc::detail::max)(ChunkSize, ipc::detail::max(size, aligned_head_size)); - head_t* p = static_cast(alloc_.alloc(size)); - p->free_ = (p->size_ = size) - aligned_head_size; - return p; - } - - void* alloc_new_chunk(std::size_t size) { - head_t* p = alloc_head(aligned_head_size + size); - if (p == nullptr) return nullptr; - if (size > (ChunkSize - aligned_head_size) && head_ != nullptr) { - p->next_ = head_->next_; - head_->next_ = p; - return base_t::buffer(p) + (p->free_ -= size); - } - p->next_ = head_; - return base_t::buffer(head_ = p) + (p->free_ -= size); - } - - void free_all() { - while (head_ != nullptr) { - head_t* curr = head_; - head_ = head_->next_; - alloc_.free(curr, curr->size_); - } - // now head_ is nullptr - } - public: variable_alloc() = default; - variable_alloc(variable_alloc&& rhs) { this->swap(rhs); } - variable_alloc& operator=(variable_alloc&& rhs) { this->swap(rhs); return (*this); } - - ~variable_alloc() { free_all(); } + variable_alloc(variable_alloc&& rhs) { swap(rhs); } + variable_alloc& operator=(variable_alloc&& rhs) { swap(rhs); return (*this); } template void set_allocator(A && alc) { @@ -326,16 +386,44 @@ public: base_t::swap(rhs); } + template + auto take(variable_alloc && rhs) -> ipc::require::value> { + base_t::take(std::move(rhs)); + alloc_.take(std::move(rhs.alloc_)); + } + + template + auto take(variable_alloc && rhs) -> ipc::require::value> { + base_t::take(std::move(rhs)); + } + void clear() { - free_all(); alloc_.~alloc_policy(); } - void* alloc(size_t size) { - if ((head_ == nullptr) || head_->free_ < size) { - return alloc_new_chunk(size); + void* alloc(std::size_t size) { + if (size >= (ChunkSize - aligned_head_size)) { + return alloc_.alloc(size); } - return base_t::buffer(head_) + (head_->free_ -= size); + if (remain() < size) { + auto it = reserves_.begin(); + if ((it == reserves_.end()) || (it->first < size)) { + head_ = static_cast(alloc_.alloc(ChunkSize)); + head_->free_ = ChunkSize - aligned_head_size - size; + } + else { + auto temp = it->second; + temp->free_ -= size; + reserves_.erase(it); + if (remain() < temp->free_) { + head_ = temp; + } + else return base_t::buffer(temp); + } + } + // size shouldn't be 0 here, otherwise behavior is undefined + else head_->free_ -= size; + return base_t::buffer(head_); } }; diff --git a/src/memory/resource.h b/src/memory/resource.h index f20a413..7e901db 100644 --- a/src/memory/resource.h +++ b/src/memory/resource.h @@ -18,7 +18,7 @@ namespace ipc { namespace mem { template -using static_sync_fixed = static_wrapper>>; +using static_sync_fixed = static_wrapper>>; namespace detail { @@ -34,25 +34,15 @@ struct chunk_mapping_policy { } }; -template -struct chunk_alloc_recoverer : default_alloc_recoverer { - void collect(alloc_policy && alc) { - alc.clear(); // recycle memory to the central heap (static_chunk_alloc) - default_alloc_recoverer::collect(std::move(alc)); - } -}; - } // namespace detail using static_chunk_alloc = variable_wrapper; using chunk_variable_alloc = variable_alloc; template -using static_async_fixed = - static_wrapper/*, detail::chunk_alloc_recoverer*/>>; +using static_async_fixed = static_wrapper>>; using async_pool_alloc = variable_wrapper; -//using async_pool_alloc = static_wrapper>; template using allocator = allocator_wrapper; diff --git a/src/memory/wrapper.h b/src/memory/wrapper.h index 2b7856a..2f6a7fe 100644 --- a/src/memory/wrapper.h +++ b/src/memory/wrapper.h @@ -13,6 +13,7 @@ #include "def.h" #include "rw_lock.h" #include "tls_pointer.h" +#include "concept.h" #include "memory/alloc.h" #include "platform/detail.h" @@ -157,6 +158,19 @@ public: } } + template + auto try_replenish(alloc_policy & alc) -> ipc::require::value> { + IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); + if (!master_allocs_.empty()) { + alc.take(std::move(master_allocs_.back())); + master_allocs_.pop_back(); + } + } + + template + constexpr static auto try_replenish(alloc_policy & /*alc*/) noexcept + -> ipc::require::value> {} + void collect(alloc_policy && alc) { IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); master_allocs_.emplace_back(std::move(alc)); @@ -175,6 +189,8 @@ private: class alloc_proxy : public AllocP { async_wrapper * w_ = nullptr; + IPC_CONCEPT_(has_empty, empty()); + public: alloc_proxy(alloc_proxy && rhs) : AllocP(std::move(rhs)) @@ -190,6 +206,20 @@ private: if (w_ == nullptr) return; w_->recoverer_.collect(std::move(*this)); } + + template + auto alloc(std::size_t size) -> ipc::require::value, void*> { + auto p = AllocP::alloc(size); + if (AllocP::empty() && (w_ != nullptr)) { + w_->recoverer_.try_replenish(*this); + } + return p; + } + + template + auto alloc(std::size_t size) -> ipc::require::value, void*> { + return AllocP::alloc(size); + } }; friend class alloc_proxy; diff --git a/src/pimpl.h b/src/pimpl.h index 6fdc0a5..a317239 100644 --- a/src/pimpl.h +++ b/src/pimpl.h @@ -11,10 +11,10 @@ namespace ipc { // pimpl small object optimization helpers template -using IsImplComfortable = Requires<(sizeof(T) <= sizeof(T*)), R>; +using IsImplComfortable = ipc::require<(sizeof(T) <= sizeof(T*)), R>; template -using IsImplUncomfortable = Requires<(sizeof(T) > sizeof(T*)), R>; +using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>; template constexpr auto make_impl(P&&... params) -> IsImplComfortable { diff --git a/src/platform/to_tchar.h b/src/platform/to_tchar.h index 57afed1..8f913b5 100644 --- a/src/platform/to_tchar.h +++ b/src/platform/to_tchar.h @@ -27,7 +27,7 @@ template struct is_same_char : std::is_same {}; template -using IsSameChar = ipc::Requires::value, R>; +using IsSameChar = ipc::require::value, R>; //////////////////////////////////////////////////////////////// /// to_tchar implementation diff --git a/test/test_mem.cpp b/test/test_mem.cpp index 88a81a9..44d060f 100644 --- a/test/test_mem.cpp +++ b/test/test_mem.cpp @@ -172,9 +172,9 @@ struct test_performance { }; void Unit::test_static() { - //test_performance::start(); - //test_performance::start(); - //test_performance::start(); + test_performance::start(); + test_performance::start(); + test_performance::start(); } void Unit::test_pool() {