fix some bugs; improve memory allocators

This commit is contained in:
zhangyi 2019-07-03 18:41:46 +08:00
parent fbf3c622e8
commit a4a6a9ca66
8 changed files with 217 additions and 90 deletions

View File

@ -33,7 +33,9 @@ HEADERS += \
../../src/policy.h \ ../../src/policy.h \
../../src/queue.h \ ../../src/queue.h \
../../src/log.h \ ../../src/log.h \
../../src/id_pool.h ../../src/id_pool.h \
../../src/pimpl.h \
../../src/concept.h
SOURCES += \ SOURCES += \
../../src/shm.cpp \ ../../src/shm.cpp \

View File

@ -6,7 +6,24 @@ namespace ipc {
// concept helpers // concept helpers
template <bool Cond, typename R> template <bool Cond, typename R = void>
using Requires = std::enable_if_t<Cond, R>; using require = std::enable_if_t<Cond, R>;
#ifdef IPC_CONCEPT_
# error "IPC_CONCEPT_ has been defined."
#endif
#define IPC_CONCEPT_(NAME, WHAT) \
template <typename T> \
class NAME { \
private: \
template <typename Type> \
static std::true_type check(decltype(std::declval<Type>().WHAT)*); \
template <typename Type> \
static std::false_type check(...); \
public: \
using type = decltype(check<T>(nullptr)); \
constexpr static auto value = type::value; \
}
} // namespace ipc } // namespace ipc

View File

