prepare for multi-thread fixed-alloc

This commit is contained in:
mutouyun 2019-01-20 22:22:47 +08:00
parent 27d6b0c7f5
commit 4d2b47cbb7
6 changed files with 129 additions and 56 deletions

View File

@ -30,6 +30,7 @@ HEADERS += \
../src/memory/alloc.hpp \ ../src/memory/alloc.hpp \
../src/memory/wrapper.hpp \ ../src/memory/wrapper.hpp \
../src/memory/resource.hpp \ ../src/memory/resource.hpp \
../src/platform/detail.h \
../src/platform/waiter.h ../src/platform/waiter.h
SOURCES += \ SOURCES += \

View File

@ -100,13 +100,13 @@ class spin_lock {
std::atomic<std::size_t> lc_ { 0 }; std::atomic<std::size_t> lc_ { 0 };
public: public:
void lock(void) { void lock(void) noexcept {
for (unsigned k = 0; for (unsigned k = 0;
lc_.exchange(1, std::memory_order_acquire); lc_.exchange(1, std::memory_order_acquire);
yield(k)) ; yield(k)) ;
} }
void unlock(void) { void unlock(void) noexcept {
lc_.store(0, std::memory_order_release); lc_.store(0, std::memory_order_release);
} }
}; };

View File

