Use $new instead of alloc

This commit is contained in:
mutouyun 2025-02-04 10:33:13 +08:00
parent db109165f3
commit 831225f763
13 changed files with 48 additions and 924 deletions

View File

@ -26,7 +26,7 @@ namespace mem {
class LIBIPC_EXPORT block_collector {
public:
virtual ~block_collector() noexcept = default;
virtual void recycle(void *p) noexcept = 0;
virtual void recycle(void */*p*/) noexcept {}
};
#if defined(LIBIPC_CPP_17)
@ -182,8 +182,7 @@ T *$new(A &&... args) noexcept {
/// \brief Destroys object previously allocated by the `$new` and releases obtained memory area.
/// \note This function is thread-safe. If the pointer type passed in is different from `$new`,
/// additional performance penalties may be incurred.
template <typename T>
void $delete(T *p) noexcept {
inline void $delete(void *p) noexcept {
if (p == nullptr) return;
auto *b = reinterpret_cast<byte *>(p) - regular_head_size;
auto *r = reinterpret_cast<recycle_t *>(b);

View File

@ -1,103 +0,0 @@
#pragma once
#include <new>
#include <utility>
#include "libipc/imp/export.h"
#include "libipc/def.h"
namespace ipc {
namespace mem {
class LIBIPC_EXPORT pool_alloc {
public:
static void* alloc(std::size_t size) noexcept;
static void free (void* p, std::size_t size) noexcept;
};
////////////////////////////////////////////////////////////////
/// construct/destruct an object
////////////////////////////////////////////////////////////////
namespace detail {
template <typename T>
struct impl {
template <typename... P>
static T* construct(T* p, P&&... params) {
::new (p) T(std::forward<P>(params)...);
return p;
}
static void destruct(T* p) {
reinterpret_cast<T*>(p)->~T();
}
};
template <typename T, size_t N>
struct impl<T[N]> {
using type = T[N];
template <typename... P>
static type* construct(type* p, P&&... params) {
for (size_t i = 0; i < N; ++i) {
impl<T>::construct(&((*p)[i]), std::forward<P>(params)...);
}
return p;
}
static void destruct(type* p) {
for (size_t i = 0; i < N; ++i) {
impl<T>::destruct(&((*p)[i]));
}
}
};
} // namespace detail
template <typename T, typename... P>
T* construct(T* p, P&&... params) {
return detail::impl<T>::construct(p, std::forward<P>(params)...);
}
template <typename T, typename... P>
T* construct(void* p, P&&... params) {
return construct(static_cast<T*>(p), std::forward<P>(params)...);
}
template <typename T>
void destruct(T* p) {
return detail::impl<T>::destruct(p);
}
template <typename T>
void destruct(void* p) {
destruct(static_cast<T*>(p));
}
////////////////////////////////////////////////////////////////
/// general alloc/free
////////////////////////////////////////////////////////////////
inline void* alloc(std::size_t size) {
return pool_alloc::alloc(size);
}
template <typename T, typename... P>
T* alloc(P&&... params) {
return construct<T>(pool_alloc::alloc(sizeof(T)), std::forward<P>(params)...);
}
inline void free(void* p, std::size_t size) {
pool_alloc::free(p, size);
}
template <typename T>
void free(T* p) {
if (p == nullptr) return;
destruct(p);
pool_alloc::free(p, sizeof(T));
}
} // namespace mem
} // namespace ipc

View File

@ -14,7 +14,6 @@
#include "libipc/ipc.h"
#include "libipc/def.h"
#include "libipc/shm.h"
#include "libipc/pool_alloc.h"
#include "libipc/queue.h"
#include "libipc/policy.h"
#include "libipc/rw_lock.h"
@ -26,6 +25,7 @@
#include "libipc/utility/utility.h"
#include "libipc/mem/resource.h"
#include "libipc/mem/new.h"
#include "libipc/platform/detail.h"
#include "libipc/circ/elem_array.h"
@ -64,10 +64,15 @@ struct msg_t : msg_t<0, AlignSize> {
};
template <typename T>
ipc::buff_t make_cache(T& data, std::size_t size) {
auto ptr = ipc::mem::alloc(size);
ipc::buff_t make_cache(T &data, std::size_t size) {
auto *ptr = ipc::mem::$new<void>(size);
std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size));
return { ptr, size, ipc::mem::free };
return {
ptr, size,
[](void *p, std::size_t) noexcept {
ipc::mem::$delete(p);
}
};
}
acc_t *cc_acc(std::string const &pref) {
@ -259,8 +264,8 @@ chunk_info_t *chunk_storage_info(conn_info_head *inf, std::size_t chunk_size) {
guard.unlock();
LIBIPC_UNUSED std::lock_guard<ipc::rw_lock> guard {lock};
it = storages.emplace(chunk_size, chunk_handle_ptr_t{
ipc::mem::alloc<chunk_handle_t>(), [](chunk_handle_t *p) {
ipc::mem::destruct(p);
ipc::mem::$new<chunk_handle_t>(), [](chunk_handle_t *p) {
ipc::mem::$delete(p);
}}).first;
}
}
@ -447,7 +452,7 @@ constexpr static queue_t* queue_of(ipc::handle_t h) noexcept {
static bool connect(ipc::handle_t * ph, ipc::prefix pref, char const * name, bool start_to_recv) {
assert(ph != nullptr);
if (*ph == nullptr) {
*ph = ipc::mem::alloc<conn_info_t>(pref.str, name);
*ph = ipc::mem::$new<conn_info_t>(pref.str, name);
}
return reconnect(ph, start_to_recv);
}
@ -490,7 +495,7 @@ static bool reconnect(ipc::handle_t * ph, bool start_to_recv) {
}
static void destroy(ipc::handle_t h) noexcept {
ipc::mem::free(info_of(h));
ipc::mem::$delete(info_of(h));
}
static std::size_t recv_count(ipc::handle_t h) noexcept {
@ -654,20 +659,20 @@ static ipc::buff_t recv(ipc::handle_t h, std::uint64_t tm) {
conn_info_t * inf;
ipc::circ::cc_t curr_conns;
ipc::circ::cc_t conn_id;
} *r_info = ipc::mem::alloc<recycle_t>(recycle_t{
} *r_info = ipc::mem::$new<recycle_t>(recycle_t{
buf_id,
inf,
que->elems()->connections(std::memory_order_relaxed),
que->connected_id()
});
if (r_info == nullptr) {
ipc::log("fail: ipc::mem::alloc<recycle_t>.\n");
ipc::log("fail: ipc::mem::$new<recycle_t>.\n");
return ipc::buff_t{buf, msg_size}; // no recycle
} else {
return ipc::buff_t{buf, msg_size, [](void* p_info, std::size_t size) {
auto r_info = static_cast<recycle_t *>(p_info);
LIBIPC_UNUSED auto finally = ipc::guard([r_info] {
ipc::mem::free(r_info);
ipc::mem::$delete(r_info);
});
recycle_storage<flag_t>(r_info->storage_id,
r_info->inf,

View File

@ -5,22 +5,10 @@
#include <string>
#include "libipc/def.h"
#include "libipc/memory/alloc.h"
#include "libipc/imp/fmt.h"
#include "libipc/mem/polymorphic_allocator.h"
namespace ipc {
namespace mem {
//using async_pool_alloc = static_wrapper<variable_wrapper<async_wrapper<
// detail::fixed_alloc<
// variable_alloc <sizeof(void*) * 1024 * 256>,
// fixed_expand_policy<sizeof(void*) * 1024, sizeof(void*) * 1024 * 256>
// >,
// default_recycler >>>;
using async_pool_alloc = ipc::mem::static_alloc;
} // namespace mem
template <typename T>
struct hash : public std::hash<T> {};

View File

@ -1,423 +0,0 @@
#pragma once
#include <algorithm>
#include <utility>
#include <iterator>
#include <limits> // std::numeric_limits
#include <cstdlib>
#include <cassert> // assert
#include "libipc/def.h"
#include "libipc/rw_lock.h"
#include "libipc/utility/concept.h"
#include "libipc/platform/detail.h"
namespace ipc {
namespace mem {
class static_alloc {
public:
static void swap(static_alloc&) noexcept {}
static void* alloc(std::size_t size) noexcept {
return size ? std::malloc(size) : nullptr;
}
static void free(void* p) noexcept {
std::free(p);
}
static void free(void* p, std::size_t /*size*/) noexcept {
free(p);
}
};
////////////////////////////////////////////////////////////////
/// Scope allocation -- The destructor will release all allocated blocks.
////////////////////////////////////////////////////////////////
namespace detail {
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<Type>())));
class scope_alloc_base {
protected:
struct block_t {
std::size_t size_;
block_t * next_;
} * head_ = nullptr, * tail_ = nullptr;
enum : std::size_t {
aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t))
};
public:
void swap(scope_alloc_base & rhs) {
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*/) {}
void free(void* /*p*/, std::size_t) {}
};
} // namespace detail
template <typename AllocP = static_alloc>
class scope_alloc : public detail::scope_alloc_base {
public:
using base_t = detail::scope_alloc_base;
using alloc_policy = AllocP;
private:
alloc_policy alloc_;
void free_all() {
while (!empty()) {
auto curr = head_;
head_ = head_->next_;
alloc_.free(curr, curr->size_);
}
// now head_ is nullptr
}
public:
scope_alloc() = default;
scope_alloc(scope_alloc && rhs) { swap(rhs); }
scope_alloc& operator=(scope_alloc rhs) { swap(rhs); return (*this); }
~scope_alloc() { free_all(); }
void swap(scope_alloc& rhs) {
alloc_.swap(rhs.alloc_);
base_t::swap(rhs);
}
template <typename A = AllocP>
auto take(scope_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
base_t::take(std::move(rhs));
alloc_.take(std::move(rhs.alloc_));
}
template <typename A = AllocP>
auto take(scope_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
base_t::take(std::move(rhs));
}
void* alloc(std::size_t size) {
std::size_t real_size = aligned_block_size + size;
auto curr = static_cast<block_t*>(alloc_.alloc(real_size));
curr->size_ = real_size;
curr->next_ = head_;
head_ = curr;
if (tail_ == nullptr) {
tail_ = curr;
}
return (reinterpret_cast<byte_t*>(curr) + aligned_block_size);
}
};
////////////////////////////////////////////////////////////////
/// Fixed-size blocks allocation
////////////////////////////////////////////////////////////////
namespace detail {
class fixed_alloc_base {
protected:
std::size_t block_size_;
std::size_t init_expand_;
void * cursor_;
void init(std::size_t block_size, std::size_t init_expand) {
block_size_ = block_size;
init_expand_ = init_expand;
cursor_ = nullptr;
}
static void** node_p(void* node) {
return reinterpret_cast<void**>(node);
}
static auto& next(void* node) {
return *node_p(node);
}
public:
bool operator<(fixed_alloc_base const & right) const {
return init_expand_ < right.init_expand_;
}
void set_block_size(std::size_t block_size) {
block_size_ = block_size;
}
void swap(fixed_alloc_base& rhs) {
std::swap(block_size_ , rhs.block_size_);
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) {
assert(block_size_ == rhs.block_size_);
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) {
if (p == nullptr) return;
next(p) = cursor_;
cursor_ = p;
}
void free(void* p, std::size_t) {
free(p);
}
};
template <typename AllocP, typename ExpandP>
class fixed_alloc : public detail::fixed_alloc_base {
public:
using base_t = detail::fixed_alloc_base;
using alloc_policy = AllocP;
private:
alloc_policy alloc_;
void* try_expand() {
if (empty()) {
auto size = ExpandP::next(block_size_, 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<byte_t*>(p) + block_size_);
(*p) = nullptr;
}
return cursor_;
}
public:
explicit fixed_alloc(std::size_t block_size, std::size_t init_expand = 1) {
init(block_size, init_expand);
}
fixed_alloc(fixed_alloc && rhs) {
init(0, 0);
swap(rhs);
}
fixed_alloc& operator=(fixed_alloc rhs) {
swap(rhs);
return (*this);
}
void swap(fixed_alloc& rhs) {
alloc_.swap(rhs.alloc_);
base_t::swap(rhs);
}
template <typename A = AllocP>
auto take(fixed_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
base_t::take(std::move(rhs));
alloc_.take(std::move(rhs.alloc_));
}
void* alloc() {
void* p = try_expand();
cursor_ = next(p);
return p;
}
void* alloc(std::size_t) {
return alloc();
}
};
} // namespace detail
template <std::size_t BaseSize = sizeof(void*) * 1024,
std::size_t LimitSize = (std::numeric_limits<std::uint32_t>::max)()>
struct fixed_expand_policy {
enum : std::size_t {
base_size = BaseSize,
limit_size = LimitSize
};
constexpr static std::size_t prev(std::size_t e) noexcept {
return ((e / 2) == 0) ? 1 : (e / 2);
}
constexpr static std::size_t next(std::size_t e) noexcept {
return e * 2;
}
static std::size_t next(std::size_t block_size, std::size_t & e) {
auto n = ipc::detail::max<std::size_t>(block_size, base_size) * e;
e = ipc::detail::min<std::size_t>(limit_size, next(e));
return n;
}
};
template <std::size_t BlockSize,
typename AllocP = scope_alloc<>,
typename ExpandP = fixed_expand_policy<>>
class fixed_alloc : public detail::fixed_alloc<AllocP, ExpandP> {
public:
using base_t = detail::fixed_alloc<AllocP, ExpandP>;
enum : std::size_t {
block_size = ipc::detail::max<std::size_t>(BlockSize, sizeof(void*))
};
public:
explicit fixed_alloc(std::size_t init_expand)
: base_t(block_size, init_expand) {
}
fixed_alloc() : fixed_alloc(1) {}
fixed_alloc(fixed_alloc && rhs)
: base_t(std::move(rhs)) {
}
fixed_alloc& operator=(fixed_alloc rhs) {
swap(rhs);
return (*this);
}
void swap(fixed_alloc& rhs) {
base_t::swap(rhs);
}
};
////////////////////////////////////////////////////////////////
/// Variable-size blocks allocation (without alignment)
////////////////////////////////////////////////////////////////
namespace detail {
class variable_alloc_base {
protected:
byte_t * head_ = nullptr, * tail_ = nullptr;
public:
void swap(variable_alloc_base & rhs) {
std::swap(head_, rhs.head_);
std::swap(tail_, rhs.tail_);
}
std::size_t remain() const noexcept {
return static_cast<std::size_t>(tail_ - head_);
}
bool empty() const noexcept {
return remain() == 0;
}
void take(variable_alloc_base && rhs) {
if (remain() < rhs.remain()) {
// replace this by rhs
head_ = rhs.head_;
tail_ = rhs.tail_;
}
// discard rhs
rhs.head_ = rhs.tail_ = nullptr;
}
void free(void* /*p*/) {}
void free(void* /*p*/, std::size_t) {}
};
} // namespace detail
template <std::size_t ChunkSize = (sizeof(void*) * 1024), typename AllocP = scope_alloc<>>
class variable_alloc : public detail::variable_alloc_base {
public:
using base_t = detail::variable_alloc_base;
using alloc_policy = AllocP;
enum : std::size_t {
aligned_chunk_size = detail::aligned(ChunkSize, alignof(std::max_align_t))
};
private:
alloc_policy alloc_;
public:
variable_alloc() = default;
variable_alloc(variable_alloc && rhs) { swap(rhs); }
variable_alloc& operator=(variable_alloc rhs) { swap(rhs); return (*this); }
void swap(variable_alloc& rhs) {
alloc_.swap(rhs.alloc_);
base_t::swap(rhs);
}
template <typename A = AllocP>
auto take(variable_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
base_t::take(std::move(rhs));
alloc_.take(std::move(rhs.alloc_));
}
void* alloc(std::size_t size) {
/*
* byte alignment is always alignof(std::max_align_t).
*/
size = detail::aligned(size, alignof(std::max_align_t));
void* ptr;
// size would never be 0 here
if (remain() < size) {
std::size_t chunk_size = ipc::detail::max<std::size_t>(aligned_chunk_size, size);
ptr = alloc_.alloc(chunk_size);
tail_ = static_cast<byte_t*>(ptr) + chunk_size;
head_ = tail_ - (chunk_size - size);
}
else {
ptr = head_;
head_ += size;
}
return ptr;
}
};
} // namespace mem
} // namespace ipc

View File

@ -1,327 +0,0 @@
#pragma once
#include <tuple>
#include <thread>
#include <deque> // std::deque
#include <functional> // std::function
#include <utility> // std::forward
#include <cstddef>
#include <cassert> // assert
#include <type_traits> // std::aligned_storage_t
#include "libipc/def.h"
#include "libipc/rw_lock.h"
#include "libipc/pool_alloc.h"
#include "libipc/utility/concept.h"
#include "libipc/memory/alloc.h"
#include "libipc/platform/detail.h"
namespace ipc {
namespace mem {
////////////////////////////////////////////////////////////////
/// Thread-safe allocation wrapper
////////////////////////////////////////////////////////////////
namespace detail {
IPC_CONCEPT_(is_comparable, operator<(std::declval<Type>()));
} // namespace detail
template <typename AllocP, bool = detail::is_comparable<AllocP>::value>
class limited_recycler;
template <typename AllocP>
class limited_recycler<AllocP, true> {
public:
using alloc_policy = AllocP;
protected:
std::deque<alloc_policy> master_allocs_;
ipc::spin_lock master_lock_;
template <typename F>
void take_first_do(F && pred) {
auto it = master_allocs_.begin();
pred(const_cast<alloc_policy&>(*it));
master_allocs_.erase(it);
}
public:
void try_recover(alloc_policy & alc) {
LIBIPC_UNUSED auto guard = ipc::detail::unique_lock(master_lock_);
if (master_allocs_.empty()) return;
take_first_do([&alc](alloc_policy & first) { alc.swap(first); });
}
void collect(alloc_policy && alc) {
LIBIPC_UNUSED auto guard = ipc::detail::unique_lock(master_lock_);
if (master_allocs_.size() >= 32) {
take_first_do([](alloc_policy &) {}); // erase first
}
master_allocs_.emplace_back(std::move(alc));
}
IPC_CONSTEXPR_ auto try_replenish(alloc_policy&, std::size_t) noexcept {}
};
template <typename AllocP>
class default_recycler : public limited_recycler<AllocP> {
IPC_CONCEPT_(has_remain, remain());
IPC_CONCEPT_(has_empty , empty());
template <typename A>
void try_fill(A & alc) {
LIBIPC_UNUSED auto guard = ipc::detail::unique_lock(this->master_lock_);
if (this->master_allocs_.empty()) return;
this->take_first_do([&alc](alloc_policy & first) { alc.take(std::move(first)); });
}
public:
using alloc_policy = typename limited_recycler<AllocP>::alloc_policy;
template <typename A = AllocP>
auto try_replenish(alloc_policy & alc, std::size_t size)
-> ipc::require<detail::has_take<A>::value && has_remain<A>::value> {
if (alc.remain() >= size) return;
this->try_fill(alc);
}
template <typename A = AllocP>
auto try_replenish(alloc_policy & alc, std::size_t /*size*/)
-> ipc::require<detail::has_take<A>::value && !has_remain<A>::value && has_empty<A>::value> {
if (!alc.empty()) return;
this->try_fill(alc);
}
template <typename A = AllocP>
auto try_replenish(alloc_policy & alc, std::size_t /*size*/)
-> ipc::require<!detail::has_take<A>::value && has_empty<A>::value> {
if (!alc.empty()) return;
this->try_recover(alc);
}
template <typename A = AllocP>
IPC_CONSTEXPR_ auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) noexcept
-> ipc::require<(!detail::has_take<A>::value || !has_remain<A>::value) && !has_empty<A>::value> {
// Do Nothing.
}
};
template <typename AllocP>
class empty_recycler {
public:
using alloc_policy = AllocP;
IPC_CONSTEXPR_ void try_recover(alloc_policy&) noexcept {}
IPC_CONSTEXPR_ auto try_replenish(alloc_policy&, std::size_t) noexcept {}
IPC_CONSTEXPR_ void collect(alloc_policy&&) noexcept {}
};
template <typename AllocP,
template <typename> class RecyclerP = default_recycler>
class async_wrapper {
public:
using alloc_policy = AllocP;
private:
RecyclerP<alloc_policy> recycler_;
class alloc_proxy : public AllocP {
async_wrapper * w_ = nullptr;
public:
alloc_proxy(alloc_proxy && rhs) = default;
template <typename ... P>
alloc_proxy(async_wrapper* w, P && ... pars)
: AllocP(std::forward<P>(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&;
std::function<ref_t()> get_alloc_;
public:
template <typename ... P>
async_wrapper(P ... pars) {
get_alloc_ = [this, pars ...]()->ref_t {
thread_local alloc_proxy tls(pars ...);
return tls;
};
}
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 <typename AllocP, typename MutexT = ipc::spin_lock>
class sync_wrapper {
public:
using alloc_policy = AllocP;
using mutex_type = MutexT;
private:
mutex_type lock_;
alloc_policy alloc_;
public:
template <typename ... P>
sync_wrapper(P && ... pars)
: alloc_(std::forward<P>(pars) ...)
{}
void swap(sync_wrapper& rhs) {
LIBIPC_UNUSED auto guard = ipc::detail::unique_lock(lock_);
alloc_.swap(rhs.alloc_);
}
void* alloc(std::size_t size) {
LIBIPC_UNUSED auto guard = ipc::detail::unique_lock(lock_);
return alloc_.alloc(size);
}
void free(void* p, std::size_t size) {
LIBIPC_UNUSED auto guard = ipc::detail::unique_lock(lock_);
alloc_.free(p, size);
}
};
////////////////////////////////////////////////////////////////
/// Variable memory allocation wrapper
////////////////////////////////////////////////////////////////
template <std::size_t BaseSize = 0, std::size_t IterSize = sizeof(void*)>
struct default_mapping_policy {
enum : std::size_t {
base_size = BaseSize,
iter_size = IterSize,
classes_size = 64
};
template <typename F, typename ... P>
IPC_CONSTEXPR_ static void foreach(F && f, P && ... params) {
for (std::size_t i = 0; i < classes_size; ++i) {
f(i, std::forward<P>(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 <typename F, typename D, typename ... P>
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, size, std::forward<P>(params)...) :
d(size, std::forward<P>(params)...);
}
};
template <typename FixedAlloc,
typename DefaultAlloc = mem::static_alloc,
typename MappingP = default_mapping_policy<>>
class variable_wrapper {
struct initiator {
using falc_t = std::aligned_storage_t<sizeof(FixedAlloc), alignof(FixedAlloc)>;
falc_t arr_[MappingP::classes_size];
initiator() {
MappingP::foreach([](std::size_t id, falc_t * a) {
ipc::mem::construct(&initiator::at(a, id), MappingP::block_size(id));
}, arr_);
}
~initiator() {
MappingP::foreach([](std::size_t id, falc_t * a) {
ipc::mem::destruct(&initiator::at(a, id));
}, arr_);
}
static FixedAlloc & at(falc_t * arr, std::size_t id) noexcept {
return reinterpret_cast<FixedAlloc&>(arr[id]);
}
} init_;
using falc_t = typename initiator::falc_t;
public:
void swap(variable_wrapper & other) {
MappingP::foreach([](std::size_t id, falc_t * in, falc_t * ot) {
initiator::at(in, id).swap(initiator::at(ot, id));
}, init_.arr_, other.init_.arr_);
}
void* alloc(std::size_t size) {
return MappingP::classify([](std::size_t id, std::size_t size, falc_t * a) {
return initiator::at(a, id).alloc(size);
}, [](std::size_t size, falc_t *) {
return DefaultAlloc::alloc(size);
}, size, init_.arr_);
}
void free(void* p, std::size_t size) {
MappingP::classify([](std::size_t id, std::size_t size, void* p, falc_t * a) {
initiator::at(a, id).free(p, size);
}, [](std::size_t size, void* p, falc_t *) {
DefaultAlloc::free(p, size);
}, size, p, init_.arr_);
}
};
////////////////////////////////////////////////////////////////
/// Static allocation wrapper
////////////////////////////////////////////////////////////////
template <typename AllocP>
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);
}
};
} // namespace mem
} // namespace ipc

View File

@ -13,10 +13,10 @@
#include "libipc/shm.h"
#include "libipc/def.h"
#include "libipc/pool_alloc.h"
#include "libipc/utility/log.h"
#include "libipc/mem/resource.h"
#include "libipc/mem/new.h"
namespace {
@ -81,7 +81,7 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) {
::fchmod(fd, S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH);
auto ii = mem::alloc<id_info_t>();
auto ii = mem::$new<id_info_t>();
ii->fd_ = fd;
ii->size_ = size;
ii->name_ = std::move(op_name);
@ -177,7 +177,7 @@ std::int32_t release(id_t id) noexcept {
}
}
else ::munmap(ii->mem_, ii->size_);
mem::free(ii);
mem::$delete(ii);
return ret;
}

View File

@ -6,10 +6,10 @@
#include "libipc/shm.h"
#include "libipc/def.h"
#include "libipc/pool_alloc.h"
#include "libipc/utility/log.h"
#include "libipc/mem/resource.h"
#include "libipc/mem/new.h"
#include "to_tchar.h"
#include "get_sa.h"
@ -58,7 +58,7 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) {
return nullptr;
}
}
auto ii = mem::alloc<id_info_t>();
auto ii = mem::$new<id_info_t>();
ii->h_ = h;
ii->size_ = size;
return ii;
@ -116,7 +116,7 @@ std::int32_t release(id_t id) noexcept {
ipc::error("fail release: invalid id (h = null)\n");
}
else ::CloseHandle(ii->h_);
mem::free(ii);
mem::$delete(ii);
return 0;
}

View File

@ -1,17 +0,0 @@
#include "libipc/pool_alloc.h"
#include "libipc/mem/resource.h"
namespace ipc {
namespace mem {
void* pool_alloc::alloc(std::size_t size) noexcept {
return async_pool_alloc::alloc(size);
}
void pool_alloc::free(void* p, std::size_t size) noexcept {
async_pool_alloc::free(p, size);
}
} // namespace mem
} // namespace ipc

View File

@ -5,7 +5,7 @@
#include "libipc/platform/detail.h"
#include "libipc/utility/concept.h"
#include "libipc/pool_alloc.h"
#include "libipc/mem/new.h"
namespace ipc {
@ -36,12 +36,12 @@ IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplComfortable<T, void> {
template <typename T, typename... P>
IPC_CONSTEXPR_ auto make_impl(P&&... params) -> IsImplUncomfortable<T> {
return mem::alloc<T>(std::forward<P>(params)...);
return mem::$new<T>(std::forward<P>(params)...);
}
template <typename T>
IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplUncomfortable<T, void> {
mem::free(p);
mem::$delete(p);
}
template <typename T>

View File

@ -11,12 +11,12 @@
#include "libipc/mem/polymorphic_allocator.h"
#include "libipc/mem/memory_resource.h"
TEST(polymorphic_allocator, construct) {
TEST(polymorphic_allocator, ctor) {
ipc::mem::bytes_allocator alc;
SUCCEED();
}
TEST(polymorphic_allocator, construct_value_initialization) {
TEST(polymorphic_allocator, ctor_value_initialization) {
ipc::mem::bytes_allocator alc{};
auto p = alc.allocate(128);
EXPECT_NE(p, nullptr);
@ -57,7 +57,7 @@ TEST(polymorphic_allocator, memory_resource_traits) {
#endif
}
TEST(polymorphic_allocator, construct_copy_move) {
TEST(polymorphic_allocator, ctor_copy_move) {
ipc::mem::new_delete_resource mem_res;
dummy_resource dummy_res;
ipc::mem::bytes_allocator alc1{&mem_res}, alc2{&dummy_res};

View File

@ -12,16 +12,16 @@ TEST(central_cache_pool, ctor) {
ASSERT_FALSE((std::is_copy_assignable<ipc::mem::central_cache_pool<ipc::mem::block<1>, 1>>::value));
ASSERT_FALSE((std::is_move_assignable<ipc::mem::central_cache_pool<ipc::mem::block<1>, 1>>::value));
{
auto &pool = ipc::mem::central_cache_pool<ipc::mem::block<1>, 1>::instance();
ipc::mem::block<1> *b1 = pool.aqueire();
auto &pool = ipc::mem::central_cache_pool<ipc::mem::block<1024>, 1>::instance();
ipc::mem::block<1024> *b1 = pool.aqueire();
ASSERT_FALSE(nullptr == b1);
ASSERT_TRUE (nullptr == b1->next);
EXPECT_TRUE (nullptr == b1->next);
pool.release(b1);
ipc::mem::block<1> *b2 = pool.aqueire();
ipc::mem::block<1024> *b2 = pool.aqueire();
EXPECT_EQ(b1, b2);
ipc::mem::block<1> *b3 = pool.aqueire();
ipc::mem::block<1024> *b3 = pool.aqueire();
ASSERT_FALSE(nullptr == b3);
ASSERT_TRUE (nullptr == b3->next);
EXPECT_TRUE (nullptr == b3->next);
EXPECT_NE(b1, b3);
}
{
@ -29,7 +29,7 @@ TEST(central_cache_pool, ctor) {
ipc::mem::block<1> *b1 = pool.aqueire();
ASSERT_FALSE(nullptr == b1);
ASSERT_FALSE(nullptr == b1->next);
ASSERT_TRUE (nullptr == b1->next->next);
EXPECT_TRUE (nullptr == b1->next->next);
pool.release(b1);
ipc::mem::block<1> *b2 = pool.aqueire();
EXPECT_EQ(b1, b2);
@ -38,7 +38,7 @@ TEST(central_cache_pool, ctor) {
ipc::mem::block<1> *b4 = pool.aqueire();
ASSERT_FALSE(nullptr == b4);
ASSERT_FALSE(nullptr == b4->next);
ASSERT_TRUE (nullptr == b4->next->next);
EXPECT_TRUE (nullptr == b4->next->next);
EXPECT_NE(b1, b4);
}
}

View File

@ -4,6 +4,7 @@
#include <mutex>
#include <atomic>
#include <cstring>
#include <new>
#include "libipc/ipc.h"
#include "libipc/buffer.h"
@ -84,18 +85,19 @@ void test_basic(char const * name) {
}
class data_set {
std::vector<rand_buf> datas_;
alignas(rand_buf) char datas_[sizeof(rand_buf[LoopCount])];
rand_buf *d_;
public:
data_set() {
datas_.resize(LoopCount);
d_ = ::new(datas_) rand_buf[LoopCount];
for (int i = 0; i < LoopCount; ++i) {
datas_[i].set_id(i);
d_[i].set_id(i);
}
}
std::vector<rand_buf> const &get() const noexcept {
return datas_;
rand_buf const *get() const noexcept {
return d_;
}
} const data_set__;
@ -112,7 +114,7 @@ void test_sr(char const * name, int s_cnt, int r_cnt) {
Que que { name, ipc::sender };
ASSERT_TRUE(que.wait_for_recv(r_cnt));
sw.start();
for (int i = 0; i < (int)data_set__.get().size(); ++i) {
for (int i = 0; i < LoopCount; ++i) {
ASSERT_TRUE(que.send(data_set__.get()[i]));
}
};
@ -128,7 +130,7 @@ void test_sr(char const * name, int s_cnt, int r_cnt) {
if (i == -1) {
return;
}
ASSERT_TRUE((i >= 0) && (i < (int)data_set__.get().size()));
ASSERT_TRUE((i >= 0) && (i < LoopCount));
auto const &data_set = data_set__.get()[i];
if (data_set != got) {
printf("data_set__.get()[%d] != got, size = %zd/%zd\n",
@ -146,7 +148,7 @@ void test_sr(char const * name, int s_cnt, int r_cnt) {
que.send(rand_buf{msg_head{-1}});
}
ipc_ut::reader().wait_for_done();
sw.print_elapsed<std::chrono::microseconds>(s_cnt, r_cnt, (int)data_set__.get().size(), name);
sw.print_elapsed<std::chrono::microseconds>(s_cnt, r_cnt, LoopCount, name);
}
} // internal-linkage