@ -3,9 +3,12 @@
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <cstdlib> #include <cstdlib>
#include <map>
#include <iterator>
#include "def.h" #include "def.h"
#include "rw_lock.h" #include "rw_lock.h"
#include "concept.h"
#include "platform/detail.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; return ((size - 1) & ~(alignment - 1)) + alignment;
} }
IPC_CONCEPT_(has_take, take(Type{}));
class scope_alloc_base { class scope_alloc_base {
protected: protected:
struct block_t { struct block_t {
block_t * next_; block_t * next_;
std::size_t size_; std::size_t size_;
} * list_ = nullptr; } * head_ = nullptr, * tail_ = nullptr;
enum : std::size_t { enum : std::size_t {
aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t)) aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t))
@ -53,7 +58,23 @@ protected:
public: public:
void swap(scope_alloc_base & rhs) { 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*/) {} void free(void* /*p*/) {}
@ -72,19 +93,19 @@ private:
alloc_policy alloc_; alloc_policy alloc_;
void free_all() { void free_all() {
while (list_ != nullptr) { while (!empty()) {
auto curr = list_; auto curr = head_;
list_ = list_->next_; head_ = head_->next_;
alloc_.free(curr, curr->size_); alloc_.free(curr, curr->size_);
} }
// now list_ is nullptr // now head_ is nullptr
} }
public: public:
scope_alloc() = default; scope_alloc() = default;
scope_alloc(scope_alloc&& rhs) { this->swap(rhs); } scope_alloc(scope_alloc&& rhs) { swap(rhs); }
scope_alloc& operator=(scope_alloc&& rhs) { this->swap(rhs); return (*this); } scope_alloc& operator=(scope_alloc&& rhs) { swap(rhs); return (*this); }
~scope_alloc() { free_all(); } ~scope_alloc() { free_all(); }
@ -98,16 +119,32 @@ public:
base_t::swap(rhs); 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 clear() { void clear() {
free_all(); free_all();
tail_ = nullptr;
alloc_.~alloc_policy(); alloc_.~alloc_policy();
} }
void* alloc(std::size_t size) { void* alloc(std::size_t size) {
auto curr = static_cast<block_t*>(alloc_.alloc(size += aligned_block_size)); auto curr = static_cast<block_t*>(alloc_.alloc(size += aligned_block_size));
curr->next_ = list_; curr->next_ = head_;
curr->size_ = size; curr->size_ = size;
return (reinterpret_cast<byte_t*>(list_ = curr) + aligned_block_size); head_ = curr;
if (tail_ == nullptr) {
tail_ = curr;
}
return (reinterpret_cast<byte_t*>(curr) + aligned_block_size);
} }
}; };
@ -137,8 +174,30 @@ protected:
public: public:
void swap(fixed_alloc_base& rhs) { void swap(fixed_alloc_base& rhs) {
std::swap(this->init_expand_, rhs.init_expand_); std::swap(init_expand_, rhs.init_expand_);
std::swap(this->cursor_ , rhs.cursor_); 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) { void free(void* p) {
@ -191,24 +250,23 @@ private:
alloc_policy alloc_; alloc_policy alloc_;
void* try_expand() { void* try_expand() {
if (this->cursor_ != nullptr) { if (empty()) {
return this->cursor_; auto size = ExpandP::template next<block_size>(init_expand_);
} auto p = node_p(cursor_ = alloc_.alloc(size));
auto size = ExpandP::template next<block_size>(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) for (std::size_t i = 0; i < (size / block_size) - 1; ++i)
p = this->node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size); p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size);
(*p) = nullptr; (*p) = nullptr;
return this->cursor_; }
return cursor_;
} }
public: public:
explicit fixed_alloc(std::size_t init_expand = 1) { 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(fixed_alloc&& rhs) : fixed_alloc() { swap(rhs); }
fixed_alloc& operator=(fixed_alloc&& rhs) { this->swap(rhs); return (*this); } fixed_alloc& operator=(fixed_alloc&& rhs) { swap(rhs); return (*this); }
template <typename A> template <typename A>
void set_allocator(A && alc) { void set_allocator(A && alc) {
@ -220,15 +278,26 @@ public:
base_t::swap(rhs); 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_));
}
template <typename A = AllocP>
auto take(fixed_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
base_t::take(std::move(rhs));
}
void clear() { void clear() {
ExpandP::prev(this->init_expand_); ExpandP::prev(init_expand_);
this->cursor_ = nullptr; cursor_ = nullptr;
alloc_.~alloc_policy(); alloc_.~alloc_policy();
} }
void* alloc() { void* alloc() {
void* p = try_expand(); void* p = try_expand();
this->cursor_ = this->next(p); cursor_ = next(p);
return p; return p;
} }
@ -246,22 +315,43 @@ namespace detail {
class variable_alloc_base { class variable_alloc_base {
protected: protected:
struct head_t { struct head_t {
head_t * next_; std::size_t free_;
size_t size_;
size_t free_;
} * head_ = nullptr; } * head_ = nullptr;
std::map<std::size_t, head_t*, std::greater<std::size_t>> reserves_;
enum : std::size_t { enum : std::size_t {
aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t)) aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t))
}; };
static byte_t * buffer(head_t* p) { static byte_t * buffer(head_t* p) {
return reinterpret_cast<byte_t*>(p) + aligned_head_size; return reinterpret_cast<byte_t*>(p) + aligned_head_size + p->free_;
}
std::size_t remain() const noexcept {
return (head_ == nullptr) ? 0 : head_->free_;
} }
public: public:
void swap(variable_alloc_base& rhs) { 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*/) {} void free(void* /*p*/) {}
@ -270,7 +360,7 @@ public:
} // namespace detail } // namespace detail
template <std::size_t ChunkSize = (sizeof(void*) * 1024), typename AllocP = static_alloc> template <std::size_t ChunkSize = (sizeof(void*) * 1024), typename AllocP = scope_alloc<>>
class variable_alloc : public detail::variable_alloc_base { class variable_alloc : public detail::variable_alloc_base {
public: public:
using base_t = detail::variable_alloc_base; using base_t = detail::variable_alloc_base;
@ -280,41 +370,11 @@ public:
private: private:
alloc_policy alloc_; alloc_policy alloc_;
head_t* alloc_head(std::size_t size) {
size = (ipc::detail::max)(ChunkSize, ipc::detail::max<std::size_t>(size, aligned_head_size));
head_t* p = static_cast<head_t*>(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: public:
variable_alloc() = default; variable_alloc() = default;
variable_alloc(variable_alloc&& rhs) { this->swap(rhs); } variable_alloc(variable_alloc&& rhs) { swap(rhs); }
variable_alloc& operator=(variable_alloc&& rhs) { this->swap(rhs); return (*this); } variable_alloc& operator=(variable_alloc&& rhs) { swap(rhs); return (*this); }
~variable_alloc() { free_all(); }
template <typename A> template <typename A>
void set_allocator(A && alc) { void set_allocator(A && alc) {
@ -326,16 +386,44 @@ public:
base_t::swap(rhs); 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_));
}
template <typename A = AllocP>
auto take(variable_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
base_t::take(std::move(rhs));
}
void clear() { void clear() {
free_all();
alloc_.~alloc_policy(); alloc_.~alloc_policy();
} }
void* alloc(size_t size) { void* alloc(std::size_t size) {
if ((head_ == nullptr) || head_->free_ < size) { if (size >= (ChunkSize - aligned_head_size)) {
return alloc_new_chunk(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<head_t*>(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_);
} }
}; };

View File

@ -18,7 +18,7 @@ namespace ipc {
namespace mem { namespace mem {
template <std::size_t Size> template <std::size_t Size>
using static_sync_fixed = static_wrapper<sync_wrapper<fixed_alloc<Size>>>; using static_sync_fixed = static_wrapper<async_wrapper<fixed_alloc<Size, static_alloc>>>;
namespace detail { namespace detail {
@ -34,25 +34,15 @@ struct chunk_mapping_policy {
} }
}; };
template <typename AllocP>
struct chunk_alloc_recoverer : default_alloc_recoverer<AllocP> {
void collect(alloc_policy && alc) {
alc.clear(); // recycle memory to the central heap (static_chunk_alloc)
default_alloc_recoverer<AllocP>::collect(std::move(alc));
}
};
} // namespace detail } // namespace detail
using static_chunk_alloc = variable_wrapper<static_sync_fixed, detail::chunk_mapping_policy>; using static_chunk_alloc = variable_wrapper<static_sync_fixed, detail::chunk_mapping_policy>;
using chunk_variable_alloc = variable_alloc<detail::chunk_mapping_policy::base_size, static_chunk_alloc>; using chunk_variable_alloc = variable_alloc<detail::chunk_mapping_policy::base_size, static_chunk_alloc>;
template <std::size_t Size> template <std::size_t Size>
using static_async_fixed = using static_async_fixed = static_wrapper<async_wrapper<fixed_alloc<Size, chunk_variable_alloc>>>;
static_wrapper<async_wrapper<fixed_alloc<Size/*, chunk_variable_alloc*/>/*, detail::chunk_alloc_recoverer*/>>;
using async_pool_alloc = variable_wrapper<static_async_fixed>; using async_pool_alloc = variable_wrapper<static_async_fixed>;
//using async_pool_alloc = static_wrapper<async_wrapper<chunk_variable_alloc, detail::chunk_alloc_recoverer>>;
template <typename T> template <typename T>
using allocator = allocator_wrapper<T, async_pool_alloc>; using allocator = allocator_wrapper<T, async_pool_alloc>;

View File

@ -13,6 +13,7 @@
#include "def.h" #include "def.h"
#include "rw_lock.h" #include "rw_lock.h"
#include "tls_pointer.h" #include "tls_pointer.h"
#include "concept.h"
#include "memory/alloc.h" #include "memory/alloc.h"
#include "platform/detail.h" #include "platform/detail.h"
@ -157,6 +158,19 @@ public:
} }
} }
template <typename A = AllocP>
auto try_replenish(alloc_policy & alc) -> ipc::require<detail::has_take<A>::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 <typename A = AllocP>
constexpr static auto try_replenish(alloc_policy & /*alc*/) noexcept
-> ipc::require<!detail::has_take<A>::value> {}
void collect(alloc_policy && alc) { void collect(alloc_policy && alc) {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
master_allocs_.emplace_back(std::move(alc)); master_allocs_.emplace_back(std::move(alc));
@ -175,6 +189,8 @@ private:
class alloc_proxy : public AllocP { class alloc_proxy : public AllocP {
async_wrapper * w_ = nullptr; async_wrapper * w_ = nullptr;
IPC_CONCEPT_(has_empty, empty());
public: public:
alloc_proxy(alloc_proxy && rhs) alloc_proxy(alloc_proxy && rhs)
: AllocP(std::move(rhs)) : AllocP(std::move(rhs))
@ -190,6 +206,20 @@ private:
if (w_ == nullptr) return; if (w_ == nullptr) return;
w_->recoverer_.collect(std::move(*this)); w_->recoverer_.collect(std::move(*this));
} }
template <typename A = AllocP>
auto alloc(std::size_t size) -> ipc::require<has_empty<A>::value, void*> {
auto p = AllocP::alloc(size);
if (AllocP::empty() && (w_ != nullptr)) {
w_->recoverer_.try_replenish(*this);
}
return p;
}
template <typename A = AllocP>
auto alloc(std::size_t size) -> ipc::require<!has_empty<A>::value, void*> {
return AllocP::alloc(size);
}
}; };
friend class alloc_proxy; friend class alloc_proxy;

View File

@ -11,10 +11,10 @@ namespace ipc {
// pimpl small object optimization helpers // pimpl small object optimization helpers
template <typename T, typename R = T*> template <typename T, typename R = T*>
using IsImplComfortable = Requires<(sizeof(T) <= sizeof(T*)), R>; using IsImplComfortable = ipc::require<(sizeof(T) <= sizeof(T*)), R>;
template <typename T, typename R = T*> template <typename T, typename R = T*>
using IsImplUncomfortable = Requires<(sizeof(T) > sizeof(T*)), R>; using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>;
template <typename T, typename... P> template <typename T, typename... P>
constexpr auto make_impl(P&&... params) -> IsImplComfortable<T> { constexpr auto make_impl(P&&... params) -> IsImplComfortable<T> {

View File

@ -27,7 +27,7 @@ template <typename T, typename U>
struct is_same_char<T, U, std::true_type> : std::is_same<T, typename U::value_type> {}; struct is_same_char<T, U, std::true_type> : std::is_same<T, typename U::value_type> {};
template <typename T, typename S, typename R = S> template <typename T, typename S, typename R = S>
using IsSameChar = ipc::Requires<is_same_char<T, S>::value, R>; using IsSameChar = ipc::require<is_same_char<T, S>::value, R>;
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// to_tchar implementation /// to_tchar implementation

View File

@ -172,9 +172,9 @@ struct test_performance<AllocT, ModeT, 1> {
}; };
void Unit::test_static() { void Unit::test_static() {
//test_performance<ipc::mem::static_alloc, alloc_FIFO , 8>::start(); test_performance<ipc::mem::static_alloc, alloc_FIFO , 8>::start();
//test_performance<ipc::mem::static_alloc, alloc_LIFO , 8>::start(); test_performance<ipc::mem::static_alloc, alloc_LIFO , 8>::start();
//test_performance<ipc::mem::static_alloc, alloc_random, 8>::start(); test_performance<ipc::mem::static_alloc, alloc_random, 8>::start();
} }
void Unit::test_pool() { void Unit::test_pool() {