@ -2,9 +2,15 @@
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <atomic>
#include <thread>
#include <mutex>
#include <cstdlib> #include <cstdlib>
#include "def.h" #include "def.h"
#include "rw_lock.h"
#include "platform/detail.h"
namespace ipc { namespace ipc {
namespace mem { namespace mem {
@ -100,14 +106,41 @@ public:
namespace detail { namespace detail {
template <typename T>
class non_atomic {
T val_;
public:
void store(T val, std::memory_order) noexcept {
val_ = val;
}
T load(std::memory_order) const noexcept {
return val_;
}
bool compare_exchange_weak(T&, T val, std::memory_order) noexcept {
// always return true
val_ = val;
return true;
}
};
class non_lock {
public:
void lock (void) noexcept {}
void unlock(void) noexcept {}
};
template <template <typename> class Atomic>
class fixed_alloc_base { class fixed_alloc_base {
protected: protected:
std::size_t init_expand_; std::size_t init_expand_;
void* cursor_; Atomic<void*> cursor_;
void init(std::size_t init_expand) { void init(std::size_t init_expand) {
init_expand_ = init_expand; init_expand_ = init_expand;
cursor_ = nullptr; cursor_.store(nullptr, std::memory_order_relaxed);
} }
static void** node_p(void* node) { static void** node_p(void* node) {
@ -119,10 +152,22 @@ protected:
} }
public: public:
void swap(fixed_alloc_base& rhs) {
std::swap(this->init_expand_, rhs.init_expand_);
auto tmp = this->cursor_.load(std::memory_order_relaxed);
this->cursor_.store(rhs.cursor_.load(std::memory_order_relaxed), std::memory_order_relaxed);
rhs.cursor_.store(tmp, std::memory_order_relaxed);
}
void free(void* p) { void free(void* p) {
if (p == nullptr) return; if (p == nullptr) return;
next(p) = cursor_; while (1) {
cursor_ = p; next(p) = cursor_.load(std::memory_order_acquire);
if (cursor_.compare_exchange_weak(next(p), p, std::memory_order_relaxed)) {
break;
}
std::this_thread::yield();
}
} }
void free(void* p, std::size_t) { void free(void* p, std::size_t) {
@ -132,9 +177,12 @@ public:
} // namespace detail } // namespace detail
template <std::size_t BlockSize, typename AllocP = scope_alloc<>> template <std::size_t BlockSize, typename AllocP = scope_alloc<>,
class fixed_alloc : public detail::fixed_alloc_base { template <typename> class Atomic = detail::non_atomic,
typename Lock = detail::non_lock>
class fixed_alloc : public detail::fixed_alloc_base<Atomic> {
public: public:
using base_t = detail::fixed_alloc_base<Atomic>;
using alloc_policy = AllocP; using alloc_policy = AllocP;
enum : std::size_t { enum : std::size_t {
@ -147,18 +195,27 @@ public:
private: private:
alloc_policy alloc_; alloc_policy alloc_;
Lock lc_;
void expand() { void* expand() {
auto p = node_p(cursor_ = alloc_.alloc(block_size)); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_);
auto c = this->cursor_.load(std::memory_order_relaxed);
if (c != nullptr) {
return c;
}
auto a = alloc_.alloc(block_size);
this->cursor_.store(a, std::memory_order_relaxed);
auto p = this->node_p(a);
auto size = alloc_.size_of(p); auto size = alloc_.size_of(p);
if (size > 0) for (std::size_t i = 0; i < (size / block_size) - 1; ++i) if (size > 0) for (std::size_t i = 0; i < (size / block_size) - 1; ++i)
p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size); p = this->node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size);
(*p) = nullptr; (*p) = nullptr;
return a;
} }
public: public:
explicit fixed_alloc(std::size_t init_expand = 1) { explicit fixed_alloc(std::size_t init_expand = 1) {
init(init_expand); this->init(init_expand);
} }
fixed_alloc(fixed_alloc&& rhs) { this->swap(rhs); } fixed_alloc(fixed_alloc&& rhs) { this->swap(rhs); }
@ -166,14 +223,15 @@ public:
public: public:
void swap(fixed_alloc& rhs) { void swap(fixed_alloc& rhs) {
std::swap(this->alloc_ , rhs.alloc_); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_);
std::swap(this->init_expand_, rhs.init_expand_); std::swap(this->alloc_, rhs.alloc_);
std::swap(this->cursor_ , rhs.cursor_); base_t::swap(rhs);
} }
void clear() { void clear() {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_);
alloc_.clear(); alloc_.clear();
init(init_expand_); this->init(this->init_expand_);
} }
constexpr std::size_t size_of(void* /*p*/) const { constexpr std::size_t size_of(void* /*p*/) const {
@ -181,9 +239,14 @@ public:
} }
void* alloc() { void* alloc() {
if (cursor_ == nullptr) expand(); void* p;
void* p = cursor_; while (1) {
cursor_ = next(p); p = expand();
if (this->cursor_.compare_exchange_weak(p, this->next(p), std::memory_order_relaxed)) {
break;
}
std::this_thread::yield();
}
return p; return p;
} }

View File

@ -3,7 +3,6 @@
#include <limits> #include <limits>
#include <new> #include <new>
#include <mutex> #include <mutex>
#include <shared_mutex>
#include <tuple> #include <tuple>
#include <map> #include <map>
#include <vector> #include <vector>
@ -16,20 +15,9 @@
#include "rw_lock.h" #include "rw_lock.h"
#include "tls_pointer.h" #include "tls_pointer.h"
#include "platform/detail.h"
namespace ipc { namespace ipc {
namespace detail {
#if __cplusplus >= 201703L
using std::unique_lock;
#else /*__cplusplus < 201703L*/
// deduction guides for std::unique_lock
template <typename T>
constexpr auto unique_lock(T&& lc) {
return std::unique_lock<std::decay_t<T>> { std::forward<T>(lc) };
}
#endif/*__cplusplus < 201703L*/
} // namespace detail
namespace mem { namespace mem {
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////

43
src/platform/detail.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include <memory>
#include <mutex>
#include <type_traits>
#if __cplusplus >= 201703L
namespace std {
// deduction guides for std::unique_ptr
template <typename T, typename D>
unique_ptr(T* p, D&& d) -> unique_ptr<T, std::decay_t<D>>;
} // namespace std
namespace ipc {
namespace detail {
using std::unique_ptr;
using std::unique_lock;
#else /*__cplusplus < 201703L*/
namespace ipc {
namespace detail {
// deduction guides for std::unique_ptr
template <typename T, typename D>
constexpr auto unique_ptr(T* p, D&& d) {
return std::unique_ptr<T, std::decay_t<D>> { p, std::forward<D>(d) };
}
// deduction guides for std::unique_lock
template <typename T>
constexpr auto unique_lock(T&& lc) {
return std::unique_lock<std::decay_t<T>> { std::forward<T>(lc) };
}
#endif/*__cplusplus < 201703L*/
} // namespace detail
} // namespace ipc

View File

@ -4,36 +4,14 @@
#include <cstring> #include <cstring>
#include <atomic> #include <atomic>
#include <memory>
#include <type_traits>
#include "def.h" #include "def.h"
#if __cplusplus >= 201703L #include "platform/detail.h"
namespace std {
// deduction guides for std::unique_ptr
template <typename T, typename D>
unique_ptr(T* p, D&& d) -> unique_ptr<T, std::decay_t<D>>;
} // namespace std
namespace ipc { namespace ipc {
namespace detail { namespace detail {
using std::unique_ptr;
#else /*__cplusplus < 201703L*/
namespace ipc {
namespace detail {
// deduction guides for std::unique_ptr
template <typename T, typename D>
constexpr auto unique_ptr(T* p, D&& d) {
return std::unique_ptr<T, std::decay_t<D>> { p, std::forward<D>(d) };
}
#endif/*__cplusplus < 201703L*/
class waiter { class waiter {
pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_ = PTHREAD_COND_INITIALIZER; pthread_cond_t cond_ = PTHREAD_COND_INITIALIZER;