#pragma once #include #include // ::new #include #include #include #include // std::function #include // std::forward #include #include // assert #include // std::aligned_storage_t #include "def.h" #include "rw_lock.h" #include "tls_pointer.h" #include "concept.h" #include "memory/alloc.h" #include "platform/detail.h" namespace ipc { namespace mem { //////////////////////////////////////////////////////////////// /// The allocator wrapper class for STL //////////////////////////////////////////////////////////////// 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 std::ptrdiff_t difference_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(value_type); } 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(value_type))); } void deallocate(pointer p, size_type count) noexcept { alloc_.free(p, count * sizeof(value_type)); } template static void construct(pointer p, P && ... params) { ::new (static_cast(p)) value_type(std::forward

(params) ...); } static void destroy(pointer p) { p->~value_type(); } }; template class allocator_wrapper { public: // type definitions typedef void value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef std::size_t size_type; typedef std::ptrdiff_t difference_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; } //////////////////////////////////////////////////////////////// /// Thread-safe allocation wrapper //////////////////////////////////////////////////////////////// template class default_alloc_recycler { public: using alloc_policy = AllocP; private: ipc::spin_lock master_lock_; std::vector master_allocs_; IPC_CONCEPT_(has_remain, remain()); IPC_CONCEPT_(has_empty , empty()); public: void swap(default_alloc_recycler& rhs) { IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); master_allocs_.swap(rhs.master_allocs_); } void try_recover(alloc_policy & alc) { IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); if (!master_allocs_.empty()) { alc.swap(master_allocs_.back()); master_allocs_.pop_back(); } } template auto try_replenish(alloc_policy & alc, std::size_t size) -> ipc::require::value && has_remain::value> { if (alc.remain() >= size) return; 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 auto try_replenish(alloc_policy & alc, std::size_t /*size*/) -> ipc::require<(!detail::has_take::value || !has_remain::value) && has_empty::value> { if (!alc.empty()) return; try_recover(alc); } template constexpr auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) const noexcept -> ipc::require<(!detail::has_take::value || !has_remain::value) && !has_empty::value> { // Do Nothing. } void collect(alloc_policy && alc) { IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); master_allocs_.emplace_back(std::move(alc)); } }; template class empty_alloc_recycler { public: using alloc_policy = AllocP; constexpr static void swap(empty_alloc_recycler&) noexcept {} constexpr static void try_recover(alloc_policy&) noexcept {} constexpr static auto try_replenish(alloc_policy&, std::size_t) noexcept {} constexpr static void collect(alloc_policy&&) noexcept {} }; template class RecyclerP = default_alloc_recycler> class async_wrapper { public: using alloc_policy = AllocP; private: RecyclerP recycler_; class alloc_proxy : public AllocP { async_wrapper * w_ = nullptr; public: alloc_proxy(alloc_proxy && rhs) = default; template alloc_proxy(async_wrapper* w, P && ... pars) : AllocP(std::forward

(pars) ...), w_(w) { assert(w_ != nullptr); w_->recycler_.try_recover(*this); } ~alloc_proxy() { w_->recycler_.collect(std::move(*this)); } auto alloc(std::size_t size) { w_->recycler_.try_replenish(*this, size); return AllocP::alloc(size); } }; friend class alloc_proxy; using ref_t = alloc_proxy&; using tls_t = tls::pointer; tls_t tls_; std::function get_alloc_; public: template async_wrapper(P ... pars) { get_alloc_ = [this, pars ...]()->ref_t { return *tls_.create(this, pars ...); }; } void* alloc(std::size_t size) { return get_alloc_().alloc(size); } void free(void* p, std::size_t size) { get_alloc_().free(p, size); } }; //////////////////////////////////////////////////////////////// /// Thread-safe allocation wrapper (with spin_lock) //////////////////////////////////////////////////////////////// template class sync_wrapper { public: using alloc_policy = AllocP; using mutex_type = MutexT; private: mutex_type lock_; alloc_policy alloc_; public: template sync_wrapper(P && ... pars) : alloc_(std::forward

(pars) ...) {} void swap(sync_wrapper& rhs) { IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); alloc_.swap(rhs.alloc_); } void* alloc(std::size_t size) { IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); return alloc_.alloc(size); } void free(void* p, std::size_t size) { IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); alloc_.free(p, size); } }; //////////////////////////////////////////////////////////////// /// Static allocation wrapper //////////////////////////////////////////////////////////////// template class static_wrapper { public: using alloc_policy = AllocP; static alloc_policy& instance() { static alloc_policy alloc; return alloc; } static void swap(static_wrapper&) {} static void* alloc(std::size_t size) { return instance().alloc(size); } static void free(void* p, std::size_t size) { instance().free(p, size); } }; //////////////////////////////////////////////////////////////// /// Variable memory allocation wrapper //////////////////////////////////////////////////////////////// template struct default_mapping_policy { enum : std::size_t { base_size = BaseSize, iter_size = IterSize, classes_size = 64 }; template IPC_CONSTEXPR_ static void foreach(F f, P ... params) { for (std::size_t i = 0; i < classes_size; ++i) f(i, params...); } IPC_CONSTEXPR_ static std::size_t block_size(std::size_t id) noexcept { return (id < classes_size) ? (base_size + (id + 1) * iter_size) : 0; } template IPC_CONSTEXPR_ static auto classify(F f, D d, std::size_t size, P ... params) { std::size_t id = (size - base_size - 1) / iter_size; return (id < classes_size) ? f(id, params..., size) : d(params..., size); } }; template > class static_variable_wrapper { private: static FixedAlloc& instance(std::size_t id) { static struct initiator { std::aligned_storage_t arr_[MappingP::classes_size]; initiator() { MappingP::foreach([](std::size_t id, initiator* t) { ::new (&(t->arr_[id])) FixedAlloc(MappingP::block_size(id)); }, this); } } init__; return reinterpret_cast(init__.arr_[id]); } public: static void swap(static_variable_wrapper&) {} static void* alloc(std::size_t size) { return MappingP::classify([](std::size_t id, std::size_t size) { return instance(id).alloc(size); }, DefaultAlloc::alloc, size); } static void free(void* p, std::size_t size) { MappingP::classify([](std::size_t id, void* p, std::size_t size) { instance(id).free(p, size); }, static_cast(DefaultAlloc::free), size, p); } }; } // namespace mem } // namespace ipc