fix tls bugs (win); modify data structure

This commit is contained in:
zhangyi 2019-07-04 16:37:00 +08:00
parent 9bc6604faa
commit 6b7c561496
39 changed files with 6296 additions and 6211 deletions

View File

@ -1,52 +1,52 @@
#pragma once #pragma once
#if defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) #if defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
# define IPC_DECL_EXPORT Q_DECL_EXPORT # define IPC_DECL_EXPORT Q_DECL_EXPORT
# define IPC_DECL_IMPORT Q_DECL_IMPORT # define IPC_DECL_IMPORT Q_DECL_IMPORT
#else // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) #else // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
/* /*
* Compiler & system detection for IPC_DECL_EXPORT & IPC_DECL_IMPORT. * Compiler & system detection for IPC_DECL_EXPORT & IPC_DECL_IMPORT.
* Not using QtCore cause it shouldn't depend on Qt. * Not using QtCore cause it shouldn't depend on Qt.
*/ */
#if defined(_MSC_VER) #if defined(_MSC_VER)
# define IPC_DECL_EXPORT __declspec(dllexport) # define IPC_DECL_EXPORT __declspec(dllexport)
# define IPC_DECL_IMPORT __declspec(dllimport) # define IPC_DECL_IMPORT __declspec(dllimport)
#elif defined(__ARMCC__) || defined(__CC_ARM) #elif defined(__ARMCC__) || defined(__CC_ARM)
# if defined(ANDROID) || defined(__linux__) || defined(__linux) # if defined(ANDROID) || defined(__linux__) || defined(__linux)
# define IPC_DECL_EXPORT __attribute__((visibility("default"))) # define IPC_DECL_EXPORT __attribute__((visibility("default")))
# define IPC_DECL_IMPORT __attribute__((visibility("default"))) # define IPC_DECL_IMPORT __attribute__((visibility("default")))
# else # else
# define IPC_DECL_EXPORT __declspec(dllexport) # define IPC_DECL_EXPORT __declspec(dllexport)
# define IPC_DECL_IMPORT __declspec(dllimport) # define IPC_DECL_IMPORT __declspec(dllimport)
# endif # endif
#elif defined(__GNUC__) #elif defined(__GNUC__)
# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ # if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
defined(WIN64) || defined(_WIN64) || defined(__WIN64__) defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
# define IPC_DECL_EXPORT __declspec(dllexport) # define IPC_DECL_EXPORT __declspec(dllexport)
# define IPC_DECL_IMPORT __declspec(dllimport) # define IPC_DECL_IMPORT __declspec(dllimport)
# else # else
# define IPC_DECL_EXPORT __attribute__((visibility("default"))) # define IPC_DECL_EXPORT __attribute__((visibility("default")))
# define IPC_DECL_IMPORT __attribute__((visibility("default"))) # define IPC_DECL_IMPORT __attribute__((visibility("default")))
# endif # endif
#else #else
# define IPC_DECL_EXPORT __attribute__((visibility("default"))) # define IPC_DECL_EXPORT __attribute__((visibility("default")))
# define IPC_DECL_IMPORT __attribute__((visibility("default"))) # define IPC_DECL_IMPORT __attribute__((visibility("default")))
#endif #endif
#endif // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) #endif // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
/* /*
* Define IPC_EXPORT for exporting function & class. * Define IPC_EXPORT for exporting function & class.
*/ */
#ifndef IPC_EXPORT #ifndef IPC_EXPORT
#if defined(__IPC_LIBRARY__) #if defined(__IPC_LIBRARY__)
# define IPC_EXPORT IPC_DECL_EXPORT # define IPC_EXPORT IPC_DECL_EXPORT
#else #else
# define IPC_EXPORT IPC_DECL_IMPORT # define IPC_EXPORT IPC_DECL_IMPORT
#endif #endif
#endif /*IPC_EXPORT*/ #endif /*IPC_EXPORT*/

View File

@ -1,151 +1,151 @@
#pragma once #pragma once
#include "export.h" #include "export.h"
#include "def.h" #include "def.h"
#include "buffer.h" #include "buffer.h"
#include "shm.h" #include "shm.h"
#include <string> #include <string>
namespace ipc { namespace ipc {
using handle_t = void*; using handle_t = void*;
using buff_t = buffer; using buff_t = buffer;
enum : unsigned { enum : unsigned {
sender, sender,
receiver receiver
}; };
template <typename Flag> template <typename Flag>
struct IPC_EXPORT chan_impl { struct IPC_EXPORT chan_impl {
static handle_t connect (char const * name, unsigned mode); static handle_t connect (char const * name, unsigned mode);
static void disconnect(handle_t h); static void disconnect(handle_t h);
static char const * name(handle_t h); static char const * name(handle_t h);
static std::size_t recv_count(handle_t h); static std::size_t recv_count(handle_t h);
static bool wait_for_recv(handle_t h, std::size_t r_count, std::size_t tm); static bool wait_for_recv(handle_t h, std::size_t r_count, std::size_t tm);
static bool send(handle_t h, void const * data, std::size_t size); static bool send(handle_t h, void const * data, std::size_t size);
static buff_t recv(handle_t h, std::size_t tm); static buff_t recv(handle_t h, std::size_t tm);
static bool try_send(handle_t h, void const * data, std::size_t size); static bool try_send(handle_t h, void const * data, std::size_t size);
static buff_t try_recv(handle_t h); static buff_t try_recv(handle_t h);
}; };
template <typename Flag> template <typename Flag>
class chan_wrapper { class chan_wrapper {
private: private:
using detail_t = chan_impl<Flag>; using detail_t = chan_impl<Flag>;
handle_t h_ = nullptr; handle_t h_ = nullptr;
public: public:
chan_wrapper() = default; chan_wrapper() = default;
explicit chan_wrapper(char const * name, unsigned mode = sender) { explicit chan_wrapper(char const * name, unsigned mode = sender) {
this->connect(name, mode); this->connect(name, mode);
} }
chan_wrapper(chan_wrapper&& rhs) { chan_wrapper(chan_wrapper&& rhs) {
swap(rhs); swap(rhs);
} }
~chan_wrapper() { ~chan_wrapper() {
disconnect(); disconnect();
} }
void swap(chan_wrapper& rhs) { void swap(chan_wrapper& rhs) {
std::swap(h_, rhs.h_); std::swap(h_, rhs.h_);
} }
chan_wrapper& operator=(chan_wrapper rhs) { chan_wrapper& operator=(chan_wrapper rhs) {
swap(rhs); swap(rhs);
return *this; return *this;
} }
char const * name() const { char const * name() const {
return detail_t::name(h_); return detail_t::name(h_);
} }
handle_t handle() const { handle_t handle() const {
return h_; return h_;
} }
bool valid() const { bool valid() const {
return (handle() != nullptr); return (handle() != nullptr);
} }
chan_wrapper clone() const { chan_wrapper clone() const {
return chan_wrapper { name() }; return chan_wrapper { name() };
} }
bool connect(char const * name, unsigned mode = sender | receiver) { bool connect(char const * name, unsigned mode = sender | receiver) {
if (name == nullptr || name[0] == '\0') return false; if (name == nullptr || name[0] == '\0') return false;
this->disconnect(); this->disconnect();
h_ = detail_t::connect(name, mode); h_ = detail_t::connect(name, mode);
return valid(); return valid();
} }
void disconnect() { void disconnect() {
if (!valid()) return; if (!valid()) return;
detail_t::disconnect(h_); detail_t::disconnect(h_);
h_ = nullptr; h_ = nullptr;
} }
std::size_t recv_count() const { std::size_t recv_count() const {
return detail_t::recv_count(h_); return detail_t::recv_count(h_);
} }
bool wait_for_recv(std::size_t r_count, std::size_t tm = invalid_value) const { bool wait_for_recv(std::size_t r_count, std::size_t tm = invalid_value) const {
return detail_t::wait_for_recv(h_, r_count, tm); return detail_t::wait_for_recv(h_, r_count, tm);
} }
static bool wait_for_recv(char const * name, std::size_t r_count, std::size_t tm = invalid_value) { static bool wait_for_recv(char const * name, std::size_t r_count, std::size_t tm = invalid_value) {
return chan_wrapper(name).wait_for_recv(r_count, tm); return chan_wrapper(name).wait_for_recv(r_count, tm);
} }
bool send (void const * data, std::size_t size) { return detail_t::send(h_, data, size) ; } bool send (void const * data, std::size_t size) { return detail_t::send(h_, data, size) ; }
bool send (buff_t const & buff) { return this->send(buff.data(), buff.size()) ; } bool send (buff_t const & buff) { return this->send(buff.data(), buff.size()) ; }
bool send (std::string const & str) { return this->send(str.c_str(), str.size() + 1); } bool send (std::string const & str) { return this->send(str.c_str(), str.size() + 1); }
bool try_send(void const * data, std::size_t size) { return detail_t::try_send(h_, data, size) ; } bool try_send(void const * data, std::size_t size) { return detail_t::try_send(h_, data, size) ; }
bool try_send(buff_t const & buff) { return this->try_send(buff.data(), buff.size()) ; } bool try_send(buff_t const & buff) { return this->try_send(buff.data(), buff.size()) ; }
bool try_send(std::string const & str) { return this->try_send(str.c_str(), str.size() + 1); } bool try_send(std::string const & str) { return this->try_send(str.c_str(), str.size() + 1); }
buff_t recv(std::size_t tm = invalid_value) { buff_t recv(std::size_t tm = invalid_value) {
return detail_t::recv(h_, tm); return detail_t::recv(h_, tm);
} }
buff_t try_recv() { buff_t try_recv() {
return detail_t::try_recv(h_); return detail_t::try_recv(h_);
} }
}; };
template <typename Flag> template <typename Flag>
using chan = chan_wrapper<Flag>; using chan = chan_wrapper<Flag>;
/* /*
* class route * class route
* *
* You could use one producer/server/sender for sending messages to a route, * You could use one producer/server/sender for sending messages to a route,
* then all the consumers/clients/receivers which are receiving with this route, * then all the consumers/clients/receivers which are receiving with this route,
* would receive your sent messages. * would receive your sent messages.
* *
* A route could only be used in 1 to N * A route could only be used in 1 to N
* (one producer/writer to multi consumers/readers) * (one producer/writer to multi consumers/readers)
*/ */
using route = chan<ipc::wr<relat::single, relat::multi, trans::broadcast>>; using route = chan<ipc::wr<relat::single, relat::multi, trans::broadcast>>;
/* /*
* class channel * class channel
* *
* You could use multi producers/writers for sending messages to a channel, * You could use multi producers/writers for sending messages to a channel,
* then all the consumers/readers which are receiving with this channel, * then all the consumers/readers which are receiving with this channel,
* would receive your sent messages. * would receive your sent messages.
*/ */
using channel = chan<ipc::wr<relat::multi, relat::multi, trans::broadcast>>; using channel = chan<ipc::wr<relat::multi, relat::multi, trans::broadcast>>;
} // namespace ipc } // namespace ipc

View File

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

View File

@ -1,170 +1,170 @@
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <thread> #include <thread>
#include <chrono> #include <chrono>
#include <limits> #include <limits>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// Gives hint to processor that improves performance of spin-wait loops. /// Gives hint to processor that improves performance of spin-wait loops.
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
#pragma push_macro("IPC_LOCK_PAUSE_") #pragma push_macro("IPC_LOCK_PAUSE_")
#undef IPC_LOCK_PAUSE_ #undef IPC_LOCK_PAUSE_
#if defined(_MSC_VER) #if defined(_MSC_VER)
#include <windows.h> // YieldProcessor #include <windows.h> // YieldProcessor
/* /*
See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms687419(v=vs.85).aspx See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms687419(v=vs.85).aspx
Not for intel c++ compiler, so ignore http://software.intel.com/en-us/forums/topic/296168 Not for intel c++ compiler, so ignore http://software.intel.com/en-us/forums/topic/296168
*/ */
# define IPC_LOCK_PAUSE_() YieldProcessor() # define IPC_LOCK_PAUSE_() YieldProcessor()
#elif defined(__GNUC__) #elif defined(__GNUC__)
#if defined(__i386__) || defined(__x86_64__) #if defined(__i386__) || defined(__x86_64__)
/* /*
See: Intel(R) 64 and IA-32 Architectures Software Developer's Manual V2 See: Intel(R) 64 and IA-32 Architectures Software Developer's Manual V2
PAUSE-Spin Loop Hint, 4-57 PAUSE-Spin Loop Hint, 4-57
http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.html?wapkw=instruction+set+reference http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.html?wapkw=instruction+set+reference
*/ */
# define IPC_LOCK_PAUSE_() __asm__ __volatile__("pause") # define IPC_LOCK_PAUSE_() __asm__ __volatile__("pause")
#elif defined(__ia64__) || defined(__ia64) #elif defined(__ia64__) || defined(__ia64)
/* /*
See: Intel(R) Itanium(R) Architecture Developer's Manual, Vol.3 See: Intel(R) Itanium(R) Architecture Developer's Manual, Vol.3
hint - Performance Hint, 3:145 hint - Performance Hint, 3:145
http://www.intel.com/content/www/us/en/processors/itanium/itanium-architecture-vol-3-manual.html http://www.intel.com/content/www/us/en/processors/itanium/itanium-architecture-vol-3-manual.html
*/ */
# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("hint @pause") # define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("hint @pause")
#elif defined(__arm__) #elif defined(__arm__)
/* /*
See: ARM Architecture Reference Manuals (YIELD) See: ARM Architecture Reference Manuals (YIELD)
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.architecture.reference/index.html http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.architecture.reference/index.html
*/ */
# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("yield") # define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("yield")
#endif #endif
#endif/*compilers*/ #endif/*compilers*/
#if !defined(IPC_LOCK_PAUSE_) #if !defined(IPC_LOCK_PAUSE_)
/* /*
Just use a compiler fence, prevent compiler from optimizing loop Just use a compiler fence, prevent compiler from optimizing loop
*/ */
# define IPC_LOCK_PAUSE_() std::atomic_signal_fence(std::memory_order_seq_cst) # define IPC_LOCK_PAUSE_() std::atomic_signal_fence(std::memory_order_seq_cst)
#endif/*!defined(IPC_LOCK_PAUSE_)*/ #endif/*!defined(IPC_LOCK_PAUSE_)*/
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// Yield to other threads /// Yield to other threads
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
namespace ipc { namespace ipc {
template <typename K> template <typename K>
inline void yield(K& k) noexcept { inline void yield(K& k) noexcept {
if (k < 4) { /* Do nothing */ } if (k < 4) { /* Do nothing */ }
else else
if (k < 16) { IPC_LOCK_PAUSE_(); } if (k < 16) { IPC_LOCK_PAUSE_(); }
else { else {
std::this_thread::yield(); std::this_thread::yield();
return; return;
} }
++k; ++k;
} }
template <std::size_t N = 4096, typename K, typename F> template <std::size_t N = 4096, typename K, typename F>
inline void sleep(K& k, F&& f) { inline void sleep(K& k, F&& f) {
if (k < static_cast<K>(N)) { if (k < static_cast<K>(N)) {
std::this_thread::yield(); std::this_thread::yield();
} }
else if (std::forward<F>(f)()) { else if (std::forward<F>(f)()) {
return; return;
} }
else { else {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
return; return;
} }
++k; ++k;
} }
template <std::size_t N = 4096, typename K> template <std::size_t N = 4096, typename K>
inline void sleep(K& k) { inline void sleep(K& k) {
sleep<N>(k, [] { return false; }); sleep<N>(k, [] { return false; });
} }
} // namespace ipc } // namespace ipc
#pragma pop_macro("IPC_LOCK_PAUSE_") #pragma pop_macro("IPC_LOCK_PAUSE_")
namespace ipc { namespace ipc {
class spin_lock { class spin_lock {
std::atomic<unsigned> lc_ { 0 }; std::atomic<unsigned> lc_ { 0 };
public: public:
void lock(void) noexcept { 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) noexcept { void unlock(void) noexcept {
lc_.store(0, std::memory_order_release); lc_.store(0, std::memory_order_release);
} }
}; };
class rw_lock { class rw_lock {
using lc_ui_t = unsigned; using lc_ui_t = unsigned;
std::atomic<lc_ui_t> lc_ { 0 }; std::atomic<lc_ui_t> lc_ { 0 };
enum : lc_ui_t { enum : lc_ui_t {
w_mask = (std::numeric_limits<std::make_signed_t<lc_ui_t>>::max)(), // b 0111 1111 w_mask = (std::numeric_limits<std::make_signed_t<lc_ui_t>>::max)(), // b 0111 1111
w_flag = w_mask + 1 // b 1000 0000 w_flag = w_mask + 1 // b 1000 0000
}; };
public: public:
rw_lock() = default; rw_lock() = default;
rw_lock(const rw_lock&) = delete; rw_lock(const rw_lock&) = delete;
rw_lock& operator=(const rw_lock&) = delete; rw_lock& operator=(const rw_lock&) = delete;
rw_lock(rw_lock&&) = delete; rw_lock(rw_lock&&) = delete;
rw_lock& operator=(rw_lock&&) = delete; rw_lock& operator=(rw_lock&&) = delete;
void lock() noexcept { void lock() noexcept {
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
auto old = lc_.fetch_or(w_flag, std::memory_order_acq_rel); auto old = lc_.fetch_or(w_flag, std::memory_order_acq_rel);
if (!old) return; // got w-lock if (!old) return; // got w-lock
if (!(old & w_flag)) break; // other thread having r-lock if (!(old & w_flag)) break; // other thread having r-lock
yield(k); // other thread having w-lock yield(k); // other thread having w-lock
} }
// wait for reading finished // wait for reading finished
for (unsigned k = 0; for (unsigned k = 0;
lc_.load(std::memory_order_acquire) & w_mask; lc_.load(std::memory_order_acquire) & w_mask;
yield(k)) ; yield(k)) ;
} }
void unlock() noexcept { void unlock() noexcept {
lc_.store(0, std::memory_order_release); lc_.store(0, std::memory_order_release);
} }
void lock_shared() noexcept { void lock_shared() noexcept {
auto old = lc_.load(std::memory_order_acquire); auto old = lc_.load(std::memory_order_acquire);
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
// if w_flag set, just continue // if w_flag set, just continue
if (old & w_flag) { if (old & w_flag) {
yield(k); yield(k);
old = lc_.load(std::memory_order_acquire); old = lc_.load(std::memory_order_acquire);
} }
// otherwise try cas lc + 1 (set r-lock) // otherwise try cas lc + 1 (set r-lock)
else if (lc_.compare_exchange_weak(old, old + 1, std::memory_order_release)) { else if (lc_.compare_exchange_weak(old, old + 1, std::memory_order_release)) {
return; return;
} }
// set r-lock failed, old has been updated // set r-lock failed, old has been updated
} }
} }
void unlock_shared() noexcept { void unlock_shared() noexcept {
lc_.fetch_sub(1, std::memory_order_release); lc_.fetch_sub(1, std::memory_order_release);
} }
}; };
} // namespace ipc } // namespace ipc

View File

@ -1,52 +1,52 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include "export.h" #include "export.h"
namespace ipc { namespace ipc {
namespace shm { namespace shm {
using id_t = void*; using id_t = void*;
enum : unsigned { enum : unsigned {
create = 0x01, create = 0x01,
open = 0x02 open = 0x02
}; };
IPC_EXPORT id_t acquire(char const * name, std::size_t size, unsigned mode = create | open); IPC_EXPORT id_t acquire(char const * name, std::size_t size, unsigned mode = create | open);
IPC_EXPORT void * get_mem(id_t id, std::size_t * size); IPC_EXPORT void * get_mem(id_t id, std::size_t * size);
IPC_EXPORT void release(id_t id); IPC_EXPORT void release(id_t id);
IPC_EXPORT void remove (id_t id); IPC_EXPORT void remove (id_t id);
IPC_EXPORT void remove (char const * name); IPC_EXPORT void remove (char const * name);
class IPC_EXPORT handle { class IPC_EXPORT handle {
public: public:
handle(); handle();
handle(char const * name, std::size_t size, unsigned mode = create | open); handle(char const * name, std::size_t size, unsigned mode = create | open);
handle(handle&& rhs); handle(handle&& rhs);
~handle(); ~handle();
void swap(handle& rhs); void swap(handle& rhs);
handle& operator=(handle rhs); handle& operator=(handle rhs);
bool valid() const; bool valid() const;
std::size_t size () const; std::size_t size () const;
char const * name () const; char const * name () const;
bool acquire(char const * name, std::size_t size, unsigned mode = create | open); bool acquire(char const * name, std::size_t size, unsigned mode = create | open);
void release(); void release();
void* get() const; void* get() const;
void attach(id_t); void attach(id_t);
id_t detach(); id_t detach();
private: private:
class handle_; class handle_;
handle_* p_; handle_* p_;
}; };
} // namespace shm } // namespace shm
} // namespace ipc } // namespace ipc

View File

@ -1,86 +1,90 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <utility> #include <utility>
#include <limits> #include <limits>
#include "export.h" #include "export.h"
namespace ipc { namespace ipc {
namespace tls { namespace tls {
using key_t = std::uint64_t; using key_t = std::uint64_t;
using destructor_t = void (*)(void*); using destructor_t = void (*)(void*);
enum : key_t { enum : key_t {
invalid_value = (std::numeric_limits<key_t>::max)() invalid_value = (std::numeric_limits<key_t>::max)()
}; };
IPC_EXPORT key_t create (destructor_t destructor = nullptr); IPC_EXPORT key_t create (destructor_t destructor = nullptr);
IPC_EXPORT void release(key_t key); IPC_EXPORT void release(key_t key);
IPC_EXPORT bool set(key_t key, void* ptr); IPC_EXPORT bool set(key_t key, void* ptr);
IPC_EXPORT void* get(key_t key); IPC_EXPORT void* get(key_t key);
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// Thread-local pointer /// Thread-local pointer
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/* /*
<Remarks> * <Remarks>
*
1. In Windows, if you do not compile thread_local_ptr.cpp, * 1. In Windows, if you do not compile thread_local_ptr.cpp,
use thread_local_ptr will cause memory leaks. * use thread_local_ptr will cause memory leaks.
*
2. You need to set the thread_local_ptr's storage manually: * 2. You need to set the thread_local_ptr's storage manually:
``` * ```
thread_local_ptr<int> p; * tls::pointer<int> p;
if (!p) p = new int(123); * if (!p) p = new int(123);
``` * ```
Just like an ordinary pointer. Or you could just call create: * Just like an ordinary pointer. Or you could just call create:
``` * ```
thread_local_ptr<int> p; * tls::pointer<int> p;
p.create(123); * p.create(123);
``` * ```
*/ */
template <typename T> template <typename T>
class pointer { class pointer {
key_t key_; key_t key_;
public: public:
using value_type = T; using value_type = T;
pointer() { pointer()
key_ = tls::create([](void* p) { delete static_cast<T*>(p); }); : key_(tls::create([](void* p) { delete static_cast<T*>(p); })) {
} }
~pointer() { ~pointer() {
tls::release(key_); tls::release(key_);
} }
template <typename... P> template <typename... P>
T* create(P&&... params) { T* create(P&&... params) {
thread_local auto ptr = static_cast<T*>(*this); thread_local auto ptr = static_cast<T*>(get(key_));
if (ptr == nullptr) { if (ptr == nullptr) {
return ptr = (*this) = new T(std::forward<P>(params)...); ptr = new T(std::forward<P>(params)...);
} if (!set(key_, ptr)) {
return ptr; delete ptr;
} return nullptr;
}
T* operator=(T* ptr) { }
set(key_, ptr); return ptr;
return ptr; }
}
T* operator=(T* ptr) {
operator T*() const { return static_cast<T*>(get(key_)); } set(key_, ptr);
return ptr;
T& operator* () { return *static_cast<T*>(*this); } }
const T& operator* () const { return *static_cast<T*>(*this); }
operator T*() const { return static_cast<T*>(get(key_)); }
T* operator->() { return static_cast<T*>(*this); }
const T* operator->() const { return static_cast<T*>(*this); } T& operator* () { return *static_cast<T*>(*this); }
}; const T& operator* () const { return *static_cast<T*>(*this); }
} // namespace tls T* operator->() { return static_cast<T*>(*this); }
} // namespace ipc const T* operator->() const { return static_cast<T*>(*this); }
};
} // namespace tls
} // namespace ipc

View File

@ -1,93 +1,93 @@
#pragma once #pragma once
#include "export.h" #include "export.h"
#include "def.h" #include "def.h"
namespace ipc { namespace ipc {
class condition; class condition;
class IPC_EXPORT mutex { class IPC_EXPORT mutex {
public: public:
mutex(); mutex();
explicit mutex(char const * name); explicit mutex(char const * name);
mutex(mutex&& rhs); mutex(mutex&& rhs);
~mutex(); ~mutex();
static void remove(char const * name); static void remove(char const * name);
void swap(mutex& rhs); void swap(mutex& rhs);
mutex& operator=(mutex rhs); mutex& operator=(mutex rhs);
bool valid() const; bool valid() const;
char const * name () const; char const * name () const;
bool open (char const * name); bool open (char const * name);
void close(); void close();
bool lock (); bool lock ();
bool unlock(); bool unlock();
private: private:
class mutex_; class mutex_;
mutex_* p_; mutex_* p_;
friend class condition; friend class condition;
}; };
class IPC_EXPORT semaphore { class IPC_EXPORT semaphore {
public: public:
semaphore(); semaphore();
explicit semaphore(char const * name); explicit semaphore(char const * name);
semaphore(semaphore&& rhs); semaphore(semaphore&& rhs);
~semaphore(); ~semaphore();
static void remove(char const * name); static void remove(char const * name);
void swap(semaphore& rhs); void swap(semaphore& rhs);
semaphore& operator=(semaphore rhs); semaphore& operator=(semaphore rhs);
bool valid() const; bool valid() const;
char const * name () const; char const * name () const;
bool open (char const * name, long count = 0); bool open (char const * name, long count = 0);
void close(); void close();
bool wait(std::size_t tm = invalid_value); bool wait(std::size_t tm = invalid_value);
bool post(long count = 1); bool post(long count = 1);
private: private:
class semaphore_; class semaphore_;
semaphore_* p_; semaphore_* p_;
}; };
class IPC_EXPORT condition { class IPC_EXPORT condition {
public: public:
condition(); condition();
explicit condition(char const * name); explicit condition(char const * name);
condition(condition&& rhs); condition(condition&& rhs);
~condition(); ~condition();
static void remove(char const * name); static void remove(char const * name);
void swap(condition& rhs); void swap(condition& rhs);
condition& operator=(condition rhs); condition& operator=(condition rhs);
bool valid() const; bool valid() const;
char const * name () const; char const * name () const;
bool open (char const * name); bool open (char const * name);
void close(); void close();
bool wait(mutex&, std::size_t tm = invalid_value); bool wait(mutex&, std::size_t tm = invalid_value);
bool notify(); bool notify();
bool broadcast(); bool broadcast();
private: private:
class condition_; class condition_;
condition_* p_; condition_* p_;
}; };
} // namespace ipc } // namespace ipc

View File

@ -1,83 +1,83 @@
#include "buffer.h" #include "buffer.h"
#include "pimpl.h" #include "pimpl.h"
#include <cstring> #include <cstring>
namespace ipc { namespace ipc {
bool operator==(buffer const & b1, buffer const & b2) { bool operator==(buffer const & b1, buffer const & b2) {
return (b1.size() == b2.size()) && (std::memcmp(b1.data(), b2.data(), b1.size()) == 0); return (b1.size() == b2.size()) && (std::memcmp(b1.data(), b2.data(), b1.size()) == 0);
} }
class buffer::buffer_ : public pimpl<buffer_> { class buffer::buffer_ : public pimpl<buffer_> {
public: public:
void* p_; void* p_;
std::size_t s_; std::size_t s_;
void* a_; void* a_;
buffer::destructor_t d_; buffer::destructor_t d_;
buffer_(void* p, std::size_t s, buffer::destructor_t d, void* a) buffer_(void* p, std::size_t s, buffer::destructor_t d, void* a)
: p_(p), s_(s), a_(a), d_(d) { : p_(p), s_(s), a_(a), d_(d) {
} }
~buffer_() { ~buffer_() {
if (d_ == nullptr) return; if (d_ == nullptr) return;
d_((a_ == nullptr) ? p_ : a_, s_); d_((a_ == nullptr) ? p_ : a_, s_);
} }
}; };
buffer::buffer() buffer::buffer()
: buffer(nullptr, 0, nullptr, nullptr) { : buffer(nullptr, 0, nullptr, nullptr) {
} }
buffer::buffer(void* p, std::size_t s, destructor_t d) buffer::buffer(void* p, std::size_t s, destructor_t d)
: p_(p_->make(p, s, d, nullptr)) { : p_(p_->make(p, s, d, nullptr)) {
} }
buffer::buffer(void* p, std::size_t s, destructor_t d, void* additional) buffer::buffer(void* p, std::size_t s, destructor_t d, void* additional)
: p_(p_->make(p, s, d, additional)) { : p_(p_->make(p, s, d, additional)) {
} }
buffer::buffer(void* p, std::size_t s) buffer::buffer(void* p, std::size_t s)
: buffer(p, s, nullptr) { : buffer(p, s, nullptr) {
} }
buffer::buffer(char const & c) buffer::buffer(char const & c)
: buffer(const_cast<char*>(&c), 1) { : buffer(const_cast<char*>(&c), 1) {
} }
buffer::buffer(buffer&& rhs) buffer::buffer(buffer&& rhs)
: buffer() { : buffer() {
swap(rhs); swap(rhs);
} }
buffer::~buffer() { buffer::~buffer() {
p_->clear(); p_->clear();
} }
void buffer::swap(buffer& rhs) { void buffer::swap(buffer& rhs) {
std::swap(p_, rhs.p_); std::swap(p_, rhs.p_);
} }
buffer& buffer::operator=(buffer rhs) { buffer& buffer::operator=(buffer rhs) {
swap(rhs); swap(rhs);
return *this; return *this;
} }
bool buffer::empty() const noexcept { bool buffer::empty() const noexcept {
return (impl(p_)->p_ == nullptr) || (impl(p_)->s_ == 0); return (impl(p_)->p_ == nullptr) || (impl(p_)->s_ == 0);
} }
void* buffer::data() noexcept { void* buffer::data() noexcept {
return impl(p_)->p_; return impl(p_)->p_;
} }
void const * buffer::data() const noexcept { void const * buffer::data() const noexcept {
return impl(p_)->p_; return impl(p_)->p_;
} }
std::size_t buffer::size() const noexcept { std::size_t buffer::size() const noexcept {
return impl(p_)->s_; return impl(p_)->s_;
} }
} // namespace ipc } // namespace ipc

View File

@ -1,60 +1,60 @@
#pragma once #pragma once
#include <limits> #include <limits>
#include <utility> #include <utility>
#include <type_traits> #include <type_traits>
#include "def.h" #include "def.h"
#include "circ/elem_def.h" #include "circ/elem_def.h"
#include "platform/detail.h" #include "platform/detail.h"
namespace ipc { namespace ipc {
namespace circ { namespace circ {
template <typename Policy, template <typename Policy,
std::size_t DataSize, std::size_t DataSize,
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))> std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))>
class elem_array : public ipc::circ::conn_head { class elem_array : public ipc::circ::conn_head {
public: public:
using base_t = ipc::circ::conn_head; using base_t = ipc::circ::conn_head;
using policy_t = Policy; using policy_t = Policy;
using cursor_t = decltype(std::declval<policy_t>().cursor()); using cursor_t = decltype(std::declval<policy_t>().cursor());
using elem_t = typename policy_t::template elem_t<DataSize, AlignSize>; using elem_t = typename policy_t::template elem_t<DataSize, AlignSize>;
enum : std::size_t { enum : std::size_t {
head_size = sizeof(base_t) + sizeof(policy_t), head_size = sizeof(base_t) + sizeof(policy_t),
data_size = DataSize, data_size = DataSize,
elem_max = (std::numeric_limits<uint_t<8>>::max)() + 1, // default is 255 + 1 elem_max = (std::numeric_limits<uint_t<8>>::max)() + 1, // default is 255 + 1
elem_size = sizeof(elem_t), elem_size = sizeof(elem_t),
block_size = elem_size * elem_max block_size = elem_size * elem_max
}; };
private: private:
policy_t head_; policy_t head_;
elem_t block_[elem_max] {}; elem_t block_[elem_max] {};
public: public:
cursor_t cursor() const noexcept { cursor_t cursor() const noexcept {
return head_.cursor(); return head_.cursor();
} }
template <typename F> template <typename F>
bool push(F&& f) { bool push(F&& f) {
return head_.push(this, std::forward<F>(f), block_); return head_.push(this, std::forward<F>(f), block_);
} }
template <typename F> template <typename F>
bool force_push(F&& f) { bool force_push(F&& f) {
return head_.force_push(this, std::forward<F>(f), block_); return head_.force_push(this, std::forward<F>(f), block_);
} }
template <typename F> template <typename F>
bool pop(cursor_t* cur, F&& f) { bool pop(cursor_t* cur, F&& f) {
if (cur == nullptr) return false; if (cur == nullptr) return false;
return head_.pop(this, *cur, std::forward<F>(f), block_); return head_.pop(this, *cur, std::forward<F>(f), block_);
} }
}; };
} // namespace circ } // namespace circ
} // namespace ipc } // namespace ipc

View File

@ -1,75 +1,75 @@
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <new> #include <new>
#include "rw_lock.h" #include "rw_lock.h"
#include "platform/detail.h" #include "platform/detail.h"
namespace ipc { namespace ipc {
namespace circ { namespace circ {
using u1_t = ipc::uint_t<8>; using u1_t = ipc::uint_t<8>;
using u2_t = ipc::uint_t<32>; using u2_t = ipc::uint_t<32>;
constexpr u1_t index_of(u2_t c) noexcept { constexpr u1_t index_of(u2_t c) noexcept {
return static_cast<u1_t>(c); return static_cast<u1_t>(c);
} }
class conn_head { class conn_head {
std::atomic<std::size_t> cc_ { 0 }; // connection counter std::atomic<u2_t> cc_ { 0 }; // connection counter
ipc::spin_lock lc_; ipc::spin_lock lc_;
std::atomic<bool> constructed_; std::atomic<bool> constructed_;
std::atomic<bool> dis_flag_; std::atomic<bool> dis_flag_;
public: public:
void init() { void init() {
/* DCLP */ /* DCLP */
if (!constructed_.load(std::memory_order_acquire)) { if (!constructed_.load(std::memory_order_acquire)) {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_);
if (!constructed_.load(std::memory_order_relaxed)) { if (!constructed_.load(std::memory_order_relaxed)) {
::new (this) conn_head; ::new (this) conn_head;
constructed_.store(true, std::memory_order_release); constructed_.store(true, std::memory_order_release);
} }
} }
} }
conn_head() = default; conn_head() = default;
conn_head(const conn_head&) = delete; conn_head(const conn_head&) = delete;
conn_head& operator=(const conn_head&) = delete; conn_head& operator=(const conn_head&) = delete;
std::size_t connect() noexcept { std::size_t connect() noexcept {
return cc_.fetch_add(1, std::memory_order_acq_rel); return cc_.fetch_add(1, std::memory_order_acq_rel);
} }
std::size_t disconnect() noexcept { std::size_t disconnect() noexcept {
return cc_.fetch_sub(1, std::memory_order_acq_rel); return cc_.fetch_sub(1, std::memory_order_acq_rel);
} }
void try_disconnect() noexcept { void try_disconnect() noexcept {
if (!dis_flag_.load(std::memory_order_acquire)) { if (!dis_flag_.load(std::memory_order_acquire)) {
cc_.fetch_sub(1, std::memory_order_relaxed); cc_.fetch_sub(1, std::memory_order_relaxed);
dis_flag_.store(true, std::memory_order_release); dis_flag_.store(true, std::memory_order_release);
} }
} }
void clear_dis_flag(std::memory_order order = std::memory_order_release) noexcept { void clear_dis_flag(std::memory_order order = std::memory_order_release) noexcept {
dis_flag_.store(false, order); dis_flag_.store(false, order);
} }
bool dis_flag(std::memory_order order = std::memory_order_acquire) const noexcept { bool dis_flag(std::memory_order order = std::memory_order_acquire) const noexcept {
return dis_flag_.load(order); return dis_flag_.load(order);
} }
std::size_t conn_count(std::memory_order order = std::memory_order_acquire) const noexcept { std::size_t conn_count(std::memory_order order = std::memory_order_acquire) const noexcept {
return cc_.load(order); return cc_.load(order);
} }
}; };
} // namespace circ } // namespace circ
} // namespace ipc } // namespace ipc

View File

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

View File

@ -1,485 +1,485 @@
#include "ipc.h" #include "ipc.h"
#include <type_traits> #include <type_traits>
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <atomic> #include <atomic>
#include <type_traits> #include <type_traits>
#include <string> #include <string>
#include <vector> #include <vector>
#include "def.h" #include "def.h"
#include "shm.h" #include "shm.h"
#include "tls_pointer.h" #include "tls_pointer.h"
#include "pool_alloc.h" #include "pool_alloc.h"
#include "queue.h" #include "queue.h"
#include "policy.h" #include "policy.h"
#include "rw_lock.h" #include "rw_lock.h"
#include "log.h" #include "log.h"
#include "memory/resource.h" #include "memory/resource.h"
#include "platform/detail.h" #include "platform/detail.h"
#include "platform/waiter_wrapper.h" #include "platform/waiter_wrapper.h"
#include "circ/elem_array.h" #include "circ/elem_array.h"
namespace { namespace {
using namespace ipc; using namespace ipc;
using msg_id_t = std::size_t; using msg_id_t = std::uint32_t;
template <std::size_t DataSize, std::size_t AlignSize> template <std::size_t DataSize, std::size_t AlignSize>
struct msg_t; struct msg_t;
template <std::size_t AlignSize> template <std::size_t AlignSize>
struct msg_t<0, AlignSize> { struct msg_t<0, AlignSize> {
msg_id_t conn_; msg_id_t conn_;
msg_id_t id_; msg_id_t id_;
int remain_; std::int32_t remain_;
bool storage_; bool storage_;
}; };
template <std::size_t DataSize, std::size_t AlignSize> template <std::size_t DataSize, std::size_t AlignSize>
struct msg_t : msg_t<0, AlignSize> { struct msg_t : msg_t<0, AlignSize> {
std::aligned_storage_t<DataSize, AlignSize> data_ {}; std::aligned_storage_t<DataSize, AlignSize> data_ {};
msg_t() = default; msg_t() = default;
msg_t(msg_id_t c, msg_id_t i, int r, void const * d, std::size_t s) msg_t(msg_id_t c, msg_id_t i, std::int32_t r, void const * d, std::size_t s)
: msg_t<0, AlignSize> { c, i, r, (d == nullptr) || (s == 0) } { : msg_t<0, AlignSize> { c, i, r, (d == nullptr) || (s == 0) } {
if (!this->storage_) { if (!this->storage_) {
std::memcpy(&data_, d, s); std::memcpy(&data_, d, s);
} }
} }
}; };
template <typename T> template <typename T>
buff_t make_cache(T& data, std::size_t size) { buff_t make_cache(T& data, std::size_t size) {
auto ptr = mem::alloc(size); auto ptr = mem::alloc(size);
std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size)); std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size));
return { ptr, size, mem::free }; return { ptr, size, mem::free };
} }
struct cache_t { struct cache_t {
std::size_t fill_; std::size_t fill_;
buff_t buff_; buff_t buff_;
cache_t(std::size_t f, buff_t&& b) cache_t(std::size_t f, buff_t&& b)
: fill_(f), buff_(std::move(b)) : fill_(f), buff_(std::move(b))
{} {}
void append(void const * data, std::size_t size) { void append(void const * data, std::size_t size) {
if (fill_ >= buff_.size() || data == nullptr || size == 0) return; if (fill_ >= buff_.size() || data == nullptr || size == 0) return;
auto new_fill = (ipc::detail::min)(fill_ + size, buff_.size()); auto new_fill = (ipc::detail::min)(fill_ + size, buff_.size());
std::memcpy(static_cast<byte_t*>(buff_.data()) + fill_, data, new_fill - fill_); std::memcpy(static_cast<byte_t*>(buff_.data()) + fill_, data, new_fill - fill_);
fill_ = new_fill; fill_ = new_fill;
} }
}; };
struct conn_info_head { struct conn_info_head {
using acc_t = std::atomic<msg_id_t>; using acc_t = std::atomic<msg_id_t>;
static auto cc_acc() { static auto cc_acc() {
static shm::handle acc_h("__CA_CONN__", sizeof(acc_t)); static shm::handle acc_h("__CA_CONN__", sizeof(acc_t));
return static_cast<acc_t*>(acc_h.get()); return static_cast<acc_t*>(acc_h.get());
} }
ipc::string name_; ipc::string name_;
msg_id_t cc_id_; // connection-info id msg_id_t cc_id_; // connection-info id
waiter cc_waiter_, wt_waiter_, rd_waiter_; waiter cc_waiter_, wt_waiter_, rd_waiter_;
shm::handle acc_h_; shm::handle acc_h_;
/* /*
* <Remarks> thread_local may have some bugs. * <Remarks> thread_local may have some bugs.
* *
* <Reference> * <Reference>
* - https://sourceforge.net/p/mingw-w64/bugs/727/ * - https://sourceforge.net/p/mingw-w64/bugs/727/
* - https://sourceforge.net/p/mingw-w64/bugs/527/ * - https://sourceforge.net/p/mingw-w64/bugs/527/
* - https://github.com/Alexpux/MINGW-packages/issues/2519 * - https://github.com/Alexpux/MINGW-packages/issues/2519
* - https://github.com/ChaiScript/ChaiScript/issues/402 * - https://github.com/ChaiScript/ChaiScript/issues/402
* - https://developercommunity.visualstudio.com/content/problem/124121/thread-local-variables-fail-to-be-initialized-when.html * - https://developercommunity.visualstudio.com/content/problem/124121/thread-local-variables-fail-to-be-initialized-when.html
* - https://software.intel.com/en-us/forums/intel-c-compiler/topic/684827 * - https://software.intel.com/en-us/forums/intel-c-compiler/topic/684827
*/ */
tls::pointer<ipc::unordered_map<msg_id_t, cache_t>> recv_cache_; tls::pointer<ipc::unordered_map<msg_id_t, cache_t>> recv_cache_;
struct simple_push { struct simple_push {
template <std::size_t, std::size_t> template <std::size_t, std::size_t>
using elem_t = shm::id_t; using elem_t = shm::id_t;
circ::u2_t wt_; // write index circ::u2_t wt_; // write index
constexpr circ::u2_t cursor() const noexcept { constexpr circ::u2_t cursor() const noexcept {
return 0; return 0;
} }
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool push(W* /*wrapper*/, F&& f, E* elems) { bool push(W* /*wrapper*/, F&& f, E* elems) {
std::forward<F>(f)(&(elems[circ::index_of(wt_)])); std::forward<F>(f)(&(elems[circ::index_of(wt_)]));
++ wt_; ++ wt_;
return true; return true;
} }
}; };
circ::elem_array<simple_push, sizeof(shm::id_t)> msg_datas_; circ::elem_array<simple_push, sizeof(shm::id_t)> msg_datas_;
conn_info_head(char const * name) conn_info_head(char const * name)
: name_ (name) : name_ (name)
, cc_id_ ((cc_acc() == nullptr) ? 0 : cc_acc()->fetch_add(1, std::memory_order_relaxed)) , cc_id_ ((cc_acc() == nullptr) ? 0 : cc_acc()->fetch_add(1, std::memory_order_relaxed))
, cc_waiter_(("__CC_CONN__" + name_).c_str()) , cc_waiter_(("__CC_CONN__" + name_).c_str())
, wt_waiter_(("__WT_CONN__" + name_).c_str()) , wt_waiter_(("__WT_CONN__" + name_).c_str())
, rd_waiter_(("__RD_CONN__" + name_).c_str()) , rd_waiter_(("__RD_CONN__" + name_).c_str())
, acc_h_ (("__AC_CONN__" + name_).c_str(), sizeof(acc_t)) { , acc_h_ (("__AC_CONN__" + name_).c_str(), sizeof(acc_t)) {
} }
auto acc() { auto acc() {
return static_cast<acc_t*>(acc_h_.get()); return static_cast<acc_t*>(acc_h_.get());
} }
auto& recv_cache() { auto& recv_cache() {
return *recv_cache_.create(); return *recv_cache_.create();
} }
shm::id_t apply_storage(msg_id_t msg_id, std::size_t size) { shm::id_t apply_storage(msg_id_t msg_id, std::size_t size) {
return shm::acquire( return shm::acquire(
("__ST_CONN__" + ipc::to_string(cc_id_) + ("__ST_CONN__" + ipc::to_string(cc_id_) +
"__" + ipc::to_string(msg_id)).c_str(), size, shm::create); "__" + ipc::to_string(msg_id)).c_str(), size, shm::create);
} }
static shm::id_t acquire_storage(msg_id_t cc_id, msg_id_t msg_id) { static shm::id_t acquire_storage(msg_id_t cc_id, msg_id_t msg_id) {
return shm::acquire( return shm::acquire(
("__ST_CONN__" + ipc::to_string(cc_id) + ("__ST_CONN__" + ipc::to_string(cc_id) +
"__" + ipc::to_string(msg_id)).c_str(), 0, shm::open); "__" + ipc::to_string(msg_id)).c_str(), 0, shm::open);
} }
void store(shm::id_t dat) { void store(shm::id_t dat) {
msg_datas_.push([dat](shm::id_t * id) { msg_datas_.push([dat](shm::id_t * id) {
(*id) = dat; (*id) = dat;
}); });
} }
void clear_store() { void clear_store() {
msg_datas_.push([](shm::id_t * id) { msg_datas_.push([](shm::id_t * id) {
if (*id == nullptr) return; if (*id == nullptr) return;
shm::remove(*id); shm::remove(*id);
(*id) = nullptr; (*id) = nullptr;
}); });
} }
}; };
template <typename W, typename F> template <typename W, typename F>
bool wait_for(W& waiter, F&& pred, std::size_t tm) { bool wait_for(W& waiter, F&& pred, std::size_t tm) {
if (tm == 0) return !pred(); if (tm == 0) return !pred();
for (unsigned k = 0; pred();) { for (unsigned k = 0; pred();) {
bool loop = true, ret = true; bool loop = true, ret = true;
ipc::sleep(k, [&k, &loop, &ret, &waiter, &pred, tm] { ipc::sleep(k, [&k, &loop, &ret, &waiter, &pred, tm] {
ret = waiter.wait_if([&loop, &pred] { ret = waiter.wait_if([&loop, &pred] {
return loop = pred(); return loop = pred();
}, tm); }, tm);
k = 0; k = 0;
return true; return true;
}); });
if (!ret ) return false; // timeout or fail if (!ret ) return false; // timeout or fail
if (!loop) break; if (!loop) break;
} }
return true; return true;
} }
template <typename Policy, template <typename Policy,
std::size_t DataSize = data_length, std::size_t DataSize = data_length,
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))> std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))>
struct queue_generator { struct queue_generator {
using queue_t = ipc::queue<msg_t<DataSize, AlignSize>, Policy>; using queue_t = ipc::queue<msg_t<DataSize, AlignSize>, Policy>;
struct conn_info_t : conn_info_head { struct conn_info_t : conn_info_head {
queue_t que_; queue_t que_;
conn_info_t(char const * name) conn_info_t(char const * name)
: conn_info_head(name) : conn_info_head(name)
, que_(("__QU_CONN__" + , que_(("__QU_CONN__" +
ipc::to_string(DataSize) + "__" + ipc::to_string(DataSize) + "__" +
ipc::to_string(AlignSize) + "__" + name).c_str()) { ipc::to_string(AlignSize) + "__" + name).c_str()) {
} }
}; };
}; };
template <typename Policy> template <typename Policy>
struct detail_impl { struct detail_impl {
using queue_t = typename queue_generator<Policy>::queue_t; using queue_t = typename queue_generator<Policy>::queue_t;
using conn_info_t = typename queue_generator<Policy>::conn_info_t; using conn_info_t = typename queue_generator<Policy>::conn_info_t;
constexpr static conn_info_t* info_of(ipc::handle_t h) { constexpr static conn_info_t* info_of(ipc::handle_t h) {
return static_cast<conn_info_t*>(h); return static_cast<conn_info_t*>(h);
} }
constexpr static queue_t* queue_of(ipc::handle_t h) { constexpr static queue_t* queue_of(ipc::handle_t h) {
return (info_of(h) == nullptr) ? nullptr : &(info_of(h)->que_); return (info_of(h) == nullptr) ? nullptr : &(info_of(h)->que_);
} }
/* API implementations */ /* API implementations */
static ipc::handle_t connect(char const * name, bool start) { static ipc::handle_t connect(char const * name, bool start) {
auto h = mem::alloc<conn_info_t>(name); auto h = mem::alloc<conn_info_t>(name);
auto que = queue_of(h); auto que = queue_of(h);
if (que == nullptr) { if (que == nullptr) {
return nullptr; return nullptr;
} }
if (start) { if (start) {
if (que->connect()) { // wouldn't connect twice if (que->connect()) { // wouldn't connect twice
info_of(h)->cc_waiter_.broadcast(); info_of(h)->cc_waiter_.broadcast();
} }
} }
return h; return h;
} }
static void disconnect(ipc::handle_t h) { static void disconnect(ipc::handle_t h) {
auto que = queue_of(h); auto que = queue_of(h);
if (que == nullptr) { if (que == nullptr) {
return; return;
} }
if (que->disconnect()) { if (que->disconnect()) {
info_of(h)->cc_waiter_.broadcast(); info_of(h)->cc_waiter_.broadcast();
} }
mem::free(info_of(h)); mem::free(info_of(h));
} }
static std::size_t recv_count(ipc::handle_t h) { static std::size_t recv_count(ipc::handle_t h) {
auto que = queue_of(h); auto que = queue_of(h);
if (que == nullptr) { if (que == nullptr) {
return invalid_value; return invalid_value;
} }
return que->conn_count(); return que->conn_count();
} }
static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) { static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) {
auto que = queue_of(h); auto que = queue_of(h);
if (que == nullptr) { if (que == nullptr) {
return false; return false;
} }
return wait_for(info_of(h)->cc_waiter_, [que, r_count] { return wait_for(info_of(h)->cc_waiter_, [que, r_count] {
return que->conn_count() < r_count; return que->conn_count() < r_count;
}, tm); }, tm);
} }
template <typename F> template <typename F>
static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t size) { static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t size) {
if (data == nullptr || size == 0) { if (data == nullptr || size == 0) {
ipc::error("fail: send(%p, %zd)\n", data, size); ipc::error("fail: send(%p, %zd)\n", data, size);
return false; return false;
} }
auto que = queue_of(h); auto que = queue_of(h);
if (que == nullptr) { if (que == nullptr) {
ipc::error("fail: send, queue_of(h) == nullptr\n"); ipc::error("fail: send, queue_of(h) == nullptr\n");
return false; return false;
} }
// calc a new message id // calc a new message id
auto acc = info_of(h)->acc(); auto acc = info_of(h)->acc();
if (acc == nullptr) { if (acc == nullptr) {
ipc::error("fail: send, info_of(h)->acc() == nullptr\n"); ipc::error("fail: send, info_of(h)->acc() == nullptr\n");
return false; return false;
} }
auto msg_id = acc->fetch_add(1, std::memory_order_relaxed); auto msg_id = acc->fetch_add(1, std::memory_order_relaxed);
auto try_push = std::forward<F>(gen_push)(info_of(h), que, msg_id); auto try_push = std::forward<F>(gen_push)(info_of(h), que, msg_id);
if (size > small_msg_limit) { if (size > small_msg_limit) {
auto dat = info_of(h)->apply_storage(msg_id, size); auto dat = info_of(h)->apply_storage(msg_id, size);
void * buf = shm::get_mem(dat, nullptr); void * buf = shm::get_mem(dat, nullptr);
if (buf != nullptr) { if (buf != nullptr) {
std::memcpy(buf, data, size); std::memcpy(buf, data, size);
info_of(h)->store(dat); info_of(h)->store(dat);
return try_push(static_cast<int>(size) - static_cast<int>(data_length), nullptr, 0); return try_push(static_cast<int>(size) - static_cast<int>(data_length), nullptr, 0);
} }
// try using message fragment // try using message fragment
ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd\n", msg_id, size); ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd\n", msg_id, size);
} }
// push message fragment // push message fragment
int offset = 0; int offset = 0;
for (int i = 0; i < static_cast<int>(size / data_length); ++i, offset += data_length) { for (int i = 0; i < static_cast<int>(size / data_length); ++i, offset += data_length) {
if (!try_push(static_cast<int>(size) - offset - static_cast<int>(data_length), if (!try_push(static_cast<int>(size) - offset - static_cast<int>(data_length),
static_cast<byte_t const *>(data) + offset, data_length)) { static_cast<byte_t const *>(data) + offset, data_length)) {
return false; return false;
} }
info_of(h)->clear_store(); info_of(h)->clear_store();
} }
// if remain > 0, this is the last message fragment // if remain > 0, this is the last message fragment
int remain = static_cast<int>(size) - offset; int remain = static_cast<int>(size) - offset;
if (remain > 0) { if (remain > 0) {
if (!try_push(remain - static_cast<int>(data_length), if (!try_push(remain - static_cast<int>(data_length),
static_cast<byte_t const *>(data) + offset, static_cast<std::size_t>(remain))) { static_cast<byte_t const *>(data) + offset, static_cast<std::size_t>(remain))) {
return false; return false;
} }
info_of(h)->clear_store(); info_of(h)->clear_store();
} }
return true; return true;
} }
static bool send(ipc::handle_t h, void const * data, std::size_t size) { static bool send(ipc::handle_t h, void const * data, std::size_t size) {
return send([](auto info, auto que, auto msg_id) { return send([](auto info, auto que, auto msg_id) {
return [info, que, msg_id](int remain, void const * data, std::size_t size) { return [info, que, msg_id](int remain, void const * data, std::size_t size) {
if (!wait_for(info->wt_waiter_, [&] { if (!wait_for(info->wt_waiter_, [&] {
return !que->push(info->cc_id_, msg_id, remain, data, size); return !que->push(info->cc_id_, msg_id, remain, data, size);
}, que->dis_flag() ? 0 : static_cast<std::size_t>(default_timeut))) { }, que->dis_flag() ? 0 : static_cast<std::size_t>(default_timeut))) {
ipc::log("force_push: msg_id = %zd, remain = %d, size = %zd\n", msg_id, remain, size); ipc::log("force_push: msg_id = %zd, remain = %d, size = %zd\n", msg_id, remain, size);
if (!que->force_push(info->cc_id_, msg_id, remain, data, size)) { if (!que->force_push(info->cc_id_, msg_id, remain, data, size)) {
return false; return false;
} }
} }
info->rd_waiter_.broadcast(); info->rd_waiter_.broadcast();
return true; return true;
}; };
}, h, data, size); }, h, data, size);
} }
static bool try_send(ipc::handle_t h, void const * data, std::size_t size) { static bool try_send(ipc::handle_t h, void const * data, std::size_t size) {
return send([](auto info, auto que, auto msg_id) { return send([](auto info, auto que, auto msg_id) {
return [info, que, msg_id](int remain, void const * data, std::size_t size) { return [info, que, msg_id](int remain, void const * data, std::size_t size) {
if (!wait_for(info->wt_waiter_, [&] { if (!wait_for(info->wt_waiter_, [&] {
return !que->push(info->cc_id_, msg_id, remain, data, size); return !que->push(info->cc_id_, msg_id, remain, data, size);
}, 0)) { }, 0)) {
return false; return false;
} }
info->rd_waiter_.broadcast(); info->rd_waiter_.broadcast();
return true; return true;
}; };
}, h, data, size); }, h, data, size);
} }
static buff_t recv(ipc::handle_t h, std::size_t tm) { static buff_t recv(ipc::handle_t h, std::size_t tm) {
auto que = queue_of(h); auto que = queue_of(h);
if (que == nullptr) { if (que == nullptr) {
ipc::error("fail: recv, queue_of(h) == nullptr\n"); ipc::error("fail: recv, queue_of(h) == nullptr\n");
return {}; return {};
} }
if (que->connect()) { // wouldn't connect twice if (que->connect()) { // wouldn't connect twice
info_of(h)->cc_waiter_.broadcast(); info_of(h)->cc_waiter_.broadcast();
} }
auto& rc = info_of(h)->recv_cache(); auto& rc = info_of(h)->recv_cache();
while (1) { while (1) {
// pop a new message // pop a new message
typename queue_t::value_t msg; typename queue_t::value_t msg;
if (!wait_for(info_of(h)->rd_waiter_, [que, &msg] { return !que->pop(msg); }, tm)) { if (!wait_for(info_of(h)->rd_waiter_, [que, &msg] { return !que->pop(msg); }, tm)) {
return {}; return {};
} }
info_of(h)->wt_waiter_.broadcast(); info_of(h)->wt_waiter_.broadcast();
if ((info_of(h)->acc() != nullptr) && (msg.conn_ == info_of(h)->cc_id_)) { if ((info_of(h)->acc() != nullptr) && (msg.conn_ == info_of(h)->cc_id_)) {
continue; // ignore message to self continue; // ignore message to self
} }
// msg.remain_ may minus & abs(msg.remain_) < data_length // msg.remain_ may minus & abs(msg.remain_) < data_length
auto remain = static_cast<std::size_t>(static_cast<int>(data_length) + msg.remain_); auto remain = static_cast<std::size_t>(static_cast<std::int32_t>(data_length) + msg.remain_);
// find cache with msg.id_ // find cache with msg.id_
auto cac_it = rc.find(msg.id_); auto cac_it = rc.find(msg.id_);
if (cac_it == rc.end()) { if (cac_it == rc.end()) {
if (remain <= data_length) { if (remain <= data_length) {
return make_cache(msg.data_, remain); return make_cache(msg.data_, remain);
} }
if (msg.storage_) { if (msg.storage_) {
auto dat = info_of(h)->acquire_storage(msg.conn_, msg.id_); auto dat = info_of(h)->acquire_storage(msg.conn_, msg.id_);
std::size_t dat_sz = 0; std::size_t dat_sz = 0;
void * buf = shm::get_mem(dat, &dat_sz); void * buf = shm::get_mem(dat, &dat_sz);
if (buf != nullptr && remain <= dat_sz) { if (buf != nullptr && remain <= dat_sz) {
return buff_t { buf, remain, [](void * dat, std::size_t) { return buff_t { buf, remain, [](void * dat, std::size_t) {
shm::release(dat); shm::release(dat);
}, dat }; }, dat };
} }
else ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd, shm.size: %zd\n", else ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd, shm.size: %zd\n",
msg.id_, remain, dat_sz); msg.id_, remain, dat_sz);
} }
// gc // gc
if (rc.size() > 1024) { if (rc.size() > 1024) {
std::vector<msg_id_t> need_del; std::vector<msg_id_t> need_del;
for (auto const & pair : rc) { for (auto const & pair : rc) {
auto cmp = std::minmax(msg.id_, pair.first); auto cmp = std::minmax(msg.id_, pair.first);
if (cmp.second - cmp.first > 8192) { if (cmp.second - cmp.first > 8192) {
need_del.push_back(pair.first); need_del.push_back(pair.first);
} }
} }
for (auto id : need_del) rc.erase(id); for (auto id : need_del) rc.erase(id);
} }
// cache the first message fragment // cache the first message fragment
rc.emplace(msg.id_, cache_t { data_length, make_cache(msg.data_, remain) }); rc.emplace(msg.id_, cache_t { data_length, make_cache(msg.data_, remain) });
} }
// has cached before this message // has cached before this message
else { else {
auto& cac = cac_it->second; auto& cac = cac_it->second;
// this is the last message fragment // this is the last message fragment
if (msg.remain_ <= 0) { if (msg.remain_ <= 0) {
cac.append(&(msg.data_), remain); cac.append(&(msg.data_), remain);
// finish this message, erase it from cache // finish this message, erase it from cache
auto buff = std::move(cac.buff_); auto buff = std::move(cac.buff_);
rc.erase(cac_it); rc.erase(cac_it);
return buff; return buff;
} }
// there are remain datas after this message // there are remain datas after this message
cac.append(&(msg.data_), data_length); cac.append(&(msg.data_), data_length);
} }
} }
} }
static buff_t try_recv(ipc::handle_t h) { static buff_t try_recv(ipc::handle_t h) {
return recv(h, 0); return recv(h, 0);
} }
}; // detail_impl<Policy> }; // detail_impl<Policy>
template <typename Flag> template <typename Flag>
using policy_t = policy::choose<circ::elem_array, Flag>; using policy_t = policy::choose<circ::elem_array, Flag>;
} // internal-linkage } // internal-linkage
namespace ipc { namespace ipc {
template <typename Flag> template <typename Flag>
ipc::handle_t chan_impl<Flag>::connect(char const * name, unsigned mode) { ipc::handle_t chan_impl<Flag>::connect(char const * name, unsigned mode) {
return detail_impl<policy_t<Flag>>::connect(name, mode & receiver); return detail_impl<policy_t<Flag>>::connect(name, mode & receiver);
} }
template <typename Flag> template <typename Flag>
void chan_impl<Flag>::disconnect(ipc::handle_t h) { void chan_impl<Flag>::disconnect(ipc::handle_t h) {
detail_impl<policy_t<Flag>>::disconnect(h); detail_impl<policy_t<Flag>>::disconnect(h);
} }
template <typename Flag> template <typename Flag>
char const * chan_impl<Flag>::name(ipc::handle_t h) { char const * chan_impl<Flag>::name(ipc::handle_t h) {
auto info = detail_impl<policy_t<Flag>>::info_of(h); auto info = detail_impl<policy_t<Flag>>::info_of(h);
return (info == nullptr) ? nullptr : info->name_.c_str(); return (info == nullptr) ? nullptr : info->name_.c_str();
} }
template <typename Flag> template <typename Flag>
std::size_t chan_impl<Flag>::recv_count(ipc::handle_t h) { std::size_t chan_impl<Flag>::recv_count(ipc::handle_t h) {
return detail_impl<policy_t<Flag>>::recv_count(h); return detail_impl<policy_t<Flag>>::recv_count(h);
} }
template <typename Flag> template <typename Flag>
bool chan_impl<Flag>::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) { bool chan_impl<Flag>::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) {
return detail_impl<policy_t<Flag>>::wait_for_recv(h, r_count, tm); return detail_impl<policy_t<Flag>>::wait_for_recv(h, r_count, tm);
} }
template <typename Flag> template <typename Flag>
bool chan_impl<Flag>::send(ipc::handle_t h, void const * data, std::size_t size) { bool chan_impl<Flag>::send(ipc::handle_t h, void const * data, std::size_t size) {
return detail_impl<policy_t<Flag>>::send(h, data, size); return detail_impl<policy_t<Flag>>::send(h, data, size);
} }
template <typename Flag> template <typename Flag>
buff_t chan_impl<Flag>::recv(ipc::handle_t h, std::size_t tm) { buff_t chan_impl<Flag>::recv(ipc::handle_t h, std::size_t tm) {
return detail_impl<policy_t<Flag>>::recv(h, tm); return detail_impl<policy_t<Flag>>::recv(h, tm);
} }
template <typename Flag> template <typename Flag>
bool chan_impl<Flag>::try_send(ipc::handle_t h, void const * data, std::size_t size) { bool chan_impl<Flag>::try_send(ipc::handle_t h, void const * data, std::size_t size) {
return detail_impl<policy_t<Flag>>::try_send(h, data, size); return detail_impl<policy_t<Flag>>::try_send(h, data, size);
} }
template <typename Flag> template <typename Flag>
buff_t chan_impl<Flag>::try_recv(ipc::handle_t h) { buff_t chan_impl<Flag>::try_recv(ipc::handle_t h) {
return detail_impl<policy_t<Flag>>::try_recv(h); return detail_impl<policy_t<Flag>>::try_recv(h);
} }
template struct chan_impl<ipc::wr<relat::single, relat::single, trans::unicast >>; template struct chan_impl<ipc::wr<relat::single, relat::single, trans::unicast >>;
template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::unicast >>; template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::unicast >>;
template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::unicast >>; template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::unicast >>;
template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::broadcast>>; template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::broadcast>>;
template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::broadcast>>; template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::broadcast>>;
} // namespace ipc } // namespace ipc

View File

@ -1,39 +1,39 @@
#pragma once #pragma once
#include <cstdio> #include <cstdio>
#include <utility> #include <utility>
namespace ipc { namespace ipc {
namespace detail { namespace detail {
template <typename O> template <typename O>
void print(O out, char const * fmt) { void print(O out, char const * fmt) {
std::fprintf(out, "%s", fmt); std::fprintf(out, "%s", fmt);
} }
template <typename O, typename P1, typename... P> template <typename O, typename P1, typename... P>
void print(O out, char const * fmt, P1&& p1, P&&... params) { void print(O out, char const * fmt, P1&& p1, P&&... params) {
std::fprintf(out, fmt, std::forward<P1>(p1), std::forward<P>(params)...); std::fprintf(out, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
} }
} // namespace detail } // namespace detail
inline void log(char const * fmt) { inline void log(char const * fmt) {
ipc::detail::print(stdout, fmt); ipc::detail::print(stdout, fmt);
} }
template <typename P1, typename... P> template <typename P1, typename... P>
void log(char const * fmt, P1&& p1, P&&... params) { void log(char const * fmt, P1&& p1, P&&... params) {
ipc::detail::print(stdout, fmt, std::forward<P1>(p1), std::forward<P>(params)...); ipc::detail::print(stdout, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
} }
inline void error(char const * fmt) { inline void error(char const * fmt) {
ipc::detail::print(stderr, fmt); ipc::detail::print(stderr, fmt);
} }
template <typename P1, typename... P> template <typename P1, typename... P>
void error(char const * fmt, P1&& p1, P&&... params) { void error(char const * fmt, P1&& p1, P&&... params) {
ipc::detail::print(stderr, fmt, std::forward<P1>(p1), std::forward<P>(params)...); ipc::detail::print(stderr, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
} }
} // namespace ipc } // namespace ipc

View File

@ -1,431 +1,432 @@
#pragma once #pragma once
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <cstdlib> #include <cstdlib>
#include <map> #include <map>
#include <iterator> #include <iterator>
#include "def.h" #include "def.h"
#include "rw_lock.h" #include "rw_lock.h"
#include "concept.h" #include "concept.h"
#include "platform/detail.h" #include "platform/detail.h"
namespace ipc { namespace ipc {
namespace mem { namespace mem {
class static_alloc { class static_alloc {
public: public:
static void swap(static_alloc&) {} static void swap(static_alloc&) {}
static void clear() {} static void clear() {}
static void* alloc(std::size_t size) { static void* alloc(std::size_t size) {
return size ? std::malloc(size) : nullptr; return size ? std::malloc(size) : nullptr;
} }
static void free(void* p) { static void free(void* p) {
std::free(p); std::free(p);
} }
static void free(void* p, std::size_t /*size*/) { static void free(void* p, std::size_t /*size*/) {
free(p); free(p);
} }
}; };
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// Scope allocation -- The destructor will release all allocated blocks. /// Scope allocation -- The destructor will release all allocated blocks.
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
namespace detail { namespace detail {
constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { 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{})); 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_;
} * head_ = nullptr, * tail_ = 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))
}; };
public: public:
void swap(scope_alloc_base & rhs) { void swap(scope_alloc_base & rhs) {
std::swap(head_, rhs.head_); std::swap(head_, rhs.head_);
std::swap(tail_, rhs.tail_); std::swap(tail_, rhs.tail_);
} }
bool empty() const noexcept { bool empty() const noexcept {
return head_ == nullptr; return head_ == nullptr;
} }
void take(scope_alloc_base && rhs) { void take(scope_alloc_base && rhs) {
if (rhs.empty()) return; if (rhs.empty()) return;
if (empty()) swap(rhs); if (empty()) swap(rhs);
else { else {
std::swap(tail_->next_, rhs.head_); std::swap(tail_->next_, rhs.head_);
// rhs.head_ should be nullptr here // rhs.head_ should be nullptr here
tail_ = rhs.tail_; tail_ = rhs.tail_;
rhs.tail_ = nullptr; rhs.tail_ = nullptr;
} }
} }
void free(void* /*p*/) {} void free(void* /*p*/) {}
void free(void* /*p*/, std::size_t) {} void free(void* /*p*/, std::size_t) {}
}; };
} // namespace detail } // namespace detail
template <typename AllocP = static_alloc> template <typename AllocP = static_alloc>
class scope_alloc : public detail::scope_alloc_base { class scope_alloc : public detail::scope_alloc_base {
public: public:
using base_t = detail::scope_alloc_base; using base_t = detail::scope_alloc_base;
using alloc_policy = AllocP; using alloc_policy = AllocP;
private: private:
alloc_policy alloc_; alloc_policy alloc_;
void free_all() { void free_all() {
while (!empty()) { while (!empty()) {
auto curr = head_; auto curr = head_;
head_ = head_->next_; head_ = head_->next_;
alloc_.free(curr, curr->size_); alloc_.free(curr, curr->size_);
} }
// now head_ is nullptr // now head_ is nullptr
} }
public: public:
scope_alloc() = default; scope_alloc() = default;
scope_alloc(scope_alloc&& rhs) { swap(rhs); } scope_alloc(scope_alloc&& rhs) { swap(rhs); }
scope_alloc& operator=(scope_alloc&& rhs) { swap(rhs); return (*this); } scope_alloc& operator=(scope_alloc rhs) { swap(rhs); return (*this); }
~scope_alloc() { free_all(); } ~scope_alloc() { free_all(); }
template <typename A> template <typename A>
void set_allocator(A && alc) { void set_allocator(A && alc) {
alloc_ = std::forward<A>(alc); alloc_ = std::forward<A>(alc);
} }
void swap(scope_alloc& rhs) { void swap(scope_alloc& rhs) {
alloc_.swap(rhs.alloc_); alloc_.swap(rhs.alloc_);
base_t::swap(rhs); base_t::swap(rhs);
} }
template <typename A = AllocP> template <typename A = AllocP>
auto take(scope_alloc && rhs) -> ipc::require<detail::has_take<A>::value> { auto take(scope_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
base_t::take(std::move(rhs)); base_t::take(std::move(rhs));
alloc_.take(std::move(rhs.alloc_)); alloc_.take(std::move(rhs.alloc_));
} }
template <typename A = AllocP> template <typename A = AllocP>
auto take(scope_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> { auto take(scope_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
base_t::take(std::move(rhs)); base_t::take(std::move(rhs));
} }
void clear() { void clear() {
free_all(); free_all();
tail_ = nullptr; 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_ = head_; curr->next_ = head_;
curr->size_ = size; curr->size_ = size;
head_ = curr; head_ = curr;
if (tail_ == nullptr) { if (tail_ == nullptr) {
tail_ = curr; tail_ = curr;
} }
return (reinterpret_cast<byte_t*>(curr) + aligned_block_size); return (reinterpret_cast<byte_t*>(curr) + aligned_block_size);
} }
}; };
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// Fixed-size blocks allocation /// Fixed-size blocks allocation
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
namespace detail { namespace detail {
class fixed_alloc_base { class fixed_alloc_base {
protected: protected:
std::size_t init_expand_; std::size_t init_expand_;
void * cursor_; 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_ = nullptr;
} }
static void** node_p(void* node) { static void** node_p(void* node) {
return reinterpret_cast<void**>(node); return reinterpret_cast<void**>(node);
} }
static auto& next(void* node) { static auto& next(void* node) {
return *node_p(node); return *node_p(node);
} }
public: public:
void swap(fixed_alloc_base& rhs) { void swap(fixed_alloc_base& rhs) {
std::swap(init_expand_, rhs.init_expand_); std::swap(init_expand_, rhs.init_expand_);
std::swap(cursor_ , rhs.cursor_); std::swap(cursor_ , rhs.cursor_);
} }
bool empty() const noexcept { bool empty() const noexcept {
return cursor_ == nullptr; return cursor_ == nullptr;
} }
void take(fixed_alloc_base && rhs) { void take(fixed_alloc_base && rhs) {
init_expand_ = (ipc::detail::max)(init_expand_, rhs.init_expand_); init_expand_ = (ipc::detail::max)(init_expand_, rhs.init_expand_);
if (rhs.empty()) return; if (rhs.empty()) return;
auto curr = cursor_; auto curr = cursor_;
if (curr != nullptr) while (1) { if (curr != nullptr) while (1) {
auto next_cur = next(curr); auto next_cur = next(curr);
if (next_cur == nullptr) { if (next_cur == nullptr) {
std::swap(next(curr), rhs.cursor_); std::swap(next(curr), rhs.cursor_);
return; return;
} }
// next_cur != nullptr // next_cur != nullptr
else curr = next_cur; else curr = next_cur;
} }
// curr == nullptr, means cursor_ == nullptr // curr == nullptr, means cursor_ == nullptr
else std::swap(cursor_, rhs.cursor_); else std::swap(cursor_, rhs.cursor_);
// rhs.cursor_ must be nullptr // rhs.cursor_ must be nullptr
} }
void free(void* p) { void free(void* p) {
if (p == nullptr) return; if (p == nullptr) return;
next(p) = cursor_; next(p) = cursor_;
cursor_ = p; cursor_ = p;
} }
void free(void* p, std::size_t) { void free(void* p, std::size_t) {
free(p); free(p);
} }
}; };
struct fixed_expand_policy { } // namespace detail
enum : std::size_t { struct fixed_expand_policy {
base_size = sizeof(void*) * 1024 / 2
}; enum : std::size_t {
base_size = sizeof(void*) * 1024
static std::size_t prev(std::size_t& e) { };
if ((e /= 2) == 0) e = 1;
return e; constexpr static std::size_t prev(std::size_t e) noexcept {
} return ((e / 2) == 0) ? 1 : (e / 2);
}
static std::size_t next(std::size_t& e) {
return e *= 2; constexpr static std::size_t next(std::size_t e) noexcept {
} return e * 2;
}
template <std::size_t BlockSize>
static std::size_t next(std::size_t & e) { template <std::size_t BlockSize>
return ipc::detail::max<std::size_t>(BlockSize, base_size) * next(e); static std::size_t next(std::size_t & e) {
} auto n = ipc::detail::max<std::size_t>(BlockSize, base_size) * e;
}; e = next(e);
return n;
} // namespace detail }
};
template <std::size_t BlockSize,
typename AllocP = scope_alloc<>, template <std::size_t BlockSize,
typename ExpandP = detail::fixed_expand_policy> typename AllocP = scope_alloc<>,
class fixed_alloc : public detail::fixed_alloc_base { typename ExpandP = fixed_expand_policy>
public: class fixed_alloc : public detail::fixed_alloc_base {
using base_t = detail::fixed_alloc_base; public:
using alloc_policy = AllocP; using base_t = detail::fixed_alloc_base;
using alloc_policy = AllocP;
enum : std::size_t {
block_size = (ipc::detail::max)(BlockSize, sizeof(void*)) enum : std::size_t {
}; block_size = (ipc::detail::max)(BlockSize, sizeof(void*))
};
private:
alloc_policy alloc_; private:
alloc_policy alloc_;
void* try_expand() {
if (empty()) { void* try_expand() {
auto size = ExpandP::template next<block_size>(init_expand_); if (empty()) {
auto p = node_p(cursor_ = alloc_.alloc(size)); auto size = ExpandP::template next<block_size>(init_expand_);
for (std::size_t i = 0; i < (size / block_size) - 1; ++i) auto p = node_p(cursor_ = alloc_.alloc(size));
p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size); for (std::size_t i = 0; i < (size / block_size) - 1; ++i)
(*p) = nullptr; p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size);
} (*p) = nullptr;
return cursor_; }
} return cursor_;
}
public:
explicit fixed_alloc(std::size_t init_expand = 1) { public:
init(init_expand); explicit fixed_alloc(std::size_t init_expand = 1) {
} init(init_expand);
}
fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { swap(rhs); }
fixed_alloc& operator=(fixed_alloc&& rhs) { swap(rhs); return (*this); } fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { swap(rhs); }
fixed_alloc& operator=(fixed_alloc rhs) { swap(rhs); return (*this); }
template <typename A>
void set_allocator(A && alc) { template <typename A>
alloc_ = std::forward<A>(alc); void set_allocator(A && alc) {
} alloc_ = std::forward<A>(alc);
}
void swap(fixed_alloc& rhs) {
alloc_.swap(rhs.alloc_); void swap(fixed_alloc& rhs) {
base_t::swap(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> { template <typename A = AllocP>
base_t::take(std::move(rhs)); auto take(fixed_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
alloc_.take(std::move(rhs.alloc_)); 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> { template <typename A = AllocP>
base_t::take(std::move(rhs)); auto take(fixed_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
} base_t::take(std::move(rhs));
}
void clear() {
ExpandP::prev(init_expand_); void clear() {
cursor_ = nullptr; init_expand_ = ExpandP::prev(init_expand_);
alloc_.~alloc_policy(); cursor_ = nullptr;
} alloc_.~alloc_policy();
}
void* alloc() {
void* p = try_expand(); void* alloc() {
cursor_ = next(p); void* p = try_expand();
return p; cursor_ = next(p);
} return p;
}
void* alloc(std::size_t) {
return alloc(); void* alloc(std::size_t) {
} return alloc();
}; }
};
////////////////////////////////////////////////////////////////
/// Variable-size blocks allocation (without alignment) ////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////// /// Variable-size blocks allocation (without alignment)
////////////////////////////////////////////////////////////////
namespace detail {
namespace detail {
class variable_alloc_base {
protected: class variable_alloc_base {
struct head_t { protected:
std::size_t free_; struct head_t {
} * head_ = nullptr; std::size_t free_;
} * head_ = nullptr;
std::map<std::size_t, head_t*, std::greater<std::size_t>> reserves_;
std::map<std::size_t, head_t*, std::greater<std::size_t>> reserves_;
enum : std::size_t {
aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t)) 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<byte_t*>(p) + aligned_head_size + p->free_; static byte_t * buffer(head_t* p) {
} return reinterpret_cast<byte_t*>(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(head_, rhs.head_);
public: }
void swap(variable_alloc_base& rhs) {
std::swap(head_, rhs.head_); std::size_t remain() const noexcept {
} return (head_ == nullptr) ? 0 : head_->free_;
}
bool empty() const noexcept {
return remain() == 0; bool empty() const noexcept {
} return remain() == 0;
}
void take(variable_alloc_base && rhs) {
if (rhs.remain() > remain()) { void take(variable_alloc_base && rhs) {
if (!empty()) { if (rhs.remain() > remain()) {
reserves_.emplace(head_->free_, head_); if (!empty()) {
} reserves_.emplace(head_->free_, head_);
head_ = rhs.head_; }
} head_ = rhs.head_;
else if (!rhs.empty()) { }
reserves_.emplace(rhs.head_->free_, rhs.head_); else if (!rhs.empty()) {
} reserves_.emplace(rhs.head_->free_, rhs.head_);
rhs.head_ = nullptr; }
} rhs.head_ = nullptr;
}
void free(void* /*p*/) {}
void free(void* /*p*/, std::size_t) {} void free(void* /*p*/) {}
}; void free(void* /*p*/, std::size_t) {}
};
} // namespace detail
} // namespace detail
template <std::size_t ChunkSize = (sizeof(void*) * 1024), typename AllocP = scope_alloc<>>
class variable_alloc : public detail::variable_alloc_base { template <std::size_t ChunkSize = (sizeof(void*) * 1024), typename AllocP = scope_alloc<>>
public: class variable_alloc : public detail::variable_alloc_base {
using base_t = detail::variable_alloc_base; public:
using head_t = base_t::head_t; using base_t = detail::variable_alloc_base;
using alloc_policy = AllocP; using head_t = base_t::head_t;
using alloc_policy = AllocP;
private:
alloc_policy alloc_; private:
alloc_policy alloc_;
public:
variable_alloc() = default; public:
variable_alloc() = default;
variable_alloc(variable_alloc&& rhs) { swap(rhs); }
variable_alloc& operator=(variable_alloc&& rhs) { swap(rhs); return (*this); } variable_alloc(variable_alloc&& rhs) { swap(rhs); }
variable_alloc& operator=(variable_alloc rhs) { swap(rhs); return (*this); }
template <typename A>
void set_allocator(A && alc) { template <typename A>
alloc_ = std::forward<A>(alc); void set_allocator(A && alc) {
} alloc_ = std::forward<A>(alc);
}
void swap(variable_alloc& rhs) {
alloc_.swap(rhs.alloc_); void swap(variable_alloc& rhs) {
base_t::swap(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> { template <typename A = AllocP>
base_t::take(std::move(rhs)); auto take(variable_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
alloc_.take(std::move(rhs.alloc_)); 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> { template <typename A = AllocP>
base_t::take(std::move(rhs)); auto take(variable_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
} base_t::take(std::move(rhs));
}
void clear() {
alloc_.~alloc_policy(); void clear() {
} alloc_.~alloc_policy();
}
void* alloc(std::size_t size) {
if (size >= (ChunkSize - aligned_head_size)) { void* alloc(std::size_t size) {
return alloc_.alloc(size); if (size >= ChunkSize) {
} return alloc_.alloc(size);
if (remain() < size) { }
auto it = reserves_.begin(); if (remain() < size) {
if ((it == reserves_.end()) || (it->first < size)) { auto it = reserves_.begin();
head_ = static_cast<head_t*>(alloc_.alloc(ChunkSize)); if ((it == reserves_.end()) || (it->first < size)) {
head_->free_ = ChunkSize - aligned_head_size - size; head_ = static_cast<head_t*>(alloc_.alloc(ChunkSize + aligned_head_size));
} head_->free_ = ChunkSize - size;
else { }
auto temp = it->second; else {
temp->free_ -= size; auto temp = it->second;
reserves_.erase(it); temp->free_ -= size;
if (remain() < temp->free_) { reserves_.erase(it);
head_ = temp; if (remain() < temp->free_) {
} head_ = temp;
else return base_t::buffer(temp); }
} else return base_t::buffer(temp);
} }
// size shouldn't be 0 here, otherwise behavior is undefined }
else head_->free_ -= size; // size shouldn't be 0 here, otherwise behavior is undefined
return base_t::buffer(head_); else head_->free_ -= size;
} return base_t::buffer(head_);
}; }
};
} // namespace mem
} // namespace ipc } // namespace mem
} // namespace ipc

142
src/memory/resource.h Executable file → Normal file
View File

@ -1,68 +1,74 @@
#pragma once #pragma once
#include <type_traits> #include <type_traits>
#include <limits> #include <limits>
#include <utility> #include <utility>
#include <functional> #include <functional>
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
#include <cstdio> #include <cstdio>
#include "def.h" #include "def.h"
#include "memory/alloc.h" #include "memory/alloc.h"
#include "memory/wrapper.h" #include "memory/wrapper.h"
#include "platform/detail.h" #include "platform/detail.h"
namespace ipc { namespace ipc {
namespace mem { namespace mem {
using chunk_variable_alloc = variable_alloc<sizeof(void*) * 256 * 1024 /* 2MB(x64) */>; namespace detail {
template <std::size_t Size> using chunk_variable_alloc =
using static_async_fixed = static_wrapper<async_wrapper<fixed_alloc<Size, chunk_variable_alloc>>>; static_wrapper<async_wrapper<variable_alloc<
sizeof(void*) * 1024 * 256 /* 2MB(x64) */ >>>;
using async_pool_alloc = static_alloc/*variable_wrapper<static_async_fixed>*/;
template <std::size_t Size>
template <typename T> using static_async_fixed =
using allocator = allocator_wrapper<T, async_pool_alloc>; static_wrapper<async_wrapper<fixed_alloc<
Size, chunk_variable_alloc >>>;
} // namespace mem
using async_pool_alloc = /*static_alloc*/variable_wrapper<static_async_fixed>;
namespace {
template <typename T>
constexpr char const * pf(int) { return "%d" ; } using allocator = allocator_wrapper<T, async_pool_alloc>;
constexpr char const * pf(long) { return "%ld" ; }
constexpr char const * pf(long long) { return "%lld"; } } // namespace mem
constexpr char const * pf(unsigned int) { return "%u" ; }
constexpr char const * pf(unsigned long) { return "%lu" ; } namespace {
constexpr char const * pf(unsigned long long) { return "%llu"; }
constexpr char const * pf(float) { return "%f" ; } constexpr char const * pf(int) { return "%d" ; }
constexpr char const * pf(double) { return "%f" ; } constexpr char const * pf(long) { return "%ld" ; }
constexpr char const * pf(long double) { return "%Lf" ; } constexpr char const * pf(long long) { return "%lld"; }
constexpr char const * pf(unsigned int) { return "%u" ; }
} // internal-linkage constexpr char const * pf(unsigned long) { return "%lu" ; }
constexpr char const * pf(unsigned long long) { return "%llu"; }
template <typename Key, typename T> constexpr char const * pf(float) { return "%f" ; }
using unordered_map = std::unordered_map< constexpr char const * pf(double) { return "%f" ; }
Key, T, std::hash<Key>, std::equal_to<Key>, ipc::mem::allocator<std::pair<const Key, T>> constexpr char const * pf(long double) { return "%Lf" ; }
>;
} // internal-linkage
template <typename Char>
using basic_string = std::basic_string< template <typename Key, typename T>
Char, std::char_traits<Char>, ipc::mem::allocator<Char> using unordered_map = std::unordered_map<
>; Key, T, std::hash<Key>, std::equal_to<Key>, ipc::mem::allocator<std::pair<const Key, T>>
>;
using string = basic_string<char>;
using wstring = basic_string<wchar_t>; template <typename Char>
using basic_string = std::basic_string<
template <typename T> Char, std::char_traits<Char>, ipc::mem::allocator<Char>
ipc::string to_string(T val) { >;
char buf[std::numeric_limits<T>::digits10 + 1] {};
if (std::snprintf(buf, sizeof(buf), pf(val), val) > 0) { using string = basic_string<char>;
return buf; using wstring = basic_string<wchar_t>;
}
return {}; template <typename T>
} ipc::string to_string(T val) {
char buf[std::numeric_limits<T>::digits10 + 1] {};
} // namespace ipc if (std::snprintf(buf, sizeof(buf), pf(val), val) > 0) {
return buf;
}
return {};
}
} // namespace ipc

View File

@ -1,379 +1,403 @@
#pragma once #pragma once
#include <limits> #include <limits>
#include <new> #include <new>
#include <tuple> #include <tuple>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <utility> #include <utility>
#include <cstddef> #include <cstddef>
#include <type_traits> #include <type_traits>
#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 "concept.h"
#include "memory/alloc.h" #include "memory/alloc.h"
#include "platform/detail.h" #include "platform/detail.h"
namespace ipc { namespace ipc {
namespace mem { namespace mem {
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// The allocator wrapper class for STL /// The allocator wrapper class for STL
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
template <typename T, typename AllocP> template <typename T, typename AllocP>
class allocator_wrapper { class allocator_wrapper {
template <typename U, typename AllocU> template <typename U, typename AllocU>
friend class allocator_wrapper; friend class allocator_wrapper;
public: public:
// type definitions // type definitions
typedef T value_type; typedef T value_type;
typedef value_type* pointer; typedef value_type* pointer;
typedef const value_type* const_pointer; typedef const value_type* const_pointer;
typedef value_type& reference; typedef value_type& reference;
typedef const value_type& const_reference; typedef const value_type& const_reference;
typedef std::size_t size_type; typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type; typedef std::ptrdiff_t difference_type;
typedef AllocP alloc_policy; typedef AllocP alloc_policy;
private: private:
alloc_policy alloc_; alloc_policy alloc_;
public: public:
allocator_wrapper(void) noexcept = default; allocator_wrapper(void) noexcept = default;
allocator_wrapper(const allocator_wrapper<T, AllocP>& rhs) noexcept allocator_wrapper(const allocator_wrapper<T, AllocP>& rhs) noexcept
: alloc_(rhs.alloc_) : alloc_(rhs.alloc_)
{} {}
template <typename U> template <typename U>
allocator_wrapper(const allocator_wrapper<U, AllocP>& rhs) noexcept allocator_wrapper(const allocator_wrapper<U, AllocP>& rhs) noexcept
: alloc_(rhs.alloc_) : alloc_(rhs.alloc_)
{} {}
allocator_wrapper(allocator_wrapper<T, AllocP>&& rhs) noexcept allocator_wrapper(allocator_wrapper<T, AllocP>&& rhs) noexcept
: alloc_(std::move(rhs.alloc_)) : alloc_(std::move(rhs.alloc_))
{} {}
template <typename U> template <typename U>
allocator_wrapper(allocator_wrapper<U, AllocP>&& rhs) noexcept allocator_wrapper(allocator_wrapper<U, AllocP>&& rhs) noexcept
: alloc_(std::move(rhs.alloc_)) : alloc_(std::move(rhs.alloc_))
{} {}
allocator_wrapper(const AllocP& rhs) noexcept allocator_wrapper(const AllocP& rhs) noexcept
: alloc_(rhs) : alloc_(rhs)
{} {}
allocator_wrapper(AllocP&& rhs) noexcept allocator_wrapper(AllocP&& rhs) noexcept
: alloc_(std::move(rhs)) : alloc_(std::move(rhs))
{} {}
public: public:
// the other type of std_allocator // the other type of std_allocator
template <typename U> template <typename U>
struct rebind { typedef allocator_wrapper<U, AllocP> other; }; struct rebind { typedef allocator_wrapper<U, AllocP> other; };
constexpr size_type max_size(void) const noexcept { constexpr size_type max_size(void) const noexcept {
return (std::numeric_limits<size_type>::max)() / sizeof(T); return (std::numeric_limits<size_type>::max)() / sizeof(T);
} }
public: public:
pointer allocate(size_type count) noexcept { pointer allocate(size_type count) noexcept {
if (count == 0) return nullptr; if (count == 0) return nullptr;
if (count > this->max_size()) return nullptr; if (count > this->max_size()) return nullptr;
return static_cast<pointer>(alloc_.alloc(count * sizeof(T))); return static_cast<pointer>(alloc_.alloc(count * sizeof(T)));
} }
void deallocate(pointer p, size_type count) noexcept { void deallocate(pointer p, size_type count) noexcept {
alloc_.free(p, count * sizeof(T)); alloc_.free(p, count * sizeof(T));
} }
template <typename... P> template <typename... P>
static void construct(pointer p, P&&... params) { static void construct(pointer p, P&&... params) {
::new (static_cast<void*>(p)) T(std::forward<P>(params)...); ::new (static_cast<void*>(p)) T(std::forward<P>(params)...);
} }
static void destroy(pointer p) { static void destroy(pointer p) {
p->~T(); p->~T();
} }
}; };
template <class AllocP> template <class AllocP>
class allocator_wrapper<void, AllocP> { class allocator_wrapper<void, AllocP> {
public: public:
// type definitions // type definitions
typedef void value_type; typedef void value_type;
typedef value_type* pointer; typedef value_type* pointer;
typedef const value_type* const_pointer; typedef const value_type* const_pointer;
typedef std::size_t size_type; typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type; typedef std::ptrdiff_t difference_type;
typedef AllocP alloc_policy; typedef AllocP alloc_policy;
}; };
template <typename T, typename U, class AllocP> template <typename T, typename U, class AllocP>
constexpr bool operator==(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept { constexpr bool operator==(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
return true; return true;
} }
template <typename T, typename U, class AllocP> template <typename T, typename U, class AllocP>
constexpr bool operator!=(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept { constexpr bool operator!=(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
return false; return false;
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// Thread-safe allocation wrapper /// Thread-safe allocation wrapper
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
template <typename AllocP> template <typename AllocP>
class default_alloc_recoverer { class default_alloc_recycler {
public: public:
using alloc_policy = AllocP; using alloc_policy = AllocP;
private: private:
ipc::spin_lock master_lock_; ipc::spin_lock master_lock_;
std::vector<alloc_policy> master_allocs_; std::vector<alloc_policy> master_allocs_;
public: IPC_CONCEPT_(has_remain, remain());
void swap(default_alloc_recoverer& rhs) { IPC_CONCEPT_(has_empty , empty());
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
master_allocs_.swap(rhs.master_allocs_); public:
} void swap(default_alloc_recycler& rhs) {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
void clear() { master_allocs_.swap(rhs.master_allocs_);
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); }
master_allocs_.clear();
} void clear() {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
void try_recover(alloc_policy & alc) { master_allocs_.clear();
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); }
if (!master_allocs_.empty()) {
alc.swap(master_allocs_.back()); void try_recover(alloc_policy & alc) {
master_allocs_.pop_back(); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
} if (!master_allocs_.empty()) {
} alc.swap(master_allocs_.back());
master_allocs_.pop_back();
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()) { template <typename A = AllocP>
alc.take(std::move(master_allocs_.back())); auto try_replenish(alloc_policy & alc, std::size_t size)
master_allocs_.pop_back(); -> ipc::require<detail::has_take<A>::value && has_remain<A>::value> {
} if (alc.remain() >= size) return;
} IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
if (!master_allocs_.empty()) {
template <typename A = AllocP> alc.take(std::move(master_allocs_.back()));
constexpr auto try_replenish(alloc_policy & /*alc*/) noexcept master_allocs_.pop_back();
-> ipc::require<!detail::has_take<A>::value> {} }
}
void collect(alloc_policy && alc) {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); template <typename A = AllocP>
master_allocs_.emplace_back(std::move(alc)); 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;
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
template <typename AllocP, if (!master_allocs_.empty()) {
template <typename> class RecovererP = default_alloc_recoverer> alc.take(std::move(master_allocs_.back()));
class async_wrapper { master_allocs_.pop_back();
public: }
using alloc_policy = AllocP; }
private: template <typename A = AllocP>
RecovererP<alloc_policy> recoverer_; 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)> {}
class alloc_proxy : public AllocP {
async_wrapper * w_ = nullptr; void collect(alloc_policy && alc) {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
IPC_CONCEPT_(has_empty, empty()); master_allocs_.emplace_back(std::move(alc));
}
public: };
alloc_proxy(alloc_proxy && rhs)
: AllocP(std::move(rhs)) template <typename AllocP>
{} class empty_alloc_recycler {
public:
alloc_proxy(async_wrapper* w) using alloc_policy = AllocP;
: AllocP(), w_(w) {
if (w_ == nullptr) return; constexpr static void swap(empty_alloc_recycler&) noexcept {}
w_->recoverer_.try_recover(*this); constexpr static void clear() noexcept {}
} constexpr static void try_recover(alloc_policy&) noexcept {}
constexpr static auto try_replenish(alloc_policy&, std::size_t) noexcept {}
~alloc_proxy() { constexpr static void collect(alloc_policy&&) noexcept {}
if (w_ == nullptr) return; };
w_->recoverer_.collect(std::move(*this));
} template <typename AllocP,
template <typename> class RecyclerP = default_alloc_recycler>
template <typename A = AllocP> class async_wrapper {
auto alloc(std::size_t size) -> ipc::require<has_empty<A>::value, void*> { public:
auto p = AllocP::alloc(size); using alloc_policy = AllocP;
if (AllocP::empty() && (w_ != nullptr)) {
w_->recoverer_.try_replenish(*this); private:
} RecyclerP<alloc_policy> recycler_;
return p;
} class alloc_proxy : public AllocP {
async_wrapper * w_ = nullptr;
template <typename A = AllocP>
auto alloc(std::size_t size) -> ipc::require<!has_empty<A>::value, void*> { public:
return AllocP::alloc(size); alloc_proxy(alloc_proxy && rhs)
} : AllocP(std::move(rhs))
}; {}
friend class alloc_proxy; alloc_proxy(async_wrapper* w)
: AllocP(), w_(w) {
auto& get_alloc() { if (w_ == nullptr) return;
static tls::pointer<alloc_proxy> tls_alc; w_->recycler_.try_recover(*this);
return *tls_alc.create(this); }
}
~alloc_proxy() {
public: if (w_ == nullptr) return;
void swap(async_wrapper& rhs) { w_->recycler_.collect(std::move(*this));
recoverer_.swap(rhs.recoverer_); }
}
auto alloc(std::size_t size) {
void clear() { if (w_ != nullptr) {
recoverer_.clear(); w_->recycler_.try_replenish(*this, size);
} }
return AllocP::alloc(size);
void* alloc(std::size_t size) { }
return get_alloc().alloc(size); };
}
friend class alloc_proxy;
void free(void* p, std::size_t size) {
get_alloc().free(p, size); auto& get_alloc() {
} static tls::pointer<alloc_proxy> tls_alc;
}; return *tls_alc.create(this);
}
////////////////////////////////////////////////////////////////
/// Thread-safe allocation wrapper (with spin_lock) public:
//////////////////////////////////////////////////////////////// void swap(async_wrapper& rhs) {
recycler_.swap(rhs.recycler_);
template <typename AllocP, typename MutexT = ipc::spin_lock> }
class sync_wrapper {
public: void clear() {
using alloc_policy = AllocP; recycler_.clear();
using mutex_type = MutexT; }
private: void* alloc(std::size_t size) {
mutex_type lock_; return get_alloc().alloc(size);
alloc_policy alloc_; }
public: void free(void* p, std::size_t size) {
void swap(sync_wrapper& rhs) { get_alloc().free(p, size);
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); }
alloc_.swap(rhs.alloc_); };
}
////////////////////////////////////////////////////////////////
void clear() { /// Thread-safe allocation wrapper (with spin_lock)
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); ////////////////////////////////////////////////////////////////
alloc_.~alloc_policy();
} template <typename AllocP, typename MutexT = ipc::spin_lock>
class sync_wrapper {
void* alloc(std::size_t size) { public:
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); using alloc_policy = AllocP;
return alloc_.alloc(size); using mutex_type = MutexT;
}
private:
void free(void* p, std::size_t size) { mutex_type lock_;
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); alloc_policy alloc_;
alloc_.free(p, size);
} public:
}; void swap(sync_wrapper& rhs) {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
//////////////////////////////////////////////////////////////// alloc_.swap(rhs.alloc_);
/// Static allocation wrapper }
////////////////////////////////////////////////////////////////
void clear() {
template <typename AllocP> IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
class static_wrapper { alloc_.~alloc_policy();
public: }
using alloc_policy = AllocP;
void* alloc(std::size_t size) {
static alloc_policy& instance() { IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
static alloc_policy alloc; return alloc_.alloc(size);
return alloc; }
}
void free(void* p, std::size_t size) {
static void swap(static_wrapper&) {} IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
alloc_.free(p, size);
static void clear() { }
instance().clear(); };
}
////////////////////////////////////////////////////////////////
static void* alloc(std::size_t size) { /// Static allocation wrapper
return instance().alloc(size); ////////////////////////////////////////////////////////////////
}
template <typename AllocP>
static void free(void* p, std::size_t size) { class static_wrapper {
instance().free(p, size); public:
} using alloc_policy = AllocP;
};
static alloc_policy& instance() {
//////////////////////////////////////////////////////////////// static alloc_policy alloc;
/// Variable memory allocation wrapper return alloc;
//////////////////////////////////////////////////////////////// }
template <std::size_t BaseSize = sizeof(void*)> static void swap(static_wrapper&) {}
struct default_mapping_policy {
static void clear() {
enum : std::size_t { instance().clear();
base_size = BaseSize, }
classes_size = 32
}; static void* alloc(std::size_t size) {
return instance().alloc(size);
static const std::size_t table[classes_size]; }
constexpr static std::size_t classify(std::size_t size) { static void free(void* p, std::size_t size) {
return (((size - 1) / base_size) < classes_size) ? instance().free(p, size);
// always uses default_mapping_policy<sizeof(void*)>::table }
default_mapping_policy<>::table[((size - 1) / base_size)] : classes_size; };
}
}; ////////////////////////////////////////////////////////////////
/// Variable memory allocation wrapper
template <std::size_t B> ////////////////////////////////////////////////////////////////
const std::size_t default_mapping_policy<B>::table[default_mapping_policy<B>::classes_size] = {
/* 1 - 8 ~ 32 */ template <std::size_t BaseSize = sizeof(void*)>
0 , 1 , 2 , 3 , struct default_mapping_policy {
/* 2 - 48 ~ 256 */
5 , 5 , 7 , 7 , 9 , 9 , 11, 11, 13, 13, 15, 15, 17, 17, enum : std::size_t {
19, 19, 21, 21, 23, 23, 25, 25, 27, 27, 29, 29, 31, 31 base_size = BaseSize,
}; classes_size = 32
};
template <template <std::size_t> class Fixed,
typename MappingP = default_mapping_policy<>, static const std::size_t table[classes_size];
typename StaticAlloc = mem::static_alloc>
class variable_wrapper { IPC_CONSTEXPR_ static std::size_t classify(std::size_t size) noexcept {
auto index = (size - 1) / base_size;
template <typename F> return (index < classes_size) ?
constexpr static auto choose(std::size_t size, F&& f) { // always uses default_mapping_policy<sizeof(void*)>::table
return ipc::detail::static_switch<MappingP::classes_size>(MappingP::classify(size), [&f](auto index) { default_mapping_policy<>::table[index] : classes_size;
return f(Fixed<(decltype(index)::value + 1) * MappingP::base_size>{}); }
}, [&f] {
return f(StaticAlloc{}); constexpr static std::size_t block_size(std::size_t value) noexcept {
}); return (value + 1) * base_size;
} }
};
public:
static void swap(variable_wrapper&) {} template <std::size_t B>
const std::size_t default_mapping_policy<B>::table[default_mapping_policy<B>::classes_size] = {
static void clear() { /* 1 - 8 ~ 32 */
ipc::detail::static_for<MappingP::classes_size>([](auto index) { 0 , 1 , 2 , 3 ,
Fixed<(decltype(index)::value + 1) * MappingP::base_size>::clear(); /* 2 - 48 ~ 256 */
}); 5 , 5 , 7 , 7 , 9 , 9 , 11, 11, 13, 13, 15, 15, 17, 17,
StaticAlloc::clear(); 19, 19, 21, 21, 23, 23, 25, 25, 27, 27, 29, 29, 31, 31
} };
static void* alloc(std::size_t size) { template <template <std::size_t> class Fixed,
return choose(size, [size](auto&& alc) { return alc.alloc(size); }); typename MappingP = default_mapping_policy<>,
} typename StaticAlloc = mem::static_alloc>
class variable_wrapper {
static void free(void* p, std::size_t size) {
choose(size, [p, size](auto&& alc) { alc.free(p, size); }); template <typename F>
} constexpr static auto choose(std::size_t size, F&& f) {
}; return ipc::detail::static_switch<MappingP::classes_size>(MappingP::classify(size), [&f](auto index) {
return f(Fixed<MappingP::block_size(decltype(index)::value)>{});
} // namespace mem }, [&f] {
} // namespace ipc return f(StaticAlloc{});
});
}
public:
static void swap(variable_wrapper&) {}
static void clear() {
ipc::detail::static_for<MappingP::classes_size>([](auto index) {
Fixed<MappingP::block_size(decltype(index)::value)>::clear();
});
StaticAlloc::clear();
}
static void* alloc(std::size_t size) {
return choose(size, [size](auto&& alc) { return alc.alloc(size); });
}
static void free(void* p, std::size_t size) {
choose(size, [p, size](auto&& alc) { alc.free(p, size); });
}
};
} // namespace mem
} // namespace ipc

View File

@ -1,67 +1,67 @@
#pragma once #pragma once
#include <new> #include <new>
#include <utility> #include <utility>
#include "concept.h" #include "concept.h"
#include "pool_alloc.h" #include "pool_alloc.h"
namespace ipc { 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 = ipc::require<(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 = ipc::require<(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> {
T* buf {}; T* buf {};
::new (&buf) T { std::forward<P>(params)... }; ::new (&buf) T { std::forward<P>(params)... };
return buf; return buf;
} }
template <typename T> template <typename T>
constexpr auto impl(T* const (& p)) -> IsImplComfortable<T> { constexpr auto impl(T* const (& p)) -> IsImplComfortable<T> {
return reinterpret_cast<T*>(&const_cast<char &>(reinterpret_cast<char const &>(p))); return reinterpret_cast<T*>(&const_cast<char &>(reinterpret_cast<char const &>(p)));
} }
template <typename T> template <typename T>
constexpr auto clear_impl(T* p) -> IsImplComfortable<T, void> { constexpr auto clear_impl(T* p) -> IsImplComfortable<T, void> {
if (p != nullptr) impl(p)->~T(); if (p != nullptr) impl(p)->~T();
} }
template <typename T, typename... P> template <typename T, typename... P>
constexpr auto make_impl(P&&... params) -> IsImplUncomfortable<T> { constexpr auto make_impl(P&&... params) -> IsImplUncomfortable<T> {
return mem::alloc<T>(std::forward<P>(params)...); return mem::alloc<T>(std::forward<P>(params)...);
} }
template <typename T> template <typename T>
constexpr auto clear_impl(T* p) -> IsImplUncomfortable<T, void> { constexpr auto clear_impl(T* p) -> IsImplUncomfortable<T, void> {
mem::free(p); mem::free(p);
} }
template <typename T> template <typename T>
constexpr auto impl(T* const (& p)) -> IsImplUncomfortable<T> { constexpr auto impl(T* const (& p)) -> IsImplUncomfortable<T> {
return p; return p;
} }
template <typename T> template <typename T>
struct pimpl { struct pimpl {
template <typename... P> template <typename... P>
constexpr static T* make(P&&... params) { constexpr static T* make(P&&... params) {
return make_impl<T>(std::forward<P>(params)...); return make_impl<T>(std::forward<P>(params)...);
} }
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
constexpr void clear() { constexpr void clear() {
#else /*__cplusplus < 201703L*/ #else /*__cplusplus < 201703L*/
void clear() { void clear() {
#endif/*__cplusplus < 201703L*/ #endif/*__cplusplus < 201703L*/
clear_impl(static_cast<T*>(const_cast<pimpl*>(this))); clear_impl(static_cast<T*>(const_cast<pimpl*>(this)));
} }
}; };
} // namespace ipc } // namespace ipc

View File

@ -1,149 +1,149 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <shared_mutex> #include <shared_mutex>
#include <type_traits> #include <type_traits>
#include <tuple> #include <tuple>
#include <atomic> #include <atomic>
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <new> #include <new>
#include "def.h" #include "def.h"
#include "export.h" #include "export.h"
// pre-defined // pre-defined
#ifdef IPC_UNUSED_ #ifdef IPC_UNUSED_
# error "IPC_UNUSED_ has been defined." # error "IPC_UNUSED_ has been defined."
#endif #endif
#ifdef IPC_FALLTHROUGH_ #ifdef IPC_FALLTHROUGH_
# error "IPC_FALLTHROUGH_ has been defined." # error "IPC_FALLTHROUGH_ has been defined."
#endif #endif
#ifdef IPC_STBIND_ #ifdef IPC_STBIND_
# error "IPC_STBIND_ has been defined." # error "IPC_STBIND_ has been defined."
#endif #endif
#ifdef IPC_CONSTEXPR_ #ifdef IPC_CONSTEXPR_
# error "IPC_CONSTEXPR_ has been defined." # error "IPC_CONSTEXPR_ has been defined."
#endif #endif
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
#define IPC_UNUSED_ [[maybe_unused]] #define IPC_UNUSED_ [[maybe_unused]]
#define IPC_FALLTHROUGH_ [[fallthrough]] #define IPC_FALLTHROUGH_ [[fallthrough]]
#define IPC_STBIND_(A, B, ...) auto [A, B] = __VA_ARGS__ #define IPC_STBIND_(A, B, ...) auto [A, B] = __VA_ARGS__
#define IPC_CONSTEXPR_ constexpr #define IPC_CONSTEXPR_ constexpr
#else /*__cplusplus < 201703L*/ #else /*__cplusplus < 201703L*/
#if defined(_MSC_VER) #if defined(_MSC_VER)
# define IPC_UNUSED_ __pragma(warning(suppress: 4100 4101 4189)) # define IPC_UNUSED_ __pragma(warning(suppress: 4100 4101 4189))
#elif defined(__GNUC__) #elif defined(__GNUC__)
# define IPC_UNUSED_ __attribute__((__unused__)) # define IPC_UNUSED_ __attribute__((__unused__))
#else #else
# define IPC_UNUSED_ # define IPC_UNUSED_
#endif #endif
#define IPC_FALLTHROUGH_ #define IPC_FALLTHROUGH_
#define IPC_STBIND_(A, B, ...) \ #define IPC_STBIND_(A, B, ...) \
auto tp___ = __VA_ARGS__ \ auto tp___ = __VA_ARGS__ \
auto A = std::get<0>(tp___); \ auto A = std::get<0>(tp___); \
auto B = std::get<1>(tp___) auto B = std::get<1>(tp___)
#define IPC_CONSTEXPR_ inline #define IPC_CONSTEXPR_ inline
#endif/*__cplusplus < 201703L*/ #endif/*__cplusplus < 201703L*/
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
namespace std { namespace std {
// deduction guides for std::unique_ptr // deduction guides for std::unique_ptr
template <typename T, typename D> template <typename T, typename D>
unique_ptr(T* p, D&& d) -> unique_ptr<T, std::decay_t<D>>; unique_ptr(T* p, D&& d) -> unique_ptr<T, std::decay_t<D>>;
} // namespace std } // namespace std
namespace ipc { namespace ipc {
namespace detail { namespace detail {
using std::unique_ptr; using std::unique_ptr;
using std::unique_lock; using std::unique_lock;
using std::shared_lock; using std::shared_lock;
using std::max; using std::max;
using std::min; using std::min;
#else /*__cplusplus < 201703L*/ #else /*__cplusplus < 201703L*/
namespace ipc { namespace ipc {
namespace detail { namespace detail {
// deduction guides for std::unique_ptr // deduction guides for std::unique_ptr
template <typename T, typename D> template <typename T, typename D>
constexpr auto unique_ptr(T* p, D&& d) { constexpr auto unique_ptr(T* p, D&& d) {
return std::unique_ptr<T, std::decay_t<D>> { p, std::forward<D>(d) }; return std::unique_ptr<T, std::decay_t<D>> { p, std::forward<D>(d) };
} }
// deduction guides for std::unique_lock // deduction guides for std::unique_lock
template <typename T> template <typename T>
constexpr auto unique_lock(T&& lc) { constexpr auto unique_lock(T&& lc) {
return std::unique_lock<std::decay_t<T>> { std::forward<T>(lc) }; return std::unique_lock<std::decay_t<T>> { std::forward<T>(lc) };
} }
// deduction guides for std::shared_lock // deduction guides for std::shared_lock
template <typename T> template <typename T>
constexpr auto shared_lock(T&& lc) { constexpr auto shared_lock(T&& lc) {
return std::shared_lock<std::decay_t<T>> { std::forward<T>(lc) }; return std::shared_lock<std::decay_t<T>> { std::forward<T>(lc) };
} }
template <typename T> template <typename T>
constexpr const T& (max)(const T& a, const T& b) { constexpr const T& (max)(const T& a, const T& b) {
return (a < b) ? b : a; return (a < b) ? b : a;
} }
template <typename T> template <typename T>
constexpr const T& (min)(const T& a, const T& b) { constexpr const T& (min)(const T& a, const T& b) {
return (b < a) ? b : a; return (b < a) ? b : a;
} }
#endif/*__cplusplus < 201703L*/ #endif/*__cplusplus < 201703L*/
template <typename F, typename D> template <typename F, typename D>
constexpr decltype(auto) static_switch(std::size_t /*i*/, std::index_sequence<>, F&& /*f*/, D&& def) { constexpr decltype(auto) static_switch(std::size_t /*i*/, std::index_sequence<>, F&& /*f*/, D&& def) {
return std::forward<D>(def)(); return std::forward<D>(def)();
} }
template <typename F, typename D, std::size_t N, std::size_t...I> template <typename F, typename D, std::size_t N, std::size_t...I>
constexpr decltype(auto) static_switch(std::size_t i, std::index_sequence<N, I...>, F&& f, D&& def) { constexpr decltype(auto) static_switch(std::size_t i, std::index_sequence<N, I...>, F&& f, D&& def) {
return (i == N) ? std::forward<F>(f)(std::integral_constant<size_t, N>{}) : return (i == N) ? std::forward<F>(f)(std::integral_constant<size_t, N>{}) :
static_switch(i, std::index_sequence<I...>{}, std::forward<F>(f), std::forward<D>(def)); static_switch(i, std::index_sequence<I...>{}, std::forward<F>(f), std::forward<D>(def));
} }
template <std::size_t N, typename F, typename D> template <std::size_t N, typename F, typename D>
constexpr decltype(auto) static_switch(std::size_t i, F&& f, D&& def) { constexpr decltype(auto) static_switch(std::size_t i, F&& f, D&& def) {
return static_switch(i, std::make_index_sequence<N>{}, std::forward<F>(f), std::forward<D>(def)); return static_switch(i, std::make_index_sequence<N>{}, std::forward<F>(f), std::forward<D>(def));
} }
template <typename F, std::size_t...I> template <typename F, std::size_t...I>
IPC_CONSTEXPR_ void static_for(std::index_sequence<I...>, F&& f) { IPC_CONSTEXPR_ void static_for(std::index_sequence<I...>, F&& f) {
IPC_UNUSED_ auto expand = { (std::forward<F>(f)(std::integral_constant<size_t, I>{}), 0)... }; IPC_UNUSED_ auto expand = { (std::forward<F>(f)(std::integral_constant<size_t, I>{}), 0)... };
} }
template <std::size_t N, typename F> template <std::size_t N, typename F>
IPC_CONSTEXPR_ void static_for(F&& f) { IPC_CONSTEXPR_ void static_for(F&& f) {
static_for(std::make_index_sequence<N>{}, std::forward<F>(f)); static_for(std::make_index_sequence<N>{}, std::forward<F>(f));
} }
// Minimum offset between two objects to avoid false sharing. // Minimum offset between two objects to avoid false sharing.
enum { enum {
// #if __cplusplus >= 201703L // #if __cplusplus >= 201703L
// cache_line_size = std::hardware_destructive_interference_size // cache_line_size = std::hardware_destructive_interference_size
// #else /*__cplusplus < 201703L*/ // #else /*__cplusplus < 201703L*/
cache_line_size = 64 cache_line_size = 64
// #endif/*__cplusplus < 201703L*/ // #endif/*__cplusplus < 201703L*/
}; };
} // namespace detail } // namespace detail
} // namespace ipc } // namespace ipc

View File

@ -1,172 +1,172 @@
#include "shm.h" #include "shm.h"
#include <sys/shm.h> #include <sys/shm.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <atomic> #include <atomic>
#include <string> #include <string>
#include <utility> #include <utility>
#include <cstring> #include <cstring>
#include "def.h" #include "def.h"
#include "log.h" #include "log.h"
#include "pool_alloc.h" #include "pool_alloc.h"
#include "memory/resource.h" #include "memory/resource.h"
namespace { namespace {
struct info_t { struct info_t {
std::atomic_size_t acc_; std::atomic_size_t acc_;
}; };
struct id_info_t { struct id_info_t {
int fd_ = -1; int fd_ = -1;
void* mem_ = nullptr; void* mem_ = nullptr;
std::size_t size_ = 0; std::size_t size_ = 0;
ipc::string name_; ipc::string name_;
}; };
constexpr std::size_t calc_size(std::size_t size) { constexpr std::size_t calc_size(std::size_t size) {
return ((((size - 1) / alignof(info_t)) + 1) * alignof(info_t)) + sizeof(info_t); return ((((size - 1) / alignof(info_t)) + 1) * alignof(info_t)) + sizeof(info_t);
} }
inline auto& acc_of(void* mem, std::size_t size) { inline auto& acc_of(void* mem, std::size_t size) {
return reinterpret_cast<info_t*>(static_cast<ipc::byte_t*>(mem) + size - sizeof(info_t))->acc_; return reinterpret_cast<info_t*>(static_cast<ipc::byte_t*>(mem) + size - sizeof(info_t))->acc_;
} }
} // internal-linkage } // internal-linkage
namespace ipc { namespace ipc {
namespace shm { namespace shm {
id_t acquire(char const * name, std::size_t size, unsigned mode) { id_t acquire(char const * name, std::size_t size, unsigned mode) {
if (name == nullptr || name[0] == '\0') { if (name == nullptr || name[0] == '\0') {
ipc::error("fail acquire: name is empty\n"); ipc::error("fail acquire: name is empty\n");
return nullptr; return nullptr;
} }
ipc::string op_name = ipc::string{"__IPC_SHM__"} + name; ipc::string op_name = ipc::string{"__IPC_SHM__"} + name;
// Open the object for read-write access. // Open the object for read-write access.
int flag = O_RDWR; int flag = O_RDWR;
switch (mode) { switch (mode) {
case open: case open:
size = 0; size = 0;
break; break;
// The check for the existence of the object, // The check for the existence of the object,
// and its creation if it does not exist, are performed atomically. // and its creation if it does not exist, are performed atomically.
case create: case create:
flag |= O_CREAT | O_EXCL; flag |= O_CREAT | O_EXCL;
break; break;
// Create the shared memory object if it does not exist. // Create the shared memory object if it does not exist.
default: default:
flag |= O_CREAT; flag |= O_CREAT;
break; break;
} }
int fd = ::shm_open(op_name.c_str(), flag, S_IRUSR | S_IWUSR | int fd = ::shm_open(op_name.c_str(), flag, S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP | S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH); S_IROTH | S_IWOTH);
if (fd == -1) { if (fd == -1) {
ipc::error("fail shm_open[%d]: %s\n", errno, name); ipc::error("fail shm_open[%d]: %s\n", errno, name);
return nullptr; return nullptr;
} }
auto ii = mem::alloc<id_info_t>(); auto ii = mem::alloc<id_info_t>();
ii->fd_ = fd; ii->fd_ = fd;
ii->size_ = size; ii->size_ = size;
ii->name_ = std::move(op_name); ii->name_ = std::move(op_name);
return ii; return ii;
} }
void * get_mem(id_t id, std::size_t * size) { void * get_mem(id_t id, std::size_t * size) {
if (id == nullptr) { if (id == nullptr) {
ipc::error("fail get_mem: invalid id (null)\n"); ipc::error("fail get_mem: invalid id (null)\n");
return nullptr; return nullptr;
} }
auto ii = static_cast<id_info_t*>(id); auto ii = static_cast<id_info_t*>(id);
if (ii->mem_ != nullptr) { if (ii->mem_ != nullptr) {
if (size != nullptr) *size = ii->size_; if (size != nullptr) *size = ii->size_;
return ii->mem_; return ii->mem_;
} }
int fd = ii->fd_; int fd = ii->fd_;
if (fd == -1) { if (fd == -1) {
ipc::error("fail to_mem: invalid id (fd = -1)\n"); ipc::error("fail to_mem: invalid id (fd = -1)\n");
return nullptr; return nullptr;
} }
if (ii->size_ == 0) { if (ii->size_ == 0) {
struct stat st; struct stat st;
if (::fstat(fd, &st) != 0) { if (::fstat(fd, &st) != 0) {
ipc::error("fail fstat[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_); ipc::error("fail fstat[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
return nullptr; return nullptr;
} }
ii->size_ = static_cast<std::size_t>(st.st_size); ii->size_ = static_cast<std::size_t>(st.st_size);
if ((ii->size_ <= sizeof(info_t)) || (ii->size_ % sizeof(info_t))) { if ((ii->size_ <= sizeof(info_t)) || (ii->size_ % sizeof(info_t))) {
ipc::error("fail to_mem: %s, invalid size = %zd\n", ii->name_.c_str(), ii->size_); ipc::error("fail to_mem: %s, invalid size = %zd\n", ii->name_.c_str(), ii->size_);
return nullptr; return nullptr;
} }
} }
else { else {
ii->size_ = calc_size(ii->size_); ii->size_ = calc_size(ii->size_);
if (::ftruncate(fd, static_cast<off_t>(ii->size_)) != 0) { if (::ftruncate(fd, static_cast<off_t>(ii->size_)) != 0) {
ipc::error("fail ftruncate[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_); ipc::error("fail ftruncate[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
return nullptr; return nullptr;
} }
} }
void* mem = ::mmap(nullptr, ii->size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); void* mem = ::mmap(nullptr, ii->size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mem == MAP_FAILED) { if (mem == MAP_FAILED) {
ipc::error("fail mmap[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_); ipc::error("fail mmap[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
return nullptr; return nullptr;
} }
::close(fd); ::close(fd);
ii->fd_ = -1; ii->fd_ = -1;
ii->mem_ = mem; ii->mem_ = mem;
if (size != nullptr) *size = ii->size_; if (size != nullptr) *size = ii->size_;
acc_of(mem, ii->size_).fetch_add(1, std::memory_order_release); acc_of(mem, ii->size_).fetch_add(1, std::memory_order_release);
return mem; return mem;
} }
void release(id_t id) { void release(id_t id) {
if (id == nullptr) { if (id == nullptr) {
ipc::error("fail release: invalid id (null)\n"); ipc::error("fail release: invalid id (null)\n");
return; return;
} }
auto ii = static_cast<id_info_t*>(id); auto ii = static_cast<id_info_t*>(id);
if (ii->mem_ == nullptr || ii->size_ == 0) { if (ii->mem_ == nullptr || ii->size_ == 0) {
ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_); ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_);
} }
else if (acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acquire) == 1) { else if (acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acquire) == 1) {
::munmap(ii->mem_, ii->size_); ::munmap(ii->mem_, ii->size_);
if (!ii->name_.empty()) { if (!ii->name_.empty()) {
::shm_unlink(ii->name_.c_str()); ::shm_unlink(ii->name_.c_str());
} }
} }
else ::munmap(ii->mem_, ii->size_); else ::munmap(ii->mem_, ii->size_);
mem::free(ii); mem::free(ii);
} }
void remove(id_t id) { void remove(id_t id) {
if (id == nullptr) { if (id == nullptr) {
ipc::error("fail remove: invalid id (null)\n"); ipc::error("fail remove: invalid id (null)\n");
return; return;
} }
auto ii = static_cast<id_info_t*>(id); auto ii = static_cast<id_info_t*>(id);
auto name = std::move(ii->name_); auto name = std::move(ii->name_);
release(id); release(id);
if (!name.empty()) { if (!name.empty()) {
::shm_unlink(name.c_str()); ::shm_unlink(name.c_str());
} }
} }
void remove(char const * name) { void remove(char const * name) {
if (name == nullptr || name[0] == '\0') { if (name == nullptr || name[0] == '\0') {
ipc::error("fail remove: name is empty\n"); ipc::error("fail remove: name is empty\n");
return; return;
} }
::shm_unlink((ipc::string{"__IPC_SHM__"} + name).c_str()); ::shm_unlink((ipc::string{"__IPC_SHM__"} + name).c_str());
} }
} // namespace shm } // namespace shm
} // namespace ipc } // namespace ipc

View File

@ -1,124 +1,124 @@
#include "shm.h" #include "shm.h"
#include <Windows.h> #include <Windows.h>
#include <string> #include <string>
#include <utility> #include <utility>
#include "def.h" #include "def.h"
#include "log.h" #include "log.h"
#include "pool_alloc.h" #include "pool_alloc.h"
#include "platform/to_tchar.h" #include "platform/to_tchar.h"
#include "memory/resource.h" #include "memory/resource.h"
namespace { namespace {
struct id_info_t { struct id_info_t {
HANDLE h_ = NULL; HANDLE h_ = NULL;
void* mem_ = nullptr; void* mem_ = nullptr;
std::size_t size_ = 0; std::size_t size_ = 0;
}; };
} // internal-linkage } // internal-linkage
namespace ipc { namespace ipc {
namespace shm { namespace shm {
id_t acquire(char const * name, std::size_t size, unsigned mode) { id_t acquire(char const * name, std::size_t size, unsigned mode) {
if (name == nullptr || name[0] == '\0') { if (name == nullptr || name[0] == '\0') {
ipc::error("fail acquire: name is empty\n"); ipc::error("fail acquire: name is empty\n");
return nullptr; return nullptr;
} }
HANDLE h; HANDLE h;
auto fmt_name = ipc::detail::to_tchar(ipc::string{"__IPC_SHM__"} + name); auto fmt_name = ipc::detail::to_tchar(ipc::string{"__IPC_SHM__"} + name);
// Opens a named file mapping object. // Opens a named file mapping object.
if (mode == open) { if (mode == open) {
h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, fmt_name.c_str()); h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, fmt_name.c_str());
} }
// Creates or opens a named file mapping object for a specified file. // Creates or opens a named file mapping object for a specified file.
else { else {
h = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, h = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT,
0, static_cast<DWORD>(size), fmt_name.c_str()); 0, static_cast<DWORD>(size), fmt_name.c_str());
// If the object exists before the function call, the function returns a handle to the existing object // If the object exists before the function call, the function returns a handle to the existing object
// (with its current size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS. // (with its current size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS.
if ((mode == create) && (::GetLastError() == ERROR_ALREADY_EXISTS)) { if ((mode == create) && (::GetLastError() == ERROR_ALREADY_EXISTS)) {
::CloseHandle(h); ::CloseHandle(h);
h = NULL; h = NULL;
} }
} }
if (h == NULL) { if (h == NULL) {
ipc::error("fail CreateFileMapping/OpenFileMapping[%d]: %s\n", static_cast<int>(::GetLastError()), name); ipc::error("fail CreateFileMapping/OpenFileMapping[%d]: %s\n", static_cast<int>(::GetLastError()), name);
return nullptr; return nullptr;
} }
auto ii = mem::alloc<id_info_t>(); auto ii = mem::alloc<id_info_t>();
ii->h_ = h; ii->h_ = h;
ii->size_ = size; ii->size_ = size;
return ii; return ii;
} }
void * get_mem(id_t id, std::size_t * size) { void * get_mem(id_t id, std::size_t * size) {
if (id == nullptr) { if (id == nullptr) {
ipc::error("fail get_mem: invalid id (null)\n"); ipc::error("fail get_mem: invalid id (null)\n");
return nullptr; return nullptr;
} }
auto ii = static_cast<id_info_t*>(id); auto ii = static_cast<id_info_t*>(id);
if (ii->mem_ != nullptr) { if (ii->mem_ != nullptr) {
if (size != nullptr) *size = ii->size_; if (size != nullptr) *size = ii->size_;
return ii->mem_; return ii->mem_;
} }
if (ii->h_ == NULL) { if (ii->h_ == NULL) {
ipc::error("fail to_mem: invalid id (h = null)\n"); ipc::error("fail to_mem: invalid id (h = null)\n");
return nullptr; return nullptr;
} }
LPVOID mem = ::MapViewOfFile(ii->h_, FILE_MAP_ALL_ACCESS, 0, 0, 0); LPVOID mem = ::MapViewOfFile(ii->h_, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (mem == NULL) { if (mem == NULL) {
ipc::error("fail MapViewOfFile[%d]\n", static_cast<int>(::GetLastError())); ipc::error("fail MapViewOfFile[%d]\n", static_cast<int>(::GetLastError()));
return nullptr; return nullptr;
} }
MEMORY_BASIC_INFORMATION mem_info; MEMORY_BASIC_INFORMATION mem_info;
if (::VirtualQuery(mem, &mem_info, sizeof(mem_info)) == 0) { if (::VirtualQuery(mem, &mem_info, sizeof(mem_info)) == 0) {
ipc::error("fail VirtualQuery[%d]\n", static_cast<int>(::GetLastError())); ipc::error("fail VirtualQuery[%d]\n", static_cast<int>(::GetLastError()));
return nullptr; return nullptr;
} }
ii->mem_ = mem; ii->mem_ = mem;
ii->size_ = static_cast<std::size_t>(mem_info.RegionSize); ii->size_ = static_cast<std::size_t>(mem_info.RegionSize);
if (size != nullptr) *size = ii->size_; if (size != nullptr) *size = ii->size_;
return static_cast<void *>(mem); return static_cast<void *>(mem);
} }
void release(id_t id) { void release(id_t id) {
if (id == nullptr) { if (id == nullptr) {
ipc::error("fail release: invalid id (null)\n"); ipc::error("fail release: invalid id (null)\n");
return; return;
} }
auto ii = static_cast<id_info_t*>(id); auto ii = static_cast<id_info_t*>(id);
if (ii->mem_ == nullptr || ii->size_ == 0) { if (ii->mem_ == nullptr || ii->size_ == 0) {
ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_); ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_);
} }
else ::UnmapViewOfFile(static_cast<LPCVOID>(ii->mem_)); else ::UnmapViewOfFile(static_cast<LPCVOID>(ii->mem_));
if (ii->h_ == NULL) { if (ii->h_ == NULL) {
ipc::error("fail release: invalid id (h = null)\n"); ipc::error("fail release: invalid id (h = null)\n");
} }
else ::CloseHandle(ii->h_); else ::CloseHandle(ii->h_);
mem::free(ii); mem::free(ii);
} }
void remove(id_t id) { void remove(id_t id) {
if (id == nullptr) { if (id == nullptr) {
ipc::error("fail release: invalid id (null)\n"); ipc::error("fail release: invalid id (null)\n");
return; return;
} }
release(id); release(id);
} }
void remove(char const * name) { void remove(char const * name) {
if (name == nullptr || name[0] == '\0') { if (name == nullptr || name[0] == '\0') {
ipc::error("fail remove: name is empty\n"); ipc::error("fail remove: name is empty\n");
return; return;
} }
// Do Nothing. // Do Nothing.
} }
} // namespace shm } // namespace shm
} // namespace ipc } // namespace ipc

View File

@ -1,29 +1,29 @@
#include "tls_pointer.h" #include "tls_pointer.h"
#include <pthread.h> // pthread_... #include <pthread.h> // pthread_...
namespace ipc { namespace ipc {
namespace tls { namespace tls {
key_t create(destructor_t destructor) { key_t create(destructor_t destructor) {
pthread_key_t k; pthread_key_t k;
if (pthread_key_create(&k, destructor) == 0) { if (pthread_key_create(&k, destructor) == 0) {
return static_cast<key_t>(k); return static_cast<key_t>(k);
} }
return invalid_value; return invalid_value;
} }
void release(key_t key) { void release(key_t key) {
pthread_key_delete(static_cast<pthread_key_t>(key)); pthread_key_delete(static_cast<pthread_key_t>(key));
} }
bool set(key_t key, void* ptr) { bool set(key_t key, void* ptr) {
return pthread_setspecific(static_cast<pthread_key_t>(key), ptr) == 0; return pthread_setspecific(static_cast<pthread_key_t>(key), ptr) == 0;
} }
void* get(key_t key) { void* get(key_t key) {
return pthread_getspecific(static_cast<pthread_key_t>(key)); return pthread_getspecific(static_cast<pthread_key_t>(key));
} }
} // namespace tls } // namespace tls
} // namespace ipc } // namespace ipc

View File

@ -1,183 +1,230 @@
#include "tls_pointer.h" #include "tls_pointer.h"
#include "log.h"
#include <Windows.h> // ::Tls...
#include <unordered_map> // std::unordered_map #include <Windows.h> // ::Tls...
#include <atomic>
namespace ipc { #include <unordered_set> // std::unordered_set
/* namespace ipc {
* <Remarks>
* /*
* Windows doesn't support a per-thread destructor with its TLS primitives. * <Remarks>
* So, here will build it manually by inserting a function to be called on each thread's exit. *
* * Windows doesn't support a per-thread destructor with its TLS primitives.
* <Reference> * So, here will build it manually by inserting a function to be called on each thread's exit.
* - https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way *
* - https://src.chromium.org/viewvc/chrome/trunk/src/base/threading/thread_local_storage_win.cc * <Reference>
* - https://github.com/mirror/mingw-org-wsl/blob/master/src/libcrt/crt/tlssup.c * - https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
* - https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-crt/crt/tlssup.c * - https://src.chromium.org/viewvc/chrome/trunk/src/base/threading/thread_local_storage_win.cc
* - http://svn.boost.org/svn/boost/trunk/libs/thread/src/win32/tss_pe.cpp * - https://github.com/mirror/mingw-org-wsl/blob/master/src/libcrt/crt/tlssup.c
*/ * - https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-crt/crt/tlssup.c
* - http://svn.boost.org/svn/boost/trunk/libs/thread/src/win32/tss_pe.cpp
namespace { */
struct tls_data { namespace {
using destructor_t = void(*)(void*);
using map_t = std::unordered_map<tls::key_t, tls_data>; struct tls_data {
using destructor_t = void(*)(void*);
static DWORD& key() {
static DWORD rec_key = ::TlsAlloc(); DWORD win_key_;
return rec_key; destructor_t destructor_;
}
void destruct(void* data) {
static map_t* records(map_t* rec) { if ((destructor_ != nullptr) && (data != nullptr)) {
::TlsSetValue(key(), static_cast<LPVOID>(rec)); destructor_(data);
return rec; }
} }
};
static map_t* records() {
return static_cast<map_t*>(::TlsGetValue(key())); using rec_t = std::unordered_set<tls_data*>;
}
DWORD& record_key() {
tls::key_t key_ = tls::invalid_value;
destructor_t destructor_ = nullptr; struct key_gen {
DWORD rec_key_;
tls_data() = default; key_gen() : rec_key_(::TlsAlloc()) {
if (rec_key_ == TLS_OUT_OF_INDEXES) {
tls_data(tls::key_t key, destructor_t destructor) ipc::error("[record_key] TlsAlloc failed[%lu].\n", ::GetLastError());
: key_ (key) }
, destructor_(destructor) }
{} ~key_gen() { ::TlsFree(rec_key_); }
};
tls_data(tls_data&& rhs) : tls_data() {
(*this) = std::move(rhs); static key_gen gen;
} return gen.rec_key_;
}
tls_data& operator=(tls_data&& rhs) {
key_ = rhs.key_; bool record(tls_data* tls) {
destructor_ = rhs.destructor_; auto rec = static_cast<rec_t*>(::TlsGetValue(record_key()));
rhs.key_ = 0; if (rec == nullptr) {
rhs.destructor_ = nullptr; if (FALSE == ::TlsSetValue(record_key(), static_cast<LPVOID>(rec = new rec_t))) {
return *this; ipc::error("[record] TlsSetValue failed[%lu].\n", ::GetLastError());
} return false;
}
~tls_data() { }
if (destructor_) destructor_(tls::get(key_)); rec->insert(tls);
} return true;
}; }
} // internal-linkage static void erase_record(tls_data* tls) {
auto rec = static_cast<rec_t*>(::TlsGetValue(record_key()));
namespace tls { if (rec == nullptr) return;
rec->erase(tls);
key_t create(destructor_t destructor) { }
key_t key = static_cast<key_t>(::TlsAlloc());
if (key == TLS_OUT_OF_INDEXES) return invalid_value; static void clear_all_records() {
auto rec = tls_data::records(); auto rec = static_cast<rec_t*>(::TlsGetValue(record_key()));
if (rec == nullptr) rec = tls_data::records(new tls_data::map_t); if (rec == nullptr) return;
if (rec == nullptr) return key; for (auto tls : *rec) {
rec->emplace(key, tls_data{ key, destructor }); if (tls != nullptr) {
return key; tls->destruct(::TlsGetValue(tls->win_key_));
} }
}
void release(key_t key) { delete rec;
auto rec = tls_data::records(); ::TlsSetValue(record_key(), static_cast<LPVOID>(nullptr));
if (rec == nullptr) return; }
rec->erase(key);
::TlsFree(static_cast<DWORD>(key)); } // internal-linkage
}
namespace tls {
bool set(key_t key, void* ptr) {
return ::TlsSetValue(static_cast<DWORD>(key), key_t create(destructor_t destructor) {
static_cast<LPVOID>(ptr)) == TRUE; record_key(); // gen record-key
} auto tls_dat = new tls_data { ::TlsAlloc(), destructor };
std::atomic_thread_fence(std::memory_order_seq_cst);
void* get(key_t key) { if (tls_dat->win_key_ == TLS_OUT_OF_INDEXES) {
return static_cast<void*>(::TlsGetValue(static_cast<DWORD>(key))); ipc::error("[tls::create] TlsAlloc failed[%lu].\n", ::GetLastError());
} delete tls_dat;
return invalid_value;
} // namespace tls }
return reinterpret_cast<key_t>(tls_dat);
namespace { }
void OnThreadExit() { void release(key_t tls_key) {
auto rec = tls_data::records(); if (tls_key == invalid_value) {
if (rec == nullptr) return; ipc::error("[tls::release] tls_key is invalid_value.\n");
delete rec; return;
tls_data::records(nullptr); }
} auto tls_dat = reinterpret_cast<tls_data*>(tls_key);
if (tls_dat == nullptr) {
void NTAPI OnTlsCallback(PVOID, DWORD dwReason, PVOID) { ipc::error("[tls::release] tls_dat is nullptr.\n");
if (dwReason == DLL_THREAD_DETACH) OnThreadExit(); return;
} }
erase_record(tls_dat);
} // internal-linkage ::TlsFree(tls_dat->win_key_);
delete tls_dat;
//////////////////////////////////////////////////////////////// }
/// Call destructors on thread exit
//////////////////////////////////////////////////////////////// bool set(key_t tls_key, void* ptr) {
if (tls_key == invalid_value) {
#if defined(_MSC_VER) ipc::error("[tls::set] tls_key is invalid_value.\n");
return false;
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) }
auto tls_dat = reinterpret_cast<tls_data*>(tls_key);
#pragma comment(linker, "/INCLUDE:_tls_used") if (tls_dat == nullptr) {
#pragma comment(linker, "/INCLUDE:_tls_xl_b__") ipc::error("[tls::set] tls_dat is nullptr.\n");
return false;
extern "C" { }
# pragma const_seg(".CRT$XLB") if (FALSE == ::TlsSetValue(tls_dat->win_key_, static_cast<LPVOID>(ptr))) {
extern const PIMAGE_TLS_CALLBACK _tls_xl_b__; ipc::error("[tls::set] TlsSetValue failed[%lu].\n", ::GetLastError());
const PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback; return false;
# pragma const_seg() }
} record(tls_dat);
return true;
#else /*!WIN64*/ }
#pragma comment(linker, "/INCLUDE:__tls_used") void* get(key_t tls_key) {
#pragma comment(linker, "/INCLUDE:__tls_xl_b__") if (tls_key == invalid_value) {
ipc::error("[tls::get] tls_key is invalid_value.\n");
extern "C" { return nullptr;
# pragma data_seg(".CRT$XLB") }
PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback; auto tls_dat = reinterpret_cast<tls_data*>(tls_key);
# pragma data_seg() if (tls_dat == nullptr) {
} ipc::error("[tls::get] tls_dat is nullptr.\n");
return nullptr;
#endif/*!WIN64*/ }
return ::TlsGetValue(tls_dat->win_key_);
#elif defined(__GNUC__) }
#define IPC_CRTALLOC__(x) __attribute__ ((section (x) )) } // namespace tls
#if defined(__MINGW64__) || (__MINGW64_VERSION_MAJOR) || \ namespace {
(__MINGW32_MAJOR_VERSION > 3) || ((__MINGW32_MAJOR_VERSION == 3) && (__MINGW32_MINOR_VERSION >= 18))
void OnThreadExit() {
extern "C" { clear_all_records();
IPC_CRTALLOC__(".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback; }
}
void NTAPI OnTlsCallback(PVOID, DWORD dwReason, PVOID) {
#else /*!MINGW*/ if (dwReason == DLL_THREAD_DETACH) OnThreadExit();
}
extern "C" {
ULONG _tls_index__ = 0; } // internal-linkage
IPC_CRTALLOC__(".tls$AAA") char _tls_start__ = 0; ////////////////////////////////////////////////////////////////
IPC_CRTALLOC__(".tls$ZZZ") char _tls_end__ = 0; /// Call destructors on thread exit
////////////////////////////////////////////////////////////////
IPC_CRTALLOC__(".CRT$XLA") PIMAGE_TLS_CALLBACK _tls_xl_a__ = 0;
IPC_CRTALLOC__(".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback; #if defined(_MSC_VER)
IPC_CRTALLOC__(".CRT$XLZ") PIMAGE_TLS_CALLBACK _tls_xl_z__ = 0;
} #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
extern "C" NX_CRTALLOC_(".tls") const IMAGE_TLS_DIRECTORY _tls_used = { #pragma comment(linker, "/INCLUDE:_tls_used")
(ULONG_PTR)(&_tls_start__ + 1), #pragma comment(linker, "/INCLUDE:_tls_xl_b__")
(ULONG_PTR) &_tls_end__,
(ULONG_PTR) &_tls_index__, extern "C" {
(ULONG_PTR) &_tls_xl_b__, # pragma const_seg(".CRT$XLB")
(ULONG)0, (ULONG)0 extern const PIMAGE_TLS_CALLBACK _tls_xl_b__;
} const PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
# pragma const_seg()
#endif/*!MINGW*/ }
#endif/*_MSC_VER, __GNUC__*/ #else /*!WIN64*/
} // namespace ipc #pragma comment(linker, "/INCLUDE:__tls_used")
#pragma comment(linker, "/INCLUDE:__tls_xl_b__")
extern "C" {
# pragma data_seg(".CRT$XLB")
PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
# pragma data_seg()
}
#endif/*!WIN64*/
#elif defined(__GNUC__)
#define IPC_CRTALLOC__(x) __attribute__ ((section (x) ))
#if defined(__MINGW64__) || (__MINGW64_VERSION_MAJOR) || \
(__MINGW32_MAJOR_VERSION > 3) || ((__MINGW32_MAJOR_VERSION == 3) && (__MINGW32_MINOR_VERSION >= 18))
extern "C" {
IPC_CRTALLOC__(".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
}
#else /*!MINGW*/
extern "C" {
ULONG _tls_index__ = 0;
IPC_CRTALLOC__(".tls$AAA") char _tls_start__ = 0;
IPC_CRTALLOC__(".tls$ZZZ") char _tls_end__ = 0;
IPC_CRTALLOC__(".CRT$XLA") PIMAGE_TLS_CALLBACK _tls_xl_a__ = 0;
IPC_CRTALLOC__(".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
IPC_CRTALLOC__(".CRT$XLZ") PIMAGE_TLS_CALLBACK _tls_xl_z__ = 0;
}
extern "C" NX_CRTALLOC_(".tls") const IMAGE_TLS_DIRECTORY _tls_used = {
(ULONG_PTR)(&_tls_start__ + 1),
(ULONG_PTR) &_tls_end__,
(ULONG_PTR) &_tls_index__,
(ULONG_PTR) &_tls_xl_b__,
(ULONG)0, (ULONG)0
}
#endif/*!MINGW*/
#endif/*_MSC_VER, __GNUC__*/
} // namespace ipc

View File

@ -1,67 +1,67 @@
#pragma once #pragma once
#include <tchar.h> #include <tchar.h>
#include <type_traits> #include <type_traits>
#include <string> #include <string>
#include <locale> #include <locale>
#include <codecvt> #include <codecvt>
#include <cstring> #include <cstring>
#include "concept.h" #include "concept.h"
#include "platform/detail.h" #include "platform/detail.h"
#include "memory/resource.h" #include "memory/resource.h"
namespace ipc::detail { namespace ipc::detail {
struct has_value_type_ { struct has_value_type_ {
template <typename T> static std::true_type check(typename T::value_type *); template <typename T> static std::true_type check(typename T::value_type *);
template <typename T> static std::false_type check(...); template <typename T> static std::false_type check(...);
}; };
template <typename T, typename U, typename = decltype(has_value_type_::check<U>(nullptr))> template <typename T, typename U, typename = decltype(has_value_type_::check<U>(nullptr))>
struct is_same_char : std::is_same<T, U> {}; struct is_same_char : std::is_same<T, U> {};
template <typename T, typename U> 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::require<is_same_char<T, S>::value, R>; using IsSameChar = ipc::require<is_same_char<T, S>::value, R>;
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// to_tchar implementation /// to_tchar implementation
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
template <typename T = TCHAR> template <typename T = TCHAR>
constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::string, ipc::string &&> { constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::string, ipc::string &&> {
return std::move(str); return std::move(str);
} }
template <typename T = TCHAR> template <typename T = TCHAR>
constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::wstring> { constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::wstring> {
return std::wstring_convert< return std::wstring_convert<
std::codecvt_utf8_utf16<wchar_t>, std::codecvt_utf8_utf16<wchar_t>,
wchar_t, wchar_t,
ipc::mem::allocator<wchar_t>, ipc::mem::allocator<wchar_t>,
ipc::mem::allocator<char> ipc::mem::allocator<char>
>{}.from_bytes(std::move(str)); >{}.from_bytes(std::move(str));
} }
template <typename T> template <typename T>
inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, char, void> { inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, char, void> {
std::memcpy(dst, src, size); std::memcpy(dst, src, size);
} }
template <typename T> template <typename T>
inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, wchar_t, void> { inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, wchar_t, void> {
auto wstr = std::wstring_convert< auto wstr = std::wstring_convert<
std::codecvt_utf8_utf16<wchar_t>, std::codecvt_utf8_utf16<wchar_t>,
wchar_t, wchar_t,
ipc::mem::allocator<wchar_t>, ipc::mem::allocator<wchar_t>,
ipc::mem::allocator<char> ipc::mem::allocator<char>
>{}.from_bytes(src, src + size); >{}.from_bytes(src, src + size);
std::memcpy(dst, wstr.data(), (ipc::detail::min)(wstr.size(), size)); std::memcpy(dst, wstr.data(), (ipc::detail::min)(wstr.size(), size));
} }
} // namespace ipc::detail } // namespace ipc::detail

View File

@ -1,347 +1,347 @@
#pragma once #pragma once
#include <pthread.h> #include <pthread.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <semaphore.h> #include <semaphore.h>
#include <errno.h> #include <errno.h>
#include <time.h> #include <time.h>
#include <atomic> #include <atomic>
#include <tuple> #include <tuple>
#include "def.h" #include "def.h"
#include "log.h" #include "log.h"
#include "platform/detail.h" #include "platform/detail.h"
#include "memory/resource.h" #include "memory/resource.h"
namespace ipc { namespace ipc {
namespace detail { namespace detail {
inline static void calc_wait_time(timespec& ts, std::size_t tm) { inline static void calc_wait_time(timespec& ts, std::size_t tm) {
::clock_gettime(CLOCK_REALTIME, &ts); ::clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_nsec += tm * 1000000; // nanoseconds ts.tv_nsec += tm * 1000000; // nanoseconds
ts.tv_sec += ts.tv_nsec / 1000000000; ts.tv_sec += ts.tv_nsec / 1000000000;
ts.tv_nsec %= 1000000000; ts.tv_nsec %= 1000000000;
} }
#pragma push_macro("IPC_PTHREAD_FUNC_") #pragma push_macro("IPC_PTHREAD_FUNC_")
#undef IPC_PTHREAD_FUNC_ #undef IPC_PTHREAD_FUNC_
#define IPC_PTHREAD_FUNC_(CALL, ...) \ #define IPC_PTHREAD_FUNC_(CALL, ...) \
int eno; \ int eno; \
if ((eno = ::CALL(__VA_ARGS__)) != 0) { \ if ((eno = ::CALL(__VA_ARGS__)) != 0) { \
ipc::error("fail " #CALL "[%d]\n", eno); \ ipc::error("fail " #CALL "[%d]\n", eno); \
return false; \ return false; \
} \ } \
return true return true
class mutex { class mutex {
pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
public: public:
pthread_mutex_t& native() { pthread_mutex_t& native() {
return mutex_; return mutex_;
} }
bool open() { bool open() {
int eno; int eno;
// init mutex // init mutex
pthread_mutexattr_t mutex_attr; pthread_mutexattr_t mutex_attr;
if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) { if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) {
ipc::error("fail pthread_mutexattr_init[%d]\n", eno); ipc::error("fail pthread_mutexattr_init[%d]\n", eno);
return false; return false;
} }
IPC_UNUSED_ auto guard_mutex_attr = unique_ptr(&mutex_attr, ::pthread_mutexattr_destroy); IPC_UNUSED_ auto guard_mutex_attr = unique_ptr(&mutex_attr, ::pthread_mutexattr_destroy);
if ((eno = ::pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0) { if ((eno = ::pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0) {
ipc::error("fail pthread_mutexattr_setpshared[%d]\n", eno); ipc::error("fail pthread_mutexattr_setpshared[%d]\n", eno);
return false; return false;
} }
if ((eno = ::pthread_mutex_init(&mutex_, &mutex_attr)) != 0) { if ((eno = ::pthread_mutex_init(&mutex_, &mutex_attr)) != 0) {
ipc::error("fail pthread_mutex_init[%d]\n", eno); ipc::error("fail pthread_mutex_init[%d]\n", eno);
return false; return false;
} }
return true; return true;
} }
bool close() { bool close() {
IPC_PTHREAD_FUNC_(pthread_mutex_destroy, &mutex_); IPC_PTHREAD_FUNC_(pthread_mutex_destroy, &mutex_);
} }
bool lock() { bool lock() {
IPC_PTHREAD_FUNC_(pthread_mutex_lock, &mutex_); IPC_PTHREAD_FUNC_(pthread_mutex_lock, &mutex_);
} }
bool unlock() { bool unlock() {
IPC_PTHREAD_FUNC_(pthread_mutex_unlock, &mutex_); IPC_PTHREAD_FUNC_(pthread_mutex_unlock, &mutex_);
} }
}; };
class condition { class condition {
pthread_cond_t cond_ = PTHREAD_COND_INITIALIZER; pthread_cond_t cond_ = PTHREAD_COND_INITIALIZER;
public: public:
bool open() { bool open() {
int eno; int eno;
// init condition // init condition
pthread_condattr_t cond_attr; pthread_condattr_t cond_attr;
if ((eno = ::pthread_condattr_init(&cond_attr)) != 0) { if ((eno = ::pthread_condattr_init(&cond_attr)) != 0) {
ipc::error("fail pthread_condattr_init[%d]\n", eno); ipc::error("fail pthread_condattr_init[%d]\n", eno);
return false; return false;
} }
IPC_UNUSED_ auto guard_cond_attr = unique_ptr(&cond_attr, ::pthread_condattr_destroy); IPC_UNUSED_ auto guard_cond_attr = unique_ptr(&cond_attr, ::pthread_condattr_destroy);
if ((eno = ::pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED)) != 0) { if ((eno = ::pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED)) != 0) {
ipc::error("fail pthread_condattr_setpshared[%d]\n", eno); ipc::error("fail pthread_condattr_setpshared[%d]\n", eno);
return false; return false;
} }
if ((eno = ::pthread_cond_init(&cond_, &cond_attr)) != 0) { if ((eno = ::pthread_cond_init(&cond_, &cond_attr)) != 0) {
ipc::error("fail pthread_cond_init[%d]\n", eno); ipc::error("fail pthread_cond_init[%d]\n", eno);
return false; return false;
} }
return true; return true;
} }
bool close() { bool close() {
IPC_PTHREAD_FUNC_(pthread_cond_destroy, &cond_); IPC_PTHREAD_FUNC_(pthread_cond_destroy, &cond_);
} }
bool wait(mutex& mtx, std::size_t tm = invalid_value) { bool wait(mutex& mtx, std::size_t tm = invalid_value) {
switch (tm) { switch (tm) {
case 0: case 0:
return true; return true;
case invalid_value: case invalid_value:
IPC_PTHREAD_FUNC_(pthread_cond_wait, &cond_, &mtx.native()); IPC_PTHREAD_FUNC_(pthread_cond_wait, &cond_, &mtx.native());
default: { default: {
timespec ts; timespec ts;
calc_wait_time(ts, tm); calc_wait_time(ts, tm);
int eno; int eno;
if ((eno = ::pthread_cond_timedwait(&cond_, &mtx.native(), &ts)) != 0) { if ((eno = ::pthread_cond_timedwait(&cond_, &mtx.native(), &ts)) != 0) {
if (eno != ETIMEDOUT) { if (eno != ETIMEDOUT) {
ipc::error("fail pthread_cond_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n", ipc::error("fail pthread_cond_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
eno, tm, ts.tv_sec, ts.tv_nsec); eno, tm, ts.tv_sec, ts.tv_nsec);
} }
return false; return false;
} }
} }
return true; return true;
} }
} }
bool notify() { bool notify() {
IPC_PTHREAD_FUNC_(pthread_cond_signal, &cond_); IPC_PTHREAD_FUNC_(pthread_cond_signal, &cond_);
} }
bool broadcast() { bool broadcast() {
IPC_PTHREAD_FUNC_(pthread_cond_broadcast, &cond_); IPC_PTHREAD_FUNC_(pthread_cond_broadcast, &cond_);
} }
}; };
#pragma pop_macro("IPC_PTHREAD_FUNC_") #pragma pop_macro("IPC_PTHREAD_FUNC_")
class sem_helper { class sem_helper {
public: public:
using handle_t = sem_t*; using handle_t = sem_t*;
constexpr static handle_t invalid() noexcept { constexpr static handle_t invalid() noexcept {
return SEM_FAILED; return SEM_FAILED;
} }
static handle_t open(char const* name, long count) { static handle_t open(char const* name, long count) {
handle_t sem = ::sem_open(name, O_CREAT, 0666, count); handle_t sem = ::sem_open(name, O_CREAT, 0666, count);
if (sem == SEM_FAILED) { if (sem == SEM_FAILED) {
ipc::error("fail sem_open[%d]: %s\n", errno, name); ipc::error("fail sem_open[%d]: %s\n", errno, name);
return invalid(); return invalid();
} }
return sem; return sem;
} }
#pragma push_macro("IPC_SEMAPHORE_FUNC_") #pragma push_macro("IPC_SEMAPHORE_FUNC_")
#undef IPC_SEMAPHORE_FUNC_ #undef IPC_SEMAPHORE_FUNC_
#define IPC_SEMAPHORE_FUNC_(CALL, ...) \ #define IPC_SEMAPHORE_FUNC_(CALL, ...) \
if (::CALL(__VA_ARGS__) != 0) { \ if (::CALL(__VA_ARGS__) != 0) { \
ipc::error("fail " #CALL "[%d]\n", errno); \ ipc::error("fail " #CALL "[%d]\n", errno); \
return false; \ return false; \
} \ } \
return true return true
static bool close(handle_t h) { static bool close(handle_t h) {
if (h == invalid()) return false; if (h == invalid()) return false;
IPC_SEMAPHORE_FUNC_(sem_close, h); IPC_SEMAPHORE_FUNC_(sem_close, h);
} }
static bool destroy(char const* name) { static bool destroy(char const* name) {
IPC_SEMAPHORE_FUNC_(sem_unlink, name); IPC_SEMAPHORE_FUNC_(sem_unlink, name);
} }
static bool post(handle_t h) { static bool post(handle_t h) {
if (h == invalid()) return false; if (h == invalid()) return false;
IPC_SEMAPHORE_FUNC_(sem_post, h); IPC_SEMAPHORE_FUNC_(sem_post, h);
} }
static bool wait(handle_t h, std::size_t tm = invalid_value) { static bool wait(handle_t h, std::size_t tm = invalid_value) {
if (h == invalid()) return false; if (h == invalid()) return false;
switch (tm) { switch (tm) {
case 0: case 0:
return true; return true;
case invalid_value: case invalid_value:
IPC_SEMAPHORE_FUNC_(sem_wait, h); IPC_SEMAPHORE_FUNC_(sem_wait, h);
default: { default: {
timespec ts; timespec ts;
calc_wait_time(ts, tm); calc_wait_time(ts, tm);
if (::sem_timedwait(h, &ts) != 0) { if (::sem_timedwait(h, &ts) != 0) {
if (errno != ETIMEDOUT) { if (errno != ETIMEDOUT) {
ipc::error("fail sem_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n", ipc::error("fail sem_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
errno, tm, ts.tv_sec, ts.tv_nsec); errno, tm, ts.tv_sec, ts.tv_nsec);
} }
return false; return false;
} }
} }
return true; return true;
} }
} }
#pragma pop_macro("IPC_SEMAPHORE_FUNC_") #pragma pop_macro("IPC_SEMAPHORE_FUNC_")
}; };
class waiter_helper { class waiter_helper {
mutex lock_; mutex lock_;
std::atomic<unsigned> waiting_ { 0 }; std::atomic<unsigned> waiting_ { 0 };
long counter_ = 0; long counter_ = 0;
public: public:
using handle_t = std::tuple<ipc::string, sem_helper::handle_t, sem_helper::handle_t>; using handle_t = std::tuple<ipc::string, sem_helper::handle_t, sem_helper::handle_t>;
static handle_t invalid() noexcept { static handle_t invalid() noexcept {
return std::make_tuple(ipc::string{}, sem_helper::invalid(), sem_helper::invalid()); return std::make_tuple(ipc::string{}, sem_helper::invalid(), sem_helper::invalid());
} }
handle_t open_h(ipc::string && name) { handle_t open_h(ipc::string && name) {
auto sem = sem_helper::open(("__WAITER_HELPER_SEM__" + name).c_str(), 0); auto sem = sem_helper::open(("__WAITER_HELPER_SEM__" + name).c_str(), 0);
if (sem == sem_helper::invalid()) { if (sem == sem_helper::invalid()) {
return invalid(); return invalid();
} }
auto han = sem_helper::open(("__WAITER_HELPER_HAN__" + name).c_str(), 0); auto han = sem_helper::open(("__WAITER_HELPER_HAN__" + name).c_str(), 0);
if (han == sem_helper::invalid()) { if (han == sem_helper::invalid()) {
return invalid(); return invalid();
} }
return std::make_tuple(std::move(name), sem, han); return std::make_tuple(std::move(name), sem, han);
} }
void release_h(handle_t const & h) { void release_h(handle_t const & h) {
sem_helper::close(std::get<2>(h)); sem_helper::close(std::get<2>(h));
sem_helper::close(std::get<1>(h)); sem_helper::close(std::get<1>(h));
} }
void close_h(handle_t const & h) { void close_h(handle_t const & h) {
auto const & name = std::get<0>(h); auto const & name = std::get<0>(h);
sem_helper::destroy(("__WAITER_HELPER_HAN__" + name).c_str()); sem_helper::destroy(("__WAITER_HELPER_HAN__" + name).c_str());
sem_helper::destroy(("__WAITER_HELPER_SEM__" + name).c_str()); sem_helper::destroy(("__WAITER_HELPER_SEM__" + name).c_str());
} }
bool open() { bool open() {
return lock_.open(); return lock_.open();
} }
void close() { void close() {
lock_.close(); lock_.close();
} }
template <typename F> template <typename F>
bool wait_if(handle_t const & h, F&& pred, std::size_t tm = invalid_value) { bool wait_if(handle_t const & h, F&& pred, std::size_t tm = invalid_value) {
waiting_.fetch_add(1, std::memory_order_release); waiting_.fetch_add(1, std::memory_order_release);
{ {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
if (!std::forward<F>(pred)()) return true; if (!std::forward<F>(pred)()) return true;
++ counter_; ++ counter_;
} }
bool ret = sem_helper::wait(std::get<1>(h), tm); bool ret = sem_helper::wait(std::get<1>(h), tm);
waiting_.fetch_sub(1, std::memory_order_release); waiting_.fetch_sub(1, std::memory_order_release);
ret = sem_helper::post(std::get<2>(h)) && ret; ret = sem_helper::post(std::get<2>(h)) && ret;
return ret; return ret;
} }
bool notify(handle_t const & h) { bool notify(handle_t const & h) {
std::atomic_thread_fence(std::memory_order_acq_rel); std::atomic_thread_fence(std::memory_order_acq_rel);
if (waiting_.load(std::memory_order_relaxed) == 0) { if (waiting_.load(std::memory_order_relaxed) == 0) {
return true; return true;
} }
bool ret = true; bool ret = true;
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
if (counter_ > 0) { if (counter_ > 0) {
ret = sem_helper::post(std::get<1>(h)); ret = sem_helper::post(std::get<1>(h));
-- counter_; -- counter_;
ret = ret && sem_helper::wait(std::get<2>(h), default_timeut); ret = ret && sem_helper::wait(std::get<2>(h), default_timeut);
} }
return ret; return ret;
} }
bool broadcast(handle_t const & h) { bool broadcast(handle_t const & h) {
std::atomic_thread_fence(std::memory_order_acq_rel); std::atomic_thread_fence(std::memory_order_acq_rel);
if (waiting_.load(std::memory_order_relaxed) == 0) { if (waiting_.load(std::memory_order_relaxed) == 0) {
return true; return true;
} }
bool ret = true; bool ret = true;
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
if (counter_ > 0) { if (counter_ > 0) {
for (long i = 0; i < counter_; ++i) { for (long i = 0; i < counter_; ++i) {
ret = ret && sem_helper::post(std::get<1>(h)); ret = ret && sem_helper::post(std::get<1>(h));
} }
do { do {
-- counter_; -- counter_;
ret = ret && sem_helper::wait(std::get<2>(h), default_timeut); ret = ret && sem_helper::wait(std::get<2>(h), default_timeut);
} while (counter_ > 0); } while (counter_ > 0);
} }
return ret; return ret;
} }
}; };
class waiter { class waiter {
waiter_helper helper_; waiter_helper helper_;
std::atomic<unsigned> opened_ { 0 }; std::atomic<unsigned> opened_ { 0 };
public: public:
using handle_t = waiter_helper::handle_t; using handle_t = waiter_helper::handle_t;
static handle_t invalid() noexcept { static handle_t invalid() noexcept {
return waiter_helper::invalid(); return waiter_helper::invalid();
} }
handle_t open(char const * name) { handle_t open(char const * name) {
if (name == nullptr || name[0] == '\0') { if (name == nullptr || name[0] == '\0') {
return invalid(); return invalid();
} }
if ((opened_.fetch_add(1, std::memory_order_acq_rel) == 0) && !helper_.open()) { if ((opened_.fetch_add(1, std::memory_order_acq_rel) == 0) && !helper_.open()) {
return invalid(); return invalid();
} }
return helper_.open_h(name); return helper_.open_h(name);
} }
void close(handle_t h) { void close(handle_t h) {
if (h == invalid()) return; if (h == invalid()) return;
helper_.release_h(h); helper_.release_h(h);
if (opened_.fetch_sub(1, std::memory_order_release) == 1) { if (opened_.fetch_sub(1, std::memory_order_release) == 1) {
helper_.close_h(h); helper_.close_h(h);
helper_.close(); helper_.close();
} }
} }
template <typename F> template <typename F>
bool wait_if(handle_t h, F&& pred, std::size_t tm = invalid_value) { bool wait_if(handle_t h, F&& pred, std::size_t tm = invalid_value) {
if (h == invalid()) return false; if (h == invalid()) return false;
return helper_.wait_if(h, std::forward<F>(pred), tm); return helper_.wait_if(h, std::forward<F>(pred), tm);
} }
void notify(handle_t h) { void notify(handle_t h) {
if (h == invalid()) return; if (h == invalid()) return;
helper_.notify(h); helper_.notify(h);
} }
void broadcast(handle_t h) { void broadcast(handle_t h) {
if (h == invalid()) return; if (h == invalid()) return;
helper_.broadcast(h); helper_.broadcast(h);
} }
}; };
} // namespace detail } // namespace detail
} // namespace ipc } // namespace ipc

View File

@ -1,215 +1,215 @@
#pragma once #pragma once
#include <Windows.h> #include <Windows.h>
#include <atomic> #include <atomic>
#include <tuple> #include <tuple>
#include "rw_lock.h" #include "rw_lock.h"
#include "pool_alloc.h" #include "pool_alloc.h"
#include "log.h" #include "log.h"
#include "shm.h" #include "shm.h"
#include "platform/to_tchar.h" #include "platform/to_tchar.h"
#include "platform/detail.h" #include "platform/detail.h"
#include "memory/resource.h" #include "memory/resource.h"
namespace ipc { namespace ipc {
namespace detail { namespace detail {
class semaphore { class semaphore {
HANDLE h_ = NULL; HANDLE h_ = NULL;
public: public:
static void remove(char const * /*name*/) {} static void remove(char const * /*name*/) {}
bool open(ipc::string && name, long count = 0, long limit = LONG_MAX) { bool open(ipc::string && name, long count = 0, long limit = LONG_MAX) {
h_ = ::CreateSemaphore(NULL, count, limit, ipc::detail::to_tchar(std::move(name)).c_str()); h_ = ::CreateSemaphore(NULL, count, limit, ipc::detail::to_tchar(std::move(name)).c_str());
if (h_ == NULL) { if (h_ == NULL) {
ipc::error("fail CreateSemaphore[%lu]: %s\n", ::GetLastError(), name.c_str()); ipc::error("fail CreateSemaphore[%lu]: %s\n", ::GetLastError(), name.c_str());
return false; return false;
} }
return true; return true;
} }
void close() { void close() {
::CloseHandle(h_); ::CloseHandle(h_);
} }
bool wait(std::size_t tm = invalid_value) { bool wait(std::size_t tm = invalid_value) {
DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm); DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
switch ((ret = ::WaitForSingleObject(h_, ms))) { switch ((ret = ::WaitForSingleObject(h_, ms))) {
case WAIT_OBJECT_0: case WAIT_OBJECT_0:
return true; return true;
case WAIT_ABANDONED: case WAIT_ABANDONED:
case WAIT_TIMEOUT: case WAIT_TIMEOUT:
default: default:
ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret); ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret);
return false; return false;
} }
} }
bool post(long count = 1) { bool post(long count = 1) {
if (::ReleaseSemaphore(h_, count, NULL)) { if (::ReleaseSemaphore(h_, count, NULL)) {
return true; return true;
} }
ipc::error("fail ReleaseSemaphore[%lu]\n", ::GetLastError()); ipc::error("fail ReleaseSemaphore[%lu]\n", ::GetLastError());
return false; return false;
} }
}; };
class mutex : public semaphore { class mutex : public semaphore {
using semaphore::wait; using semaphore::wait;
using semaphore::post; using semaphore::post;
public: public:
bool open(ipc::string && name) { bool open(ipc::string && name) {
return semaphore::open(std::move(name), 1, 1); return semaphore::open(std::move(name), 1, 1);
} }
bool lock () { return semaphore::wait(); } bool lock () { return semaphore::wait(); }
bool unlock() { return semaphore::post(); } bool unlock() { return semaphore::post(); }
}; };
class condition { class condition {
mutex lock_; mutex lock_;
semaphore sema_, handshake_; semaphore sema_, handshake_;
std::atomic<unsigned> * waiting_ = nullptr; std::atomic<unsigned> * waiting_ = nullptr;
long * counter_ = nullptr; long * counter_ = nullptr;
public: public:
friend bool operator==(condition const & c1, condition const & c2) { friend bool operator==(condition const & c1, condition const & c2) {
return (c1.waiting_ == c2.waiting_) && (c1.counter_ == c2.counter_); return (c1.waiting_ == c2.waiting_) && (c1.counter_ == c2.counter_);
} }
friend bool operator!=(condition const & c1, condition const & c2) { friend bool operator!=(condition const & c1, condition const & c2) {
return !(c1 == c2); return !(c1 == c2);
} }
static void remove(char const * name) { static void remove(char const * name) {
semaphore::remove((ipc::string{ "__COND_HAN__" } + name).c_str()); semaphore::remove((ipc::string{ "__COND_HAN__" } + name).c_str());
semaphore::remove((ipc::string{ "__COND_SEM__" } + name).c_str()); semaphore::remove((ipc::string{ "__COND_SEM__" } + name).c_str());
mutex ::remove((ipc::string{ "__COND_MTX__" } + name).c_str()); mutex ::remove((ipc::string{ "__COND_MTX__" } + name).c_str());
} }
bool open(ipc::string const & name, std::atomic<unsigned> * waiting, long * counter) { bool open(ipc::string const & name, std::atomic<unsigned> * waiting, long * counter) {
if (lock_ .open("__COND_MTX__" + name) && if (lock_ .open("__COND_MTX__" + name) &&
sema_ .open("__COND_SEM__" + name) && sema_ .open("__COND_SEM__" + name) &&
handshake_.open("__COND_HAN__" + name)) { handshake_.open("__COND_HAN__" + name)) {
waiting_ = waiting; waiting_ = waiting;
counter_ = counter; counter_ = counter;
return true; return true;
} }
return false; return false;
} }
void close() { void close() {
handshake_.close(); handshake_.close();
sema_ .close(); sema_ .close();
lock_ .close(); lock_ .close();
} }
template <typename Mutex, typename F> template <typename Mutex, typename F>
bool wait_if(Mutex& mtx, F&& pred, std::size_t tm = invalid_value) { bool wait_if(Mutex& mtx, F&& pred, std::size_t tm = invalid_value) {
waiting_->fetch_add(1, std::memory_order_release); waiting_->fetch_add(1, std::memory_order_release);
{ {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
if (!std::forward<F>(pred)()) return true; if (!std::forward<F>(pred)()) return true;
++ *counter_; ++ *counter_;
} }
mtx.unlock(); mtx.unlock();
bool ret = sema_.wait(tm); bool ret = sema_.wait(tm);
waiting_->fetch_sub(1, std::memory_order_release); waiting_->fetch_sub(1, std::memory_order_release);
ret = handshake_.post() && ret; ret = handshake_.post() && ret;
mtx.lock(); mtx.lock();
return ret; return ret;
} }
bool notify() { bool notify() {
std::atomic_thread_fence(std::memory_order_acq_rel); std::atomic_thread_fence(std::memory_order_acq_rel);
if (waiting_->load(std::memory_order_relaxed) == 0) { if (waiting_->load(std::memory_order_relaxed) == 0) {
return true; return true;
} }
bool ret = true; bool ret = true;
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
if (*counter_ > 0) { if (*counter_ > 0) {
ret = sema_.post(); ret = sema_.post();
-- *counter_; -- *counter_;
ret = ret && handshake_.wait(default_timeut); ret = ret && handshake_.wait(default_timeut);
} }
return ret; return ret;
} }
bool broadcast() { bool broadcast() {
std::atomic_thread_fence(std::memory_order_acq_rel); std::atomic_thread_fence(std::memory_order_acq_rel);
if (waiting_->load(std::memory_order_relaxed) == 0) { if (waiting_->load(std::memory_order_relaxed) == 0) {
return true; return true;
} }
bool ret = true; bool ret = true;
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
if (*counter_ > 0) { if (*counter_ > 0) {
ret = sema_.post(*counter_); ret = sema_.post(*counter_);
do { do {
-- *counter_; -- *counter_;
ret = ret && handshake_.wait(default_timeut); ret = ret && handshake_.wait(default_timeut);
} while (*counter_ > 0); } while (*counter_ > 0);
} }
return ret; return ret;
} }
}; };
class waiter { class waiter {
std::atomic<unsigned> waiting_ { 0 }; std::atomic<unsigned> waiting_ { 0 };
long counter_ = 0; long counter_ = 0;
public: public:
using handle_t = condition; using handle_t = condition;
static handle_t invalid() { static handle_t invalid() {
return condition {}; return condition {};
} }
handle_t open(char const * name) { handle_t open(char const * name) {
if (name == nullptr || name[0] == '\0') { if (name == nullptr || name[0] == '\0') {
return invalid(); return invalid();
} }
condition cond; condition cond;
if (cond.open(name, &waiting_, &counter_)) { if (cond.open(name, &waiting_, &counter_)) {
return cond; return cond;
} }
return invalid(); return invalid();
} }
void close(handle_t& h) { void close(handle_t& h) {
if (h == invalid()) return; if (h == invalid()) return;
h.close(); h.close();
} }
template <typename F> template <typename F>
bool wait_if(handle_t& h, F&& pred, std::size_t tm = invalid_value) { bool wait_if(handle_t& h, F&& pred, std::size_t tm = invalid_value) {
if (h == invalid()) return false; if (h == invalid()) return false;
class non_mutex { class non_mutex {
public: public:
void lock () noexcept {} void lock () noexcept {}
void unlock() noexcept {} void unlock() noexcept {}
} nm; } nm;
return h.wait_if(nm, std::forward<F>(pred), tm); return h.wait_if(nm, std::forward<F>(pred), tm);
} }
void notify(handle_t& h) { void notify(handle_t& h) {
if (h == invalid()) return; if (h == invalid()) return;
h.notify(); h.notify();
} }
void broadcast(handle_t& h) { void broadcast(handle_t& h) {
if (h == invalid()) return; if (h == invalid()) return;
h.broadcast(); h.broadcast();
} }
}; };
} // namespace detail } // namespace detail
} // namespace ipc } // namespace ipc

View File

@ -1,286 +1,286 @@
#pragma once #pragma once
#include <type_traits> #include <type_traits>
#include <atomic> #include <atomic>
#include <utility> #include <utility>
#include "shm.h" #include "shm.h"
#include "memory/resource.h" #include "memory/resource.h"
#include "platform/detail.h" #include "platform/detail.h"
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \ #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
defined(WINCE) || defined(_WIN32_WCE) defined(WINCE) || defined(_WIN32_WCE)
#include "platform/waiter_win.h" #include "platform/waiter_win.h"
namespace ipc { namespace ipc {
namespace detail { namespace detail {
using mutex_impl = ipc::detail::mutex; using mutex_impl = ipc::detail::mutex;
using semaphore_impl = ipc::detail::semaphore; using semaphore_impl = ipc::detail::semaphore;
class condition_impl : public ipc::detail::condition { class condition_impl : public ipc::detail::condition {
ipc::shm::handle wait_h_, cnt_h_; ipc::shm::handle wait_h_, cnt_h_;
public: public:
static void remove(char const * name) { static void remove(char const * name) {
ipc::detail::condition::remove(name); ipc::detail::condition::remove(name);
ipc::string n = name; ipc::string n = name;
ipc::shm::remove((n + "__COND_CNT__" ).c_str()); ipc::shm::remove((n + "__COND_CNT__" ).c_str());
ipc::shm::remove((n + "__COND_WAIT__").c_str()); ipc::shm::remove((n + "__COND_WAIT__").c_str());
} }
bool open(ipc::string const & name) { bool open(ipc::string const & name) {
if (wait_h_.acquire((name + "__COND_WAIT__").c_str(), sizeof(std::atomic<unsigned>)) && if (wait_h_.acquire((name + "__COND_WAIT__").c_str(), sizeof(std::atomic<unsigned>)) &&
cnt_h_ .acquire((name + "__COND_CNT__" ).c_str(), sizeof(long))) { cnt_h_ .acquire((name + "__COND_CNT__" ).c_str(), sizeof(long))) {
return ipc::detail::condition::open(name, return ipc::detail::condition::open(name,
static_cast<std::atomic<unsigned> *>(wait_h_.get()), static_cast<std::atomic<unsigned> *>(wait_h_.get()),
static_cast<long *>(cnt_h_.get())); static_cast<long *>(cnt_h_.get()));
} }
return false; return false;
} }
void close() { void close() {
ipc::detail::condition::close(); ipc::detail::condition::close();
cnt_h_ .release(); cnt_h_ .release();
wait_h_.release(); wait_h_.release();
} }
bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) { bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) {
return ipc::detail::condition::wait_if(mtx, [] { return true; }, tm); return ipc::detail::condition::wait_if(mtx, [] { return true; }, tm);
} }
}; };
} // namespace detail } // namespace detail
} // namespace ipc } // namespace ipc
#else /*!WIN*/ #else /*!WIN*/
#include "platform/waiter_linux.h" #include "platform/waiter_linux.h"
namespace ipc { namespace ipc {
namespace detail { namespace detail {
template <typename T> template <typename T>
class object_impl { class object_impl {
ipc::shm::handle h_; ipc::shm::handle h_;
struct info_t { struct info_t {
T object_; T object_;
std::atomic<unsigned> opened_; std::atomic<unsigned> opened_;
}; };
public: public:
static void remove(char const * name) { static void remove(char const * name) {
{ {
ipc::shm::handle h { name, sizeof(info_t) }; ipc::shm::handle h { name, sizeof(info_t) };
if (h.valid()) { if (h.valid()) {
auto info = static_cast<info_t*>(h.get()); auto info = static_cast<info_t*>(h.get());
info->object_.close(); info->object_.close();
} }
} }
ipc::shm::remove(name); ipc::shm::remove(name);
} }
T& object() { T& object() {
return static_cast<info_t*>(h_.get())->object_; return static_cast<info_t*>(h_.get())->object_;
} }
template <typename... P> template <typename... P>
bool open(char const * name, P&&... params) { bool open(char const * name, P&&... params) {
if (!h_.acquire(name, sizeof(info_t))) { if (!h_.acquire(name, sizeof(info_t))) {
return false; return false;
} }
auto info = static_cast<info_t*>(h_.get()); auto info = static_cast<info_t*>(h_.get());
if ((info->opened_.fetch_add(1, std::memory_order_acq_rel) == 0) && if ((info->opened_.fetch_add(1, std::memory_order_acq_rel) == 0) &&
!info->object_.open(std::forward<P>(params)...)) { !info->object_.open(std::forward<P>(params)...)) {
return false; return false;
} }
return true; return true;
} }
void close() { void close() {
if (!h_.valid()) return; if (!h_.valid()) return;
auto info = static_cast<info_t*>(h_.get()); auto info = static_cast<info_t*>(h_.get());
if (info->opened_.fetch_sub(1, std::memory_order_release) == 1) { if (info->opened_.fetch_sub(1, std::memory_order_release) == 1) {
info->object_.close(); info->object_.close();
} }
h_.release(); h_.release();
} }
}; };
class mutex_impl : public object_impl<ipc::detail::mutex> { class mutex_impl : public object_impl<ipc::detail::mutex> {
public: public:
bool lock () { return object().lock (); } bool lock () { return object().lock (); }
bool unlock() { return object().unlock(); } bool unlock() { return object().unlock(); }
}; };
class condition_impl : public object_impl<ipc::detail::condition> { class condition_impl : public object_impl<ipc::detail::condition> {
public: public:
bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) { bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) {
return object().wait(mtx.object(), tm); return object().wait(mtx.object(), tm);
} }
bool notify () { return object().notify (); } bool notify () { return object().notify (); }
bool broadcast() { return object().broadcast(); } bool broadcast() { return object().broadcast(); }
}; };
class semaphore_impl { class semaphore_impl {
sem_helper::handle_t h_; sem_helper::handle_t h_;
ipc::shm::handle opened_; // std::atomic<unsigned> ipc::shm::handle opened_; // std::atomic<unsigned>
ipc::string name_; ipc::string name_;
auto cnt() { auto cnt() {
return static_cast<std::atomic<unsigned>*>(opened_.get()); return static_cast<std::atomic<unsigned>*>(opened_.get());
} }
public: public:
static void remove(char const * name) { static void remove(char const * name) {
sem_helper::destroy((ipc::string{ "__SEMAPHORE_IMPL_SEM__" } + name).c_str()); sem_helper::destroy((ipc::string{ "__SEMAPHORE_IMPL_SEM__" } + name).c_str());
ipc::shm::remove ((ipc::string{ "__SEMAPHORE_IMPL_CNT__" } + name).c_str()); ipc::shm::remove ((ipc::string{ "__SEMAPHORE_IMPL_CNT__" } + name).c_str());
} }
bool open(char const * name, long count) { bool open(char const * name, long count) {
name_ = name; name_ = name;
if (!opened_.acquire(("__SEMAPHORE_IMPL_CNT__" + name_).c_str(), sizeof(std::atomic<unsigned>))) { if (!opened_.acquire(("__SEMAPHORE_IMPL_CNT__" + name_).c_str(), sizeof(std::atomic<unsigned>))) {
return false; return false;
} }
if ((h_ = sem_helper::open(("__SEMAPHORE_IMPL_SEM__" + name_).c_str(), count)) == sem_helper::invalid()) { if ((h_ = sem_helper::open(("__SEMAPHORE_IMPL_SEM__" + name_).c_str(), count)) == sem_helper::invalid()) {
return false; return false;
} }
cnt()->fetch_add(1, std::memory_order_acq_rel); cnt()->fetch_add(1, std::memory_order_acq_rel);
return true; return true;
} }
void close() { void close() {
if (h_ == sem_helper::invalid()) return; if (h_ == sem_helper::invalid()) return;
sem_helper::close(h_); sem_helper::close(h_);
if (cnt() == nullptr) return; if (cnt() == nullptr) return;
if (cnt()->fetch_sub(1, std::memory_order_release) == 1) { if (cnt()->fetch_sub(1, std::memory_order_release) == 1) {
sem_helper::destroy(("__SEMAPHORE_IMPL_SEM__" + name_).c_str()); sem_helper::destroy(("__SEMAPHORE_IMPL_SEM__" + name_).c_str());
} }
opened_.release(); opened_.release();
} }
bool wait(std::size_t tm = invalid_value) { bool wait(std::size_t tm = invalid_value) {
if (h_ == sem_helper::invalid()) return false; if (h_ == sem_helper::invalid()) return false;
return sem_helper::wait(h_, tm); return sem_helper::wait(h_, tm);
} }
bool post(long count) { bool post(long count) {
if (h_ == sem_helper::invalid()) return false; if (h_ == sem_helper::invalid()) return false;
bool ret = true; bool ret = true;
for (long i = 0; i < count; ++i) { for (long i = 0; i < count; ++i) {
ret = ret && sem_helper::post(h_); ret = ret && sem_helper::post(h_);
} }
return ret; return ret;
} }
}; };
} // namespace detail } // namespace detail
} // namespace ipc } // namespace ipc
#endif/*!WIN*/ #endif/*!WIN*/
namespace ipc { namespace ipc {
namespace detail { namespace detail {
class waiter_wrapper { class waiter_wrapper {
public: public:
using waiter_t = detail::waiter; using waiter_t = detail::waiter;
private: private:
waiter_t* w_ = nullptr; waiter_t* w_ = nullptr;
waiter_t::handle_t h_ = waiter_t::invalid(); waiter_t::handle_t h_ = waiter_t::invalid();
public: public:
waiter_wrapper() = default; waiter_wrapper() = default;
explicit waiter_wrapper(waiter_t* w) { explicit waiter_wrapper(waiter_t* w) {
attach(w); attach(w);
} }
waiter_wrapper(const waiter_wrapper&) = delete; waiter_wrapper(const waiter_wrapper&) = delete;
waiter_wrapper& operator=(const waiter_wrapper&) = delete; waiter_wrapper& operator=(const waiter_wrapper&) = delete;
waiter_t * waiter() { return w_; } waiter_t * waiter() { return w_; }
waiter_t const * waiter() const { return w_; } waiter_t const * waiter() const { return w_; }
void attach(waiter_t* w) { void attach(waiter_t* w) {
close(); close();
w_ = w; w_ = w;
} }
bool valid() const { bool valid() const {
return (w_ != nullptr) && (h_ != waiter_t::invalid()); return (w_ != nullptr) && (h_ != waiter_t::invalid());
} }
bool open(char const * name) { bool open(char const * name) {
if (w_ == nullptr) return false; if (w_ == nullptr) return false;
close(); close();
h_ = w_->open(name); h_ = w_->open(name);
return valid(); return valid();
} }
void close() { void close() {
if (!valid()) return; if (!valid()) return;
w_->close(h_); w_->close(h_);
h_ = waiter_t::invalid(); h_ = waiter_t::invalid();
} }
template <typename F> template <typename F>
bool wait_if(F&& pred, std::size_t tm = invalid_value) { bool wait_if(F&& pred, std::size_t tm = invalid_value) {
if (!valid()) return false; if (!valid()) return false;
return w_->wait_if(h_, std::forward<F>(pred), tm); return w_->wait_if(h_, std::forward<F>(pred), tm);
} }
bool notify() { bool notify() {
if (!valid()) return false; if (!valid()) return false;
w_->notify(h_); w_->notify(h_);
return true; return true;
} }
bool broadcast() { bool broadcast() {
if (!valid()) return false; if (!valid()) return false;
w_->broadcast(h_); w_->broadcast(h_);
return true; return true;
} }
}; };
} // namespace detail } // namespace detail
class waiter : public detail::waiter_wrapper { class waiter : public detail::waiter_wrapper {
shm::handle shm_; shm::handle shm_;
using detail::waiter_wrapper::attach; using detail::waiter_wrapper::attach;
public: public:
waiter() = default; waiter() = default;
waiter(char const * name) { waiter(char const * name) {
open(name); open(name);
} }
~waiter() { ~waiter() {
close(); close();
} }
bool open(char const * name) { bool open(char const * name) {
if (name == nullptr || name[0] == '\0') { if (name == nullptr || name[0] == '\0') {
return false; return false;
} }
close(); close();
if (!shm_.acquire((ipc::string{ "__SHM_WAITER__" } + name).c_str(), sizeof(waiter_t))) { if (!shm_.acquire((ipc::string{ "__SHM_WAITER__" } + name).c_str(), sizeof(waiter_t))) {
return false; return false;
} }
attach(static_cast<waiter_t*>(shm_.get())); attach(static_cast<waiter_t*>(shm_.get()));
return detail::waiter_wrapper::open((ipc::string{ "__IMP_WAITER__" } + name).c_str()); return detail::waiter_wrapper::open((ipc::string{ "__IMP_WAITER__" } + name).c_str());
} }
void close() { void close() {
detail::waiter_wrapper::close(); detail::waiter_wrapper::close();
shm_.release(); shm_.release();
} }
}; };
} // namespace ipc } // namespace ipc

View File

@ -1,23 +1,23 @@
#pragma once #pragma once
#include <type_traits> #include <type_traits>
#include "def.h" #include "def.h"
#include "prod_cons.h" #include "prod_cons.h"
#include "circ/elem_array.h" #include "circ/elem_array.h"
namespace ipc { namespace ipc {
namespace policy { namespace policy {
template <template <typename, std::size_t...> class Elems, typename Flag> template <template <typename, std::size_t...> class Elems, typename Flag>
struct choose; struct choose;
template <typename Flag> template <typename Flag>
struct choose<circ::elem_array, Flag> { struct choose<circ::elem_array, Flag> {
template <std::size_t DataSize, std::size_t AlignSize> template <std::size_t DataSize, std::size_t AlignSize>
using elems_t = circ::elem_array<ipc::prod_cons_impl<Flag>, DataSize, AlignSize>; using elems_t = circ::elem_array<ipc::prod_cons_impl<Flag>, DataSize, AlignSize>;
}; };
} // namespace policy } // namespace policy
} // namespace ipc } // namespace ipc

View File

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

View File

@ -1,379 +1,379 @@
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <utility> #include <utility>
#include <cstring> #include <cstring>
#include <type_traits> #include <type_traits>
#include "def.h" #include "def.h"
#include "platform/detail.h" #include "platform/detail.h"
#include "circ/elem_def.h" #include "circ/elem_def.h"
namespace ipc { namespace ipc {
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// producer-consumer implementation /// producer-consumer implementation
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
template <typename Flag> template <typename Flag>
struct prod_cons_impl; struct prod_cons_impl;
template <> template <>
struct prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> { struct prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
template <std::size_t DataSize, std::size_t AlignSize> template <std::size_t DataSize, std::size_t AlignSize>
struct elem_t { struct elem_t {
std::aligned_storage_t<DataSize, AlignSize> data_ {}; std::aligned_storage_t<DataSize, AlignSize> data_ {};
}; };
alignas(detail::cache_line_size) std::atomic<circ::u2_t> rd_; // read index alignas(detail::cache_line_size) std::atomic<circ::u2_t> rd_; // read index
alignas(detail::cache_line_size) std::atomic<circ::u2_t> wt_; // write index alignas(detail::cache_line_size) std::atomic<circ::u2_t> wt_; // write index
constexpr circ::u2_t cursor() const noexcept { constexpr circ::u2_t cursor() const noexcept {
return 0; return 0;
} }
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool push(W* /*wrapper*/, F&& f, E* elems) { bool push(W* /*wrapper*/, F&& f, E* elems) {
auto cur_wt = circ::index_of(wt_.load(std::memory_order_relaxed)); auto cur_wt = circ::index_of(wt_.load(std::memory_order_relaxed));
if (cur_wt == circ::index_of(rd_.load(std::memory_order_acquire) - 1)) { if (cur_wt == circ::index_of(rd_.load(std::memory_order_acquire) - 1)) {
return false; // full return false; // full
} }
std::forward<F>(f)(&(elems[cur_wt].data_)); std::forward<F>(f)(&(elems[cur_wt].data_));
wt_.fetch_add(1, std::memory_order_release); wt_.fetch_add(1, std::memory_order_release);
return true; return true;
} }
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool force_push(W* wrapper, F&& f, E* elems) { bool force_push(W* wrapper, F&& f, E* elems) {
return push(wrapper, std::forward<F>(f), elems); return push(wrapper, std::forward<F>(f), elems);
} }
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E* elems) { bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E* elems) {
auto cur_rd = circ::index_of(rd_.load(std::memory_order_relaxed)); auto cur_rd = circ::index_of(rd_.load(std::memory_order_relaxed));
if (cur_rd == circ::index_of(wt_.load(std::memory_order_acquire))) { if (cur_rd == circ::index_of(wt_.load(std::memory_order_acquire))) {
return false; // empty return false; // empty
} }
std::forward<F>(f)(&(elems[cur_rd].data_)); std::forward<F>(f)(&(elems[cur_rd].data_));
rd_.fetch_add(1, std::memory_order_release); rd_.fetch_add(1, std::memory_order_release);
return true; return true;
} }
}; };
template <> template <>
struct prod_cons_impl<wr<relat::single, relat::multi , trans::unicast>> struct prod_cons_impl<wr<relat::single, relat::multi , trans::unicast>>
: prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> { : prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
template <typename W, typename F, template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS> template <typename W, typename F, template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS>
bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E<DS, AS>* elems) { bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E<DS, AS>* elems) {
byte_t buff[DS]; byte_t buff[DS];
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
auto cur_rd = rd_.load(std::memory_order_relaxed); auto cur_rd = rd_.load(std::memory_order_relaxed);
if (circ::index_of(cur_rd) == if (circ::index_of(cur_rd) ==
circ::index_of(wt_.load(std::memory_order_acquire))) { circ::index_of(wt_.load(std::memory_order_acquire))) {
return false; // empty return false; // empty
} }
std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff)); std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) { if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
std::forward<F>(f)(buff); std::forward<F>(f)(buff);
return true; return true;
} }
ipc::yield(k); ipc::yield(k);
} }
} }
}; };
template <> template <>
struct prod_cons_impl<wr<relat::multi , relat::multi, trans::unicast>> struct prod_cons_impl<wr<relat::multi , relat::multi, trans::unicast>>
: prod_cons_impl<wr<relat::single, relat::multi, trans::unicast>> { : prod_cons_impl<wr<relat::single, relat::multi, trans::unicast>> {
using flag_t = std::uint64_t; using flag_t = std::uint64_t;
template <std::size_t DataSize, std::size_t AlignSize> template <std::size_t DataSize, std::size_t AlignSize>
struct elem_t { struct elem_t {
std::aligned_storage_t<DataSize, AlignSize> data_ {}; std::aligned_storage_t<DataSize, AlignSize> data_ {};
std::atomic<flag_t> f_ct_ { 0 }; // commit flag std::atomic<flag_t> f_ct_ { 0 }; // commit flag
}; };
alignas(detail::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index alignas(detail::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool push(W* /*wrapper*/, F&& f, E* elems) { bool push(W* /*wrapper*/, F&& f, E* elems) {
circ::u2_t cur_ct, nxt_ct; circ::u2_t cur_ct, nxt_ct;
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
cur_ct = ct_.load(std::memory_order_relaxed); cur_ct = ct_.load(std::memory_order_relaxed);
if (circ::index_of(nxt_ct = cur_ct + 1) == if (circ::index_of(nxt_ct = cur_ct + 1) ==
circ::index_of(rd_.load(std::memory_order_acquire))) { circ::index_of(rd_.load(std::memory_order_acquire))) {
return false; // full return false; // full
} }
if (ct_.compare_exchange_weak(cur_ct, nxt_ct, std::memory_order_release)) { if (ct_.compare_exchange_weak(cur_ct, nxt_ct, std::memory_order_release)) {
break; break;
} }
ipc::yield(k); ipc::yield(k);
} }
auto* el = elems + circ::index_of(cur_ct); auto* el = elems + circ::index_of(cur_ct);
std::forward<F>(f)(&(el->data_)); std::forward<F>(f)(&(el->data_));
// set flag & try update wt // set flag & try update wt
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release); el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
while (1) { while (1) {
auto cac_ct = el->f_ct_.load(std::memory_order_acquire); auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
if (cur_ct != wt_.load(std::memory_order_acquire)) { if (cur_ct != wt_.load(std::memory_order_acquire)) {
return true; return true;
} }
if ((~cac_ct) != cur_ct) { if ((~cac_ct) != cur_ct) {
return true; return true;
} }
if (!el->f_ct_.compare_exchange_strong(cac_ct, 0, std::memory_order_relaxed)) { if (!el->f_ct_.compare_exchange_strong(cac_ct, 0, std::memory_order_relaxed)) {
return true; return true;
} }
wt_.store(nxt_ct, std::memory_order_release); wt_.store(nxt_ct, std::memory_order_release);
cur_ct = nxt_ct; cur_ct = nxt_ct;
nxt_ct = cur_ct + 1; nxt_ct = cur_ct + 1;
el = elems + circ::index_of(cur_ct); el = elems + circ::index_of(cur_ct);
} }
return true; return true;
} }
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool force_push(W* wrapper, F&& f, E* elems) { bool force_push(W* wrapper, F&& f, E* elems) {
return push(wrapper, std::forward<F>(f), elems); /* TBD */ return push(wrapper, std::forward<F>(f), elems); /* TBD */
} }
template <typename W, typename F, template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS> template <typename W, typename F, template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS>
bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E<DS, AS>* elems) { bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E<DS, AS>* elems) {
byte_t buff[DS]; byte_t buff[DS];
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
auto cur_rd = rd_.load(std::memory_order_relaxed); auto cur_rd = rd_.load(std::memory_order_relaxed);
auto cur_wt = wt_.load(std::memory_order_acquire); auto cur_wt = wt_.load(std::memory_order_acquire);
auto id_rd = circ::index_of(cur_rd); auto id_rd = circ::index_of(cur_rd);
auto id_wt = circ::index_of(cur_wt); auto id_wt = circ::index_of(cur_wt);
if (id_rd == id_wt) { if (id_rd == id_wt) {
auto* el = elems + id_wt; auto* el = elems + id_wt;
auto cac_ct = el->f_ct_.load(std::memory_order_acquire); auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
if ((~cac_ct) != cur_wt) { if ((~cac_ct) != cur_wt) {
return false; // empty return false; // empty
} }
if (el->f_ct_.compare_exchange_weak(cac_ct, 0, std::memory_order_relaxed)) { if (el->f_ct_.compare_exchange_weak(cac_ct, 0, std::memory_order_relaxed)) {
wt_.store(cur_wt + 1, std::memory_order_release); wt_.store(cur_wt + 1, std::memory_order_release);
} }
k = 0; k = 0;
} }
else { else {
std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff)); std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) { if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
std::forward<F>(f)(buff); std::forward<F>(f)(buff);
return true; return true;
} }
ipc::yield(k); ipc::yield(k);
} }
} }
} }
}; };
template <> template <>
struct prod_cons_impl<wr<relat::single, relat::multi, trans::broadcast>> { struct prod_cons_impl<wr<relat::single, relat::multi, trans::broadcast>> {
using rc_t = std::size_t; using rc_t = std::uint32_t;
template <std::size_t DataSize, std::size_t AlignSize> template <std::size_t DataSize, std::size_t AlignSize>
struct elem_t { struct elem_t {
std::aligned_storage_t<DataSize, AlignSize> data_ {}; std::aligned_storage_t<DataSize, AlignSize> data_ {};
std::atomic<rc_t> rc_ { 0 }; // read-counter std::atomic<rc_t> rc_ { 0 }; // read-counter
}; };
alignas(detail::cache_line_size) std::atomic<circ::u2_t> wt_; // write index alignas(detail::cache_line_size) std::atomic<circ::u2_t> wt_; // write index
circ::u2_t cursor() const noexcept { circ::u2_t cursor() const noexcept {
return wt_.load(std::memory_order_acquire); return wt_.load(std::memory_order_acquire);
} }
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool push(W* wrapper, F&& f, E* elems) { bool push(W* wrapper, F&& f, E* elems) {
E* el; E* el;
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
auto cc = wrapper->conn_count(std::memory_order_relaxed); auto cc = wrapper->conn_count(std::memory_order_relaxed);
if (cc == 0) return false; // no reader if (cc == 0) return false; // no reader
el = elems + circ::index_of(wt_.load(std::memory_order_acquire)); el = elems + circ::index_of(wt_.load(std::memory_order_acquire));
// check all consumers have finished reading this element // check all consumers have finished reading this element
auto cur_rc = el->rc_.load(std::memory_order_acquire); auto cur_rc = el->rc_.load(std::memory_order_acquire);
if (cur_rc) { if (cur_rc) {
return false; // full return false; // full
} }
// cur_rc should be 0 here // cur_rc should be 0 here
if (el->rc_.compare_exchange_weak( if (el->rc_.compare_exchange_weak(
cur_rc, static_cast<rc_t>(cc), std::memory_order_release)) { cur_rc, static_cast<rc_t>(cc), std::memory_order_release)) {
wrapper->clear_dis_flag(std::memory_order_relaxed); wrapper->clear_dis_flag(std::memory_order_relaxed);
break; break;
} }
ipc::yield(k); ipc::yield(k);
} }
std::forward<F>(f)(&(el->data_)); std::forward<F>(f)(&(el->data_));
wt_.fetch_add(1, std::memory_order_release); wt_.fetch_add(1, std::memory_order_release);
return true; return true;
} }
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool force_push(W* wrapper, F&& f, E* elems) { bool force_push(W* wrapper, F&& f, E* elems) {
E* el; E* el;
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
auto cc = wrapper->conn_count(std::memory_order_relaxed); auto cc = wrapper->conn_count(std::memory_order_relaxed);
if (cc == 0) return false; // no reader if (cc == 0) return false; // no reader
el = elems + circ::index_of(wt_.load(std::memory_order_acquire)); el = elems + circ::index_of(wt_.load(std::memory_order_acquire));
// check all consumers have finished reading this element // check all consumers have finished reading this element
auto cur_rc = el->rc_.load(std::memory_order_acquire); auto cur_rc = el->rc_.load(std::memory_order_acquire);
if (cur_rc) { if (cur_rc) {
wrapper->try_disconnect(); // try disconnect a reader wrapper->try_disconnect(); // try disconnect a reader
cc = wrapper->conn_count(std::memory_order_relaxed); cc = wrapper->conn_count(std::memory_order_relaxed);
if (cc == 0) return false; // no reader if (cc == 0) return false; // no reader
} }
// just compare & exchange // just compare & exchange
if (el->rc_.compare_exchange_weak( if (el->rc_.compare_exchange_weak(
cur_rc, static_cast<rc_t>(cc), std::memory_order_release)) { cur_rc, static_cast<rc_t>(cc), std::memory_order_release)) {
break; break;
} }
ipc::yield(k); ipc::yield(k);
} }
std::forward<F>(f)(&(el->data_)); std::forward<F>(f)(&(el->data_));
wt_.fetch_add(1, std::memory_order_release); wt_.fetch_add(1, std::memory_order_release);
return true; return true;
} }
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool pop(W* /*wrapper*/, circ::u2_t& cur, F&& f, E* elems) { bool pop(W* /*wrapper*/, circ::u2_t& cur, F&& f, E* elems) {
if (cur == cursor()) return false; // acquire if (cur == cursor()) return false; // acquire
auto* el = elems + circ::index_of(cur++); auto* el = elems + circ::index_of(cur++);
std::forward<F>(f)(&(el->data_)); std::forward<F>(f)(&(el->data_));
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
rc_t cur_rc = el->rc_.load(std::memory_order_acquire); rc_t cur_rc = el->rc_.load(std::memory_order_acquire);
if (cur_rc == 0) { if (cur_rc == 0) {
return true; return true;
} }
if (el->rc_.compare_exchange_weak( if (el->rc_.compare_exchange_weak(
cur_rc, cur_rc - 1, std::memory_order_release)) { cur_rc, cur_rc - 1, std::memory_order_release)) {
return true; return true;
} }
ipc::yield(k); ipc::yield(k);
} }
} }
}; };
template <> template <>
struct prod_cons_impl<wr<relat::multi , relat::multi, trans::broadcast>> { struct prod_cons_impl<wr<relat::multi , relat::multi, trans::broadcast>> {
using rc_t = std::uint64_t; using rc_t = std::uint64_t;
using flag_t = std::uint64_t; using flag_t = std::uint64_t;
enum : rc_t { enum : rc_t {
rc_mask = 0x00000000ffffffffull, rc_mask = 0x00000000ffffffffull,
rc_incr = 0x0000000100000000ull rc_incr = 0x0000000100000000ull
}; };
template <std::size_t DataSize, std::size_t AlignSize> template <std::size_t DataSize, std::size_t AlignSize>
struct elem_t { struct elem_t {
std::aligned_storage_t<DataSize, AlignSize> data_ {}; std::aligned_storage_t<DataSize, AlignSize> data_ {};
std::atomic<rc_t > rc_ { 0 }; // read-counter std::atomic<rc_t > rc_ { 0 }; // read-counter
std::atomic<flag_t> f_ct_ { 0 }; // commit flag std::atomic<flag_t> f_ct_ { 0 }; // commit flag
}; };
alignas(detail::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index alignas(detail::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
circ::u2_t cursor() const noexcept { circ::u2_t cursor() const noexcept {
return ct_.load(std::memory_order_acquire); return ct_.load(std::memory_order_acquire);
} }
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool push(W* wrapper, F&& f, E* elems) { bool push(W* wrapper, F&& f, E* elems) {
E* el; E* el;
circ::u2_t cur_ct; circ::u2_t cur_ct;
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
auto cc = wrapper->conn_count(std::memory_order_relaxed); auto cc = wrapper->conn_count(std::memory_order_relaxed);
if (cc == 0) return false; // no reader if (cc == 0) return false; // no reader
el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed)); el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
// check all consumers have finished reading this element // check all consumers have finished reading this element
auto cur_rc = el->rc_.load(std::memory_order_acquire); auto cur_rc = el->rc_.load(std::memory_order_acquire);
if (cur_rc & rc_mask) { if (cur_rc & rc_mask) {
return false; // full return false; // full
} }
auto cur_fl = el->f_ct_.load(std::memory_order_acquire); auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
if ((cur_fl != cur_ct) && cur_fl) { if ((cur_fl != cur_ct) && cur_fl) {
return false; // full return false; // full
} }
// (cur_rc & rc_mask) should be 0 here // (cur_rc & rc_mask) should be 0 here
if (el->rc_.compare_exchange_weak( if (el->rc_.compare_exchange_weak(
cur_rc, static_cast<rc_t>(cc) | ((cur_rc & ~rc_mask) + rc_incr), std::memory_order_release)) { cur_rc, static_cast<rc_t>(cc) | ((cur_rc & ~rc_mask) + rc_incr), std::memory_order_release)) {
wrapper->clear_dis_flag(std::memory_order_relaxed); wrapper->clear_dis_flag(std::memory_order_relaxed);
break; break;
} }
ipc::yield(k); ipc::yield(k);
} }
// only one thread/process would touch here at one time // only one thread/process would touch here at one time
ct_.store(cur_ct + 1, std::memory_order_release); ct_.store(cur_ct + 1, std::memory_order_release);
std::forward<F>(f)(&(el->data_)); std::forward<F>(f)(&(el->data_));
// set flag & try update wt // set flag & try update wt
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release); el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
return true; return true;
} }
template <typename W, typename F, typename E> template <typename W, typename F, typename E>
bool force_push(W* wrapper, F&& f, E* elems) { bool force_push(W* wrapper, F&& f, E* elems) {
E* el; E* el;
circ::u2_t cur_ct; circ::u2_t cur_ct;
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
auto cc = wrapper->conn_count(std::memory_order_relaxed); auto cc = wrapper->conn_count(std::memory_order_relaxed);
if (cc == 0) return false; // no reader if (cc == 0) return false; // no reader
el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed)); el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
// check all consumers have finished reading this element // check all consumers have finished reading this element
auto cur_rc = el->rc_.load(std::memory_order_acquire); auto cur_rc = el->rc_.load(std::memory_order_acquire);
ipc::log("force_push: k = %d, cc = %zd, rc = %zd\n", k, cc, (cur_rc & rc_mask)); ipc::log("force_push: k = %d, cc = %zd, rc = %zd\n", k, cc, (cur_rc & rc_mask));
if (cur_rc & rc_mask) { if (cur_rc & rc_mask) {
wrapper->try_disconnect(); // try disconnect a reader wrapper->try_disconnect(); // try disconnect a reader
cc = wrapper->conn_count(std::memory_order_relaxed); cc = wrapper->conn_count(std::memory_order_relaxed);
if (cc == 0) return false; // no reader if (cc == 0) return false; // no reader
} }
// just compare & exchange // just compare & exchange
if (el->rc_.compare_exchange_weak( if (el->rc_.compare_exchange_weak(
cur_rc, static_cast<rc_t>(cc) | ((cur_rc & ~rc_mask) + rc_incr), std::memory_order_release)) { cur_rc, static_cast<rc_t>(cc) | ((cur_rc & ~rc_mask) + rc_incr), std::memory_order_release)) {
break; break;
} }
ipc::yield(k); ipc::yield(k);
} }
// only one thread/process would touch here at one time // only one thread/process would touch here at one time
ct_.store(cur_ct + 1, std::memory_order_release); ct_.store(cur_ct + 1, std::memory_order_release);
std::forward<F>(f)(&(el->data_)); std::forward<F>(f)(&(el->data_));
// set flag & try update wt // set flag & try update wt
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release); el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
return true; return true;
} }
template <typename W, typename F, typename E, std::size_t N> template <typename W, typename F, typename E, std::size_t N>
bool pop(W* /*wrapper*/, circ::u2_t& cur, F&& f, E(& elems)[N]) { bool pop(W* /*wrapper*/, circ::u2_t& cur, F&& f, E(& elems)[N]) {
auto* el = elems + circ::index_of(cur); auto* el = elems + circ::index_of(cur);
auto cur_fl = el->f_ct_.load(std::memory_order_acquire); auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
if (cur_fl != ~static_cast<flag_t>(cur)) { if (cur_fl != ~static_cast<flag_t>(cur)) {
return false; // empty return false; // empty
} }
++cur; ++cur;
std::forward<F>(f)(&(el->data_)); std::forward<F>(f)(&(el->data_));
for (unsigned k = 0;;) { for (unsigned k = 0;;) {
auto cur_rc = el->rc_.load(std::memory_order_acquire); auto cur_rc = el->rc_.load(std::memory_order_acquire);
switch (cur_rc & rc_mask) { switch (cur_rc & rc_mask) {
case 0: case 0:
el->f_ct_.store(cur + N - 1, std::memory_order_release); el->f_ct_.store(cur + N - 1, std::memory_order_release);
return true; return true;
case 1: case 1:
el->f_ct_.store(cur + N - 1, std::memory_order_release); el->f_ct_.store(cur + N - 1, std::memory_order_release);
IPC_FALLTHROUGH_; IPC_FALLTHROUGH_;
default: default:
if (el->rc_.compare_exchange_weak( if (el->rc_.compare_exchange_weak(
cur_rc, cur_rc + rc_incr - 1, std::memory_order_release)) { cur_rc, cur_rc + rc_incr - 1, std::memory_order_release)) {
return true; return true;
} }
break; break;
} }
ipc::yield(k); ipc::yield(k);
} }
} }
}; };
} // namespace ipc } // namespace ipc

View File

@ -1,197 +1,197 @@
#pragma once #pragma once
#include <type_traits> #include <type_traits>
#include <new> #include <new>
#include <utility> #include <utility>
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <tuple> #include <tuple>
#include <thread> #include <thread>
#include <chrono> #include <chrono>
#include <string> #include <string>
#include "def.h" #include "def.h"
#include "shm.h" #include "shm.h"
#include "log.h" #include "log.h"
#include "rw_lock.h" #include "rw_lock.h"
#include "platform/detail.h" #include "platform/detail.h"
namespace ipc { namespace ipc {
namespace detail { namespace detail {
class queue_conn { class queue_conn {
protected: protected:
bool connected_ = false; bool connected_ = false;
shm::handle elems_h_; shm::handle elems_h_;
template <typename Elems> template <typename Elems>
Elems* open(char const * name) { Elems* open(char const * name) {
if (name == nullptr || name[0] == '\0') { if (name == nullptr || name[0] == '\0') {
ipc::error("fail open waiter: name is empty!\n"); ipc::error("fail open waiter: name is empty!\n");
return nullptr; return nullptr;
} }
if (!elems_h_.acquire(name, sizeof(Elems))) { if (!elems_h_.acquire(name, sizeof(Elems))) {
return nullptr; return nullptr;
} }
auto elems = static_cast<Elems*>(elems_h_.get()); auto elems = static_cast<Elems*>(elems_h_.get());
if (elems == nullptr) { if (elems == nullptr) {
ipc::error("fail acquire elems: %s\n", name); ipc::error("fail acquire elems: %s\n", name);
return nullptr; return nullptr;
} }
elems->init(); elems->init();
return elems; return elems;
} }
void close() { void close() {
elems_h_.release(); elems_h_.release();
} }
public: public:
queue_conn() = default; queue_conn() = default;
queue_conn(const queue_conn&) = delete; queue_conn(const queue_conn&) = delete;
queue_conn& operator=(const queue_conn&) = delete; queue_conn& operator=(const queue_conn&) = delete;
bool connected() const noexcept { bool connected() const noexcept {
return connected_; return connected_;
} }
template <typename Elems> template <typename Elems>
auto connect(Elems* elems) auto connect(Elems* elems)
-> std::tuple<bool, decltype(std::declval<Elems>().cursor())> { -> std::tuple<bool, decltype(std::declval<Elems>().cursor())> {
if (elems == nullptr) return {}; if (elems == nullptr) return {};
if (connected_) { if (connected_) {
// if it's already connected, just return false // if it's already connected, just return false
return {}; return {};
} }
connected_ = true; connected_ = true;
elems->connect(); elems->connect();
return std::make_tuple(true, elems->cursor()); return std::make_tuple(true, elems->cursor());
} }
template <typename Elems> template <typename Elems>
bool disconnect(Elems* elems) { bool disconnect(Elems* elems) {
if (elems == nullptr) return false; if (elems == nullptr) return false;
if (!connected_) { if (!connected_) {
// if it's already disconnected, just return false // if it's already disconnected, just return false
return false; return false;
} }
connected_ = false; connected_ = false;
elems->disconnect(); elems->disconnect();
return true; return true;
} }
}; };
template <typename Elems> template <typename Elems>
class queue_base : public queue_conn { class queue_base : public queue_conn {
using base_t = queue_conn; using base_t = queue_conn;
public: public:
using elems_t = Elems; using elems_t = Elems;
using policy_t = typename elems_t::policy_t; using policy_t = typename elems_t::policy_t;
protected: protected:
elems_t * elems_ = nullptr; elems_t * elems_ = nullptr;
decltype(std::declval<elems_t>().cursor()) cursor_ = 0; decltype(std::declval<elems_t>().cursor()) cursor_ = 0;
public: public:
using base_t::base_t; using base_t::base_t;
queue_base() = default; queue_base() = default;
explicit queue_base(char const * name) explicit queue_base(char const * name)
: queue_base() { : queue_base() {
elems_ = open<elems_t>(name); elems_ = open<elems_t>(name);
} }
/* not virtual */ ~queue_base() { /* not virtual */ ~queue_base() {
base_t::close(); base_t::close();
} }
constexpr elems_t * elems() const noexcept { constexpr elems_t * elems() const noexcept {
return elems_; return elems_;
} }
bool connect() { bool connect() {
auto tp = base_t::connect(elems_); auto tp = base_t::connect(elems_);
if (std::get<0>(tp)) { if (std::get<0>(tp)) {
cursor_ = std::get<1>(tp); cursor_ = std::get<1>(tp);
return true; return true;
} }
return false; return false;
} }
bool disconnect() { bool disconnect() {
return base_t::disconnect(elems_); return base_t::disconnect(elems_);
} }
bool dis_flag() { bool dis_flag() {
return elems_->dis_flag(); return elems_->dis_flag();
} }
std::size_t conn_count() const noexcept { std::size_t conn_count() const noexcept {
return (elems_ == nullptr) ? invalid_value : elems_->conn_count(); return (elems_ == nullptr) ? invalid_value : elems_->conn_count();
} }
bool valid() const noexcept { bool valid() const noexcept {
return elems_ != nullptr; return elems_ != nullptr;
} }
bool empty() const noexcept { bool empty() const noexcept {
return (elems_ == nullptr) ? true : (cursor_ == elems_->cursor()); return (elems_ == nullptr) ? true : (cursor_ == elems_->cursor());
} }
template <typename T, typename... P> template <typename T, typename... P>
auto push(P&&... params) { auto push(P&&... params) {
if (elems_ == nullptr) return false; if (elems_ == nullptr) return false;
return elems_->push([&](void* p) { return elems_->push([&](void* p) {
::new (p) T(std::forward<P>(params)...); ::new (p) T(std::forward<P>(params)...);
}); });
} }
template <typename T, typename... P> template <typename T, typename... P>
auto force_push(P&&... params) { auto force_push(P&&... params) {
if (elems_ == nullptr) return false; if (elems_ == nullptr) return false;
return elems_->force_push([&](void* p) { return elems_->force_push([&](void* p) {
::new (p) T(std::forward<P>(params)...); ::new (p) T(std::forward<P>(params)...);
}); });
} }
template <typename T> template <typename T>
bool pop(T& item) { bool pop(T& item) {
if (elems_ == nullptr) { if (elems_ == nullptr) {
return false; return false;
} }
return elems_->pop(&(this->cursor_), [&item](void* p) { return elems_->pop(&(this->cursor_), [&item](void* p) {
::new (&item) T(std::move(*static_cast<T*>(p))); ::new (&item) T(std::move(*static_cast<T*>(p)));
}); });
} }
}; };
} // namespace detail } // namespace detail
template <typename T, typename Policy> template <typename T, typename Policy>
class queue : public detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>> { class queue : public detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>> {
using base_t = detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>>; using base_t = detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>>;
public: public:
using value_t = T; using value_t = T;
using base_t::base_t; using base_t::base_t;
template <typename... P> template <typename... P>
auto push(P&&... params) { auto push(P&&... params) {
return base_t::template push<T>(std::forward<P>(params)...); return base_t::template push<T>(std::forward<P>(params)...);
} }
template <typename... P> template <typename... P>
auto force_push(P&&... params) { auto force_push(P&&... params) {
return base_t::template force_push<T>(std::forward<P>(params)...); return base_t::template force_push<T>(std::forward<P>(params)...);
} }
bool pop(T& item) { bool pop(T& item) {
return base_t::pop(item); return base_t::pop(item);
} }
}; };
} // namespace ipc } // namespace ipc

View File

@ -1,94 +1,94 @@
#include "shm.h" #include "shm.h"
#include <string> #include <string>
#include <utility> #include <utility>
#include "pimpl.h" #include "pimpl.h"
#include "memory/resource.h" #include "memory/resource.h"
namespace ipc { namespace ipc {
namespace shm { namespace shm {
class handle::handle_ : public pimpl<handle_> { class handle::handle_ : public pimpl<handle_> {
public: public:
shm::id_t id_ = nullptr; shm::id_t id_ = nullptr;
void* m_ = nullptr; void* m_ = nullptr;
ipc::string n_; ipc::string n_;
std::size_t s_ = 0; std::size_t s_ = 0;
}; };
handle::handle() handle::handle()
: p_(p_->make()) { : p_(p_->make()) {
} }
handle::handle(char const * name, std::size_t size, unsigned mode) handle::handle(char const * name, std::size_t size, unsigned mode)
: handle() { : handle() {
acquire(name, size, mode); acquire(name, size, mode);
} }
handle::handle(handle&& rhs) handle::handle(handle&& rhs)
: handle() { : handle() {
swap(rhs); swap(rhs);
} }
handle::~handle() { handle::~handle() {
release(); release();
p_->clear(); p_->clear();
} }
void handle::swap(handle& rhs) { void handle::swap(handle& rhs) {
std::swap(p_, rhs.p_); std::swap(p_, rhs.p_);
} }
handle& handle::operator=(handle rhs) { handle& handle::operator=(handle rhs) {
swap(rhs); swap(rhs);
return *this; return *this;
} }
bool handle::valid() const { bool handle::valid() const {
return impl(p_)->m_ != nullptr; return impl(p_)->m_ != nullptr;
} }
std::size_t handle::size() const { std::size_t handle::size() const {
return impl(p_)->s_; return impl(p_)->s_;
} }
char const * handle::name() const { char const * handle::name() const {
return impl(p_)->n_.c_str(); return impl(p_)->n_.c_str();
} }
bool handle::acquire(char const * name, std::size_t size, unsigned mode) { bool handle::acquire(char const * name, std::size_t size, unsigned mode) {
release(); release();
impl(p_)->id_ = shm::acquire((impl(p_)->n_ = name).c_str(), size, mode); impl(p_)->id_ = shm::acquire((impl(p_)->n_ = name).c_str(), size, mode);
impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_)); impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_));
return valid(); return valid();
} }
void handle::release() { void handle::release() {
if (impl(p_)->id_ == nullptr) return; if (impl(p_)->id_ == nullptr) return;
shm::release(detach()); shm::release(detach());
} }
void* handle::get() const { void* handle::get() const {
return impl(p_)->m_; return impl(p_)->m_;
} }
void handle::attach(id_t id) { void handle::attach(id_t id) {
if (id == nullptr) return; if (id == nullptr) return;
release(); release();
impl(p_)->id_ = id; impl(p_)->id_ = id;
impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_)); impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_));
} }
id_t handle::detach() { id_t handle::detach() {
auto old = impl(p_)->id_; auto old = impl(p_)->id_;
impl(p_)->id_ = nullptr; impl(p_)->id_ = nullptr;
impl(p_)->m_ = nullptr; impl(p_)->m_ = nullptr;
impl(p_)->s_ = 0; impl(p_)->s_ = 0;
impl(p_)->n_.clear(); impl(p_)->n_.clear();
return old; return old;
} }
} // namespace shm } // namespace shm
} // namespace ipc } // namespace ipc

View File

@ -1,76 +1,76 @@
#include "waiter.h" #include "waiter.h"
#include <string> #include <string>
#include "pimpl.h" #include "pimpl.h"
#include "platform/waiter_wrapper.h" #include "platform/waiter_wrapper.h"
#undef IPC_PP_CAT_ #undef IPC_PP_CAT_
#undef IPC_PP_JOIN_T__ #undef IPC_PP_JOIN_T__
#undef IPC_PP_JOIN_ #undef IPC_PP_JOIN_
#define IPC_PP_CAT_(X, ...) X##__VA_ARGS__ #define IPC_PP_CAT_(X, ...) X##__VA_ARGS__
#define IPC_PP_JOIN_T__(X, ...) IPC_PP_CAT_(X, __VA_ARGS__) #define IPC_PP_JOIN_T__(X, ...) IPC_PP_CAT_(X, __VA_ARGS__)
#define IPC_PP_JOIN_(X, ...) IPC_PP_JOIN_T__(X, __VA_ARGS__) #define IPC_PP_JOIN_(X, ...) IPC_PP_JOIN_T__(X, __VA_ARGS__)
namespace ipc { namespace ipc {
#undef IPC_OBJECT_TYPE_ #undef IPC_OBJECT_TYPE_
#undef IPC_OBJECT_TYPE_OPEN_PARS_ #undef IPC_OBJECT_TYPE_OPEN_PARS_
#undef IPC_OBJECT_TYPE_OPEN_ARGS_ #undef IPC_OBJECT_TYPE_OPEN_ARGS_
#define IPC_OBJECT_TYPE_ mutex #define IPC_OBJECT_TYPE_ mutex
#define IPC_OBJECT_TYPE_OPEN_PARS_ #define IPC_OBJECT_TYPE_OPEN_PARS_
#define IPC_OBJECT_TYPE_OPEN_ARGS_ #define IPC_OBJECT_TYPE_OPEN_ARGS_
#include "waiter_template.inc" #include "waiter_template.inc"
bool mutex::lock() { bool mutex::lock() {
return impl(p_)->h_.lock(); return impl(p_)->h_.lock();
} }
bool mutex::unlock() { bool mutex::unlock() {
return impl(p_)->h_.unlock(); return impl(p_)->h_.unlock();
} }
#undef IPC_OBJECT_TYPE_ #undef IPC_OBJECT_TYPE_
#undef IPC_OBJECT_TYPE_OPEN_PARS_ #undef IPC_OBJECT_TYPE_OPEN_PARS_
#undef IPC_OBJECT_TYPE_OPEN_ARGS_ #undef IPC_OBJECT_TYPE_OPEN_ARGS_
#define IPC_OBJECT_TYPE_ semaphore #define IPC_OBJECT_TYPE_ semaphore
#define IPC_OBJECT_TYPE_OPEN_PARS_ , long count #define IPC_OBJECT_TYPE_OPEN_PARS_ , long count
#define IPC_OBJECT_TYPE_OPEN_ARGS_ , count #define IPC_OBJECT_TYPE_OPEN_ARGS_ , count
#include "waiter_template.inc" #include "waiter_template.inc"
bool semaphore::wait(std::size_t tm) { bool semaphore::wait(std::size_t tm) {
return impl(p_)->h_.wait(tm); return impl(p_)->h_.wait(tm);
} }
bool semaphore::post(long count) { bool semaphore::post(long count) {
return impl(p_)->h_.post(count); return impl(p_)->h_.post(count);
} }
#undef IPC_OBJECT_TYPE_ #undef IPC_OBJECT_TYPE_
#undef IPC_OBJECT_TYPE_OPEN_PARS_ #undef IPC_OBJECT_TYPE_OPEN_PARS_
#undef IPC_OBJECT_TYPE_OPEN_ARGS_ #undef IPC_OBJECT_TYPE_OPEN_ARGS_
#define IPC_OBJECT_TYPE_ condition #define IPC_OBJECT_TYPE_ condition
#define IPC_OBJECT_TYPE_OPEN_PARS_ #define IPC_OBJECT_TYPE_OPEN_PARS_
#define IPC_OBJECT_TYPE_OPEN_ARGS_ #define IPC_OBJECT_TYPE_OPEN_ARGS_
#include "waiter_template.inc" #include "waiter_template.inc"
bool condition::wait(mutex& mtx, std::size_t tm) { bool condition::wait(mutex& mtx, std::size_t tm) {
return impl(p_)->h_.wait(impl(mtx.p_)->h_, tm); return impl(p_)->h_.wait(impl(mtx.p_)->h_, tm);
} }
bool condition::notify() { bool condition::notify() {
return impl(p_)->h_.notify(); return impl(p_)->h_.notify();
} }
bool condition::broadcast() { bool condition::broadcast() {
return impl(p_)->h_.broadcast(); return impl(p_)->h_.broadcast();
} }
} // namespace ipc } // namespace ipc

View File

@ -1,43 +1,43 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QObject> #include <QObject>
#include <QVector> #include <QVector>
#include <QString> #include <QString>
#include <QDebug> #include <QDebug>
#include "test.h" #include "test.h"
namespace { namespace {
QVector<QObject*>* suites__ = nullptr; QVector<QObject*>* suites__ = nullptr;
} // internal-linkage } // internal-linkage
TestSuite::TestSuite() { TestSuite::TestSuite() {
static struct __ { static struct __ {
QVector<QObject*> suites_; QVector<QObject*> suites_;
__() { suites__ = &suites_; } __() { suites__ = &suites_; }
} _; } _;
_.suites_ << this; _.suites_ << this;
} }
const char* TestSuite::name() const { const char* TestSuite::name() const {
return ""; return "";
} }
void TestSuite::initTestCase() { void TestSuite::initTestCase() {
qDebug() << QString("#### Start: %1 ####").arg(name()); qDebug() << QString("#### Start: %1 ####").arg(name());
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
Q_UNUSED(app) Q_UNUSED(app)
// QThread::sleep(5); // QThread::sleep(5);
int failed_count = 0; int failed_count = 0;
for (const auto& suite : (*suites__)) { for (const auto& suite : (*suites__)) {
if (QTest::qExec(suite, argc, argv) != 0) if (QTest::qExec(suite, argc, argv) != 0)
++failed_count; ++failed_count;
} }
return failed_count; return failed_count;
} }

View File

@ -1,155 +1,155 @@
#pragma once #pragma once
#include <QtTest> #include <QtTest>
#include <iostream> #include <iostream>
#include <atomic> #include <atomic>
#include <thread> #include <thread>
#include <string> #include <string>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#if defined(__GNUC__) #if defined(__GNUC__)
# include <cxxabi.h> // abi::__cxa_demangle # include <cxxabi.h> // abi::__cxa_demangle
#endif/*__GNUC__*/ #endif/*__GNUC__*/
#include "stopwatch.hpp" #include "stopwatch.hpp"
#include "spin_lock.hpp" #include "spin_lock.hpp"
class TestSuite : public QObject class TestSuite : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit TestSuite(); explicit TestSuite();
protected: protected:
virtual const char* name() const; virtual const char* name() const;
protected slots: protected slots:
virtual void initTestCase(); virtual void initTestCase();
}; };
struct test_stopwatch { struct test_stopwatch {
capo::stopwatch<> sw_; capo::stopwatch<> sw_;
std::atomic_flag started_ = ATOMIC_FLAG_INIT; std::atomic_flag started_ = ATOMIC_FLAG_INIT;
void start() { void start() {
if (!started_.test_and_set()) { if (!started_.test_and_set()) {
sw_.start(); sw_.start();
} }
} }
template <int Factor> template <int Factor>
void print_elapsed(int N, int M, int Loops) { void print_elapsed(int N, int M, int Loops) {
auto ts = sw_.elapsed<std::chrono::microseconds>(); auto ts = sw_.elapsed<std::chrono::microseconds>();
std::cout << "[" << N << ":" << M << ", " << Loops << "] " std::cout << "[" << N << ":" << M << ", " << Loops << "] "
<< "performance: " << (ts / 1000.0) << " ms, " << "performance: " << (ts / 1000.0) << " ms, "
<< (double(ts) / double(Factor ? (Loops * Factor) : (Loops * N))) << " us/d" << std::endl; << (double(ts) / double(Factor ? (Loops * Factor) : (Loops * N))) << " us/d" << std::endl;
} }
void print_elapsed(int N, int M, int Loops) { void print_elapsed(int N, int M, int Loops) {
print_elapsed<0>(N, M, Loops); print_elapsed<0>(N, M, Loops);
} }
}; };
template <typename V> template <typename V>
struct test_verify; struct test_verify;
template <> template <>
struct test_verify<void> { struct test_verify<void> {
test_verify (int) {} test_verify (int) {}
void prepare (void*) {} void prepare (void*) {}
void verify (int, int) {} void verify (int, int) {}
template <typename U> template <typename U>
void push_data(int, U&&) {} void push_data(int, U&&) {}
}; };
template <typename T> template <typename T>
struct test_cq; struct test_cq;
template <typename T> template <typename T>
std::string type_name() { std::string type_name() {
#if defined(__GNUC__) #if defined(__GNUC__)
const char* typeid_name = typeid(T).name(); const char* typeid_name = typeid(T).name();
const char* real_name = abi::__cxa_demangle(typeid_name, nullptr, nullptr, nullptr); const char* real_name = abi::__cxa_demangle(typeid_name, nullptr, nullptr, nullptr);
std::unique_ptr<void, decltype(::free)*> guard { (void*)real_name, ::free }; std::unique_ptr<void, decltype(::free)*> guard { (void*)real_name, ::free };
if (real_name == nullptr) real_name = typeid_name; if (real_name == nullptr) real_name = typeid_name;
return real_name; return real_name;
#else #else
return typeid(T).name(); return typeid(T).name();
#endif/*__GNUC__*/ #endif/*__GNUC__*/
} }
template <int N, int M, int Loops, typename V = void, typename T> template <int N, int M, int Loops, typename V = void, typename T>
void benchmark_prod_cons(T* cq) { void benchmark_prod_cons(T* cq) {
std::cout << "benchmark_prod_cons " << type_name<T>() << " [" << N << ":" << M << ", " << Loops << "]" << std::endl; std::cout << "benchmark_prod_cons " << type_name<T>() << " [" << N << ":" << M << ", " << Loops << "]" << std::endl;
test_cq<T> tcq { cq }; test_cq<T> tcq { cq };
std::thread producers[N]; std::thread producers[N];
std::thread consumers[M]; std::thread consumers[M];
std::atomic_int fini_p { 0 }, fini_c { 0 }; std::atomic_int fini_p { 0 }, fini_c { 0 };
test_stopwatch sw; test_stopwatch sw;
test_verify<V> vf { M }; test_verify<V> vf { M };
// capo::spin_lock lc; // capo::spin_lock lc;
int cid = 0; int cid = 0;
for (auto& t : consumers) { for (auto& t : consumers) {
t = std::thread{[&, cid] { t = std::thread{[&, cid] {
vf.prepare(&t); vf.prepare(&t);
auto cn = tcq.connect(); auto cn = tcq.connect();
int i = 0; int i = 0;
tcq.recv(cn, [&](auto&& msg) { tcq.recv(cn, [&](auto&& msg) {
// if (i % ((Loops * N) / 10) == 0) { // if (i % ((Loops * N) / 10) == 0) {
// std::unique_lock<capo::spin_lock> guard { lc }; // std::unique_lock<capo::spin_lock> guard { lc };
// std::cout << cid << "-recving: " << (i * 100) / (Loops * N) << "%" << std::endl; // std::cout << cid << "-recving: " << (i * 100) / (Loops * N) << "%" << std::endl;
// } // }
vf.push_data(cid, msg); vf.push_data(cid, msg);
++i; ++i;
}); });
// { // {
// std::unique_lock<capo::spin_lock> guard { lc }; // std::unique_lock<capo::spin_lock> guard { lc };
// std::cout << cid << "-consumer-disconnect" << std::endl; // std::cout << cid << "-consumer-disconnect" << std::endl;
// } // }
tcq.disconnect(cn); tcq.disconnect(cn);
if ((fini_c.fetch_add(1, std::memory_order_relaxed) + 1) != M) { if ((fini_c.fetch_add(1, std::memory_order_relaxed) + 1) != M) {
// std::unique_lock<capo::spin_lock> guard { lc }; // std::unique_lock<capo::spin_lock> guard { lc };
// std::cout << cid << "-consumer-end" << std::endl; // std::cout << cid << "-consumer-end" << std::endl;
return; return;
} }
sw.print_elapsed(N, M, Loops); sw.print_elapsed(N, M, Loops);
vf.verify(N, Loops); vf.verify(N, Loops);
}}; }};
++cid; ++cid;
} }
tcq.wait_start(M); tcq.wait_start(M);
std::cout << "start producers..." << std::endl; std::cout << "start producers..." << std::endl;
int pid = 0; int pid = 0;
for (auto& t : producers) { for (auto& t : producers) {
t = std::thread{[&, pid] { t = std::thread{[&, pid] {
auto cn = tcq.connect_send(); auto cn = tcq.connect_send();
sw.start(); sw.start();
for (int i = 0; i < Loops; ++i) { for (int i = 0; i < Loops; ++i) {
// if (i % (Loops / 10) == 0) { // if (i % (Loops / 10) == 0) {
// std::unique_lock<capo::spin_lock> guard { lc }; // std::unique_lock<capo::spin_lock> guard { lc };
// std::cout << pid << "-sending: " << (i * 100 / Loops) << "%" << std::endl; // std::cout << pid << "-sending: " << (i * 100 / Loops) << "%" << std::endl;
// } // }
tcq.send(cn, { pid, i }); tcq.send(cn, { pid, i });
} }
if ((fini_p.fetch_add(1, std::memory_order_relaxed) + 1) != N) { if ((fini_p.fetch_add(1, std::memory_order_relaxed) + 1) != N) {
return; return;
} }
// quit // quit
tcq.send(cn, { -1, -1 }); tcq.send(cn, { -1, -1 });
tcq.disconnect(cn); tcq.disconnect(cn);
}}; }};
++pid; ++pid;
} }
for (auto& t : producers) t.join(); for (auto& t : producers) t.join();
for (auto& t : consumers) t.join(); for (auto& t : consumers) t.join();
} }

View File

@ -1,400 +1,400 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <memory> #include <memory>
#include <new> #include <new>
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include "queue.h" #include "queue.h"
#include "prod_cons.h" #include "prod_cons.h"
#include "policy.h" #include "policy.h"
#include "circ/elem_array.h" #include "circ/elem_array.h"
#include "memory/resource.h" #include "memory/resource.h"
#include "test.h" #include "test.h"
namespace { namespace {
struct msg_t { struct msg_t {
int pid_; int pid_;
int dat_; int dat_;
}; };
template <ipc::relat Rp, ipc::relat Rc, ipc::trans Ts> template <ipc::relat Rp, ipc::relat Rc, ipc::trans Ts>
using pc_t = ipc::prod_cons_impl<ipc::wr<Rp, Rc, Ts>>; using pc_t = ipc::prod_cons_impl<ipc::wr<Rp, Rc, Ts>>;
template <std::size_t DataSize, typename Policy> template <std::size_t DataSize, typename Policy>
struct ea_t : public ipc::circ::elem_array<Policy, DataSize, 1> { struct ea_t : public ipc::circ::elem_array<Policy, DataSize, 1> {
ea_t() { std::memset(this, 0, sizeof(ipc::circ::elem_array<Policy, DataSize, 1>)); } ea_t() { std::memset(this, 0, sizeof(ipc::circ::elem_array<Policy, DataSize, 1>)); }
}; };
using cq_t = ea_t< using cq_t = ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::broadcast> pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::broadcast>
>; >;
cq_t* cq__; cq_t* cq__;
bool operator==(msg_t const & m1, msg_t const & m2) { bool operator==(msg_t const & m1, msg_t const & m2) {
return (m1.pid_ == m2.pid_) && (m1.dat_ == m2.dat_); return (m1.pid_ == m2.pid_) && (m1.dat_ == m2.dat_);
} }
} // internal-linkage } // internal-linkage
template <std::size_t D, typename P> template <std::size_t D, typename P>
struct test_verify<ea_t<D, P>> { struct test_verify<ea_t<D, P>> {
std::vector<std::unordered_map<int, std::vector<int>>> list_; std::vector<std::unordered_map<int, std::vector<int>>> list_;
test_verify(int M) test_verify(int M)
: list_(static_cast<std::size_t>(M)) : list_(static_cast<std::size_t>(M))
{} {}
void prepare(void* pt) { void prepare(void* pt) {
std::cout << "start consumer: " << pt << std::endl; std::cout << "start consumer: " << pt << std::endl;
} }
void push_data(int cid, msg_t const & msg) { void push_data(int cid, msg_t const & msg) {
list_[cid][msg.pid_].push_back(msg.dat_); list_[cid][msg.pid_].push_back(msg.dat_);
} }
void verify(int N, int Loops) { void verify(int N, int Loops) {
std::cout << "verifying..." << std::endl; std::cout << "verifying..." << std::endl;
for (auto& c_dats : list_) { for (auto& c_dats : list_) {
for (int n = 0; n < N; ++n) { for (int n = 0; n < N; ++n) {
auto& vec = c_dats[n]; auto& vec = c_dats[n];
//for (int d : vec) { //for (int d : vec) {
// std::cout << d << " "; // std::cout << d << " ";
//} //}
//std::cout << std::endl; //std::cout << std::endl;
QCOMPARE(vec.size(), static_cast<std::size_t>(Loops)); QCOMPARE(vec.size(), static_cast<std::size_t>(Loops));
int i = 0; int i = 0;
for (int d : vec) { for (int d : vec) {
QCOMPARE(i, d); QCOMPARE(i, d);
++i; ++i;
} }
} }
} }
} }
}; };
template <ipc::relat Rp> template <ipc::relat Rp>
struct test_verify<pc_t<Rp, ipc::relat::multi, ipc::trans::unicast>> : test_verify<cq_t> { struct test_verify<pc_t<Rp, ipc::relat::multi, ipc::trans::unicast>> : test_verify<cq_t> {
using test_verify<cq_t>::test_verify; using test_verify<cq_t>::test_verify;
void verify(int N, int Loops) { void verify(int N, int Loops) {
std::cout << "verifying..." << std::endl; std::cout << "verifying..." << std::endl;
for (int n = 0; n < N; ++n) { for (int n = 0; n < N; ++n) {
std::vector<int> datas; std::vector<int> datas;
std::uint64_t sum = 0; std::uint64_t sum = 0;
for (auto& c_dats : list_) { for (auto& c_dats : list_) {
for (int d : c_dats[n]) { for (int d : c_dats[n]) {
datas.push_back(d); datas.push_back(d);
sum += d; sum += d;
} }
} }
QCOMPARE(datas.size(), static_cast<std::size_t>(Loops)); QCOMPARE(datas.size(), static_cast<std::size_t>(Loops));
QCOMPARE(sum, (Loops * std::uint64_t(Loops - 1)) / 2); QCOMPARE(sum, (Loops * std::uint64_t(Loops - 1)) / 2);
} }
} }
}; };
template <typename P> template <typename P>
struct quit_mode; struct quit_mode;
template <ipc::relat Rp, ipc::relat Rc> template <ipc::relat Rp, ipc::relat Rc>
struct quit_mode<pc_t<Rp, Rc, ipc::trans::unicast>> { struct quit_mode<pc_t<Rp, Rc, ipc::trans::unicast>> {
using type = volatile bool; using type = volatile bool;
}; };
template <ipc::relat Rp, ipc::relat Rc> template <ipc::relat Rp, ipc::relat Rc>
struct quit_mode<pc_t<Rp, Rc, ipc::trans::broadcast>> { struct quit_mode<pc_t<Rp, Rc, ipc::trans::broadcast>> {
struct type { struct type {
constexpr type(bool) {} constexpr type(bool) {}
constexpr operator bool() const { return false; } constexpr operator bool() const { return false; }
}; };
}; };
template <std::size_t D, typename P> template <std::size_t D, typename P>
struct test_cq<ea_t<D, P>> { struct test_cq<ea_t<D, P>> {
using ca_t = ea_t<D, P>; using ca_t = ea_t<D, P>;
using cn_t = decltype(std::declval<ca_t>().cursor()); using cn_t = decltype(std::declval<ca_t>().cursor());
typename quit_mode<P>::type quit_ = false; typename quit_mode<P>::type quit_ = false;
ca_t* ca_; ca_t* ca_;
test_cq(ca_t* ca) : ca_(ca) {} test_cq(ca_t* ca) : ca_(ca) {}
cn_t connect() { cn_t connect() {
auto cur = ca_->cursor(); auto cur = ca_->cursor();
ca_->connect(); ca_->connect();
return cur; return cur;
} }
void disconnect(cn_t) { void disconnect(cn_t) {
ca_->disconnect(); ca_->disconnect();
} }
void disconnect(ca_t*) { void disconnect(ca_t*) {
} }
void wait_start(int M) { void wait_start(int M) {
while (ca_->conn_count() != static_cast<std::size_t>(M)) { while (ca_->conn_count() != static_cast<std::size_t>(M)) {
std::this_thread::yield(); std::this_thread::yield();
} }
} }
template <typename F> template <typename F>
void recv(cn_t cur, F&& proc) { void recv(cn_t cur, F&& proc) {
while (1) { while (1) {
msg_t msg; msg_t msg;
while (ca_->pop(&cur, [&msg](void* p) { while (ca_->pop(&cur, [&msg](void* p) {
msg = *static_cast<msg_t*>(p); msg = *static_cast<msg_t*>(p);
})) { })) {
if (msg.pid_ < 0) { if (msg.pid_ < 0) {
quit_ = true; quit_ = true;
return; return;
} }
proc(msg); proc(msg);
} }
if (quit_) return; if (quit_) return;
std::this_thread::yield(); std::this_thread::yield();
} }
} }
ca_t* connect_send() { ca_t* connect_send() {
return ca_; return ca_;
} }
void send(ca_t* ca, msg_t const & msg) { void send(ca_t* ca, msg_t const & msg) {
while (!ca->push([&msg](void* p) { while (!ca->push([&msg](void* p) {
(*static_cast<msg_t*>(p)) = msg; (*static_cast<msg_t*>(p)) = msg;
})) { })) {
std::this_thread::yield(); std::this_thread::yield();
} }
} }
}; };
template <typename... T> template <typename... T>
struct test_cq<ipc::queue<T...>> { struct test_cq<ipc::queue<T...>> {
using cn_t = ipc::queue<T...>; using cn_t = ipc::queue<T...>;
test_cq(void*) {} test_cq(void*) {}
cn_t* connect() { cn_t* connect() {
cn_t* queue = new cn_t { "test-ipc-queue" }; cn_t* queue = new cn_t { "test-ipc-queue" };
[&] { QVERIFY(queue->connect()); } (); [&] { QVERIFY(queue->connect()); } ();
return queue; return queue;
} }
void disconnect(cn_t* queue) { void disconnect(cn_t* queue) {
queue->disconnect(); queue->disconnect();
delete queue; delete queue;
} }
void wait_start(int M) { void wait_start(int M) {
cn_t que("test-ipc-queue"); cn_t que("test-ipc-queue");
while (que.conn_count() != static_cast<std::size_t>(M)) { while (que.conn_count() != static_cast<std::size_t>(M)) {
std::this_thread::yield(); std::this_thread::yield();
} }
} }
template <typename F> template <typename F>
void recv(cn_t* queue, F&& proc) { void recv(cn_t* queue, F&& proc) {
while(1) { while(1) {
typename cn_t::value_t msg; typename cn_t::value_t msg;
while (!queue->pop(msg)) { while (!queue->pop(msg)) {
std::this_thread::yield(); std::this_thread::yield();
} }
if (msg.pid_ < 0) return; if (msg.pid_ < 0) return;
proc(msg); proc(msg);
} }
} }
cn_t* connect_send() { cn_t* connect_send() {
return new cn_t { "test-ipc-queue" }; return new cn_t { "test-ipc-queue" };
} }
void send(cn_t* cn, msg_t const & msg) { void send(cn_t* cn, msg_t const & msg) {
while (!cn->push(msg)) { while (!cn->push(msg)) {
std::this_thread::yield(); std::this_thread::yield();
} }
} }
}; };
namespace { namespace {
class Unit : public TestSuite { class Unit : public TestSuite {
Q_OBJECT Q_OBJECT
const char* name() const { const char* name() const {
return "test_circ"; return "test_circ";
} }
private slots: private slots:
void initTestCase(); void initTestCase();
void cleanupTestCase(); void cleanupTestCase();
void test_inst(); void test_inst();
void test_prod_cons_1v1(); void test_prod_cons_1v1();
void test_prod_cons_1v3(); void test_prod_cons_1v3();
void test_prod_cons_performance(); void test_prod_cons_performance();
void test_queue(); void test_queue();
} /*unit__*/; } unit__;
#include "test_circ.moc" #include "test_circ.moc"
constexpr int LoopCount = 1000000; constexpr int LoopCount = 1000000;
//constexpr int LoopCount = 1000/*0000*/; //constexpr int LoopCount = 1000/*0000*/;
void Unit::initTestCase() { void Unit::initTestCase() {
TestSuite::initTestCase(); TestSuite::initTestCase();
cq__ = new cq_t; cq__ = new cq_t;
} }
void Unit::cleanupTestCase() { void Unit::cleanupTestCase() {
delete cq__; delete cq__;
} }
void Unit::test_inst() { void Unit::test_inst() {
std::cout << "cq_t::head_size = " << cq_t::head_size << std::endl; std::cout << "cq_t::head_size = " << cq_t::head_size << std::endl;
std::cout << "cq_t::data_size = " << cq_t::data_size << std::endl; std::cout << "cq_t::data_size = " << cq_t::data_size << std::endl;
std::cout << "cq_t::elem_size = " << cq_t::elem_size << std::endl; std::cout << "cq_t::elem_size = " << cq_t::elem_size << std::endl;
std::cout << "cq_t::block_size = " << cq_t::block_size << std::endl; std::cout << "cq_t::block_size = " << cq_t::block_size << std::endl;
QCOMPARE(static_cast<std::size_t>(cq_t::data_size), sizeof(msg_t)); QCOMPARE(static_cast<std::size_t>(cq_t::data_size), sizeof(msg_t));
std::cout << "sizeof(ea_t<sizeof(msg_t)>) = " << sizeof(*cq__) << std::endl; std::cout << "sizeof(ea_t<sizeof(msg_t)>) = " << sizeof(*cq__) << std::endl;
} }
template <int N, int M, bool V = true, int Loops = LoopCount> template <int N, int M, bool V = true, int Loops = LoopCount>
void test_prod_cons() { void test_prod_cons() {
benchmark_prod_cons<N, M, Loops, std::conditional_t<V, cq_t, void>>(cq__); benchmark_prod_cons<N, M, Loops, std::conditional_t<V, cq_t, void>>(cq__);
} }
void Unit::test_prod_cons_1v1() { void Unit::test_prod_cons_1v1() {
// ea_t< // ea_t<
// sizeof(msg_t), // sizeof(msg_t),
// pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast> // pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
// > el_arr_mmb; // > el_arr_mmb;
// benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmb); // benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmb);
// benchmark_prod_cons<2, 1, LoopCount, void>(&el_arr_mmb); // benchmark_prod_cons<2, 1, LoopCount, void>(&el_arr_mmb);
ea_t< ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::single, ipc::relat::single, ipc::trans::unicast> pc_t<ipc::relat::single, ipc::relat::single, ipc::trans::unicast>
> el_arr_ssu; > el_arr_ssu;
benchmark_prod_cons<1, 1, LoopCount, cq_t>(&el_arr_ssu); benchmark_prod_cons<1, 1, LoopCount, cq_t>(&el_arr_ssu);
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_ssu); benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_ssu);
ea_t< ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast> pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast>
> el_arr_smu; > el_arr_smu;
benchmark_prod_cons<1, 1, LoopCount, decltype(el_arr_smu)::policy_t>(&el_arr_smu); benchmark_prod_cons<1, 1, LoopCount, decltype(el_arr_smu)::policy_t>(&el_arr_smu);
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_smu); benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_smu);
ea_t< ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast> pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast>
> el_arr_mmu; > el_arr_mmu;
benchmark_prod_cons<1, 1, LoopCount, decltype(el_arr_mmu)::policy_t>(&el_arr_mmu); benchmark_prod_cons<1, 1, LoopCount, decltype(el_arr_mmu)::policy_t>(&el_arr_mmu);
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmu); benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmu);
test_prod_cons<1, 1>(); test_prod_cons<1, 1>();
test_prod_cons<1, 1, false>(); test_prod_cons<1, 1, false>();
ea_t< ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast> pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
> el_arr_mmb; > el_arr_mmb;
benchmark_prod_cons<1, 1, LoopCount, cq_t>(&el_arr_mmb); benchmark_prod_cons<1, 1, LoopCount, cq_t>(&el_arr_mmb);
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmb); benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmb);
} }
void Unit::test_prod_cons_1v3() { void Unit::test_prod_cons_1v3() {
ea_t< ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast> pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast>
> el_arr_smu; > el_arr_smu;
benchmark_prod_cons<1, 3, LoopCount, decltype(el_arr_smu)::policy_t>(&el_arr_smu); benchmark_prod_cons<1, 3, LoopCount, decltype(el_arr_smu)::policy_t>(&el_arr_smu);
benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_smu); benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_smu);
ea_t< ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast> pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast>
> el_arr_mmu; > el_arr_mmu;
benchmark_prod_cons<1, 3, LoopCount, decltype(el_arr_mmu)::policy_t>(&el_arr_mmu); benchmark_prod_cons<1, 3, LoopCount, decltype(el_arr_mmu)::policy_t>(&el_arr_mmu);
benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_mmu); benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_mmu);
test_prod_cons<1, 3>(); test_prod_cons<1, 3>();
test_prod_cons<1, 3, false>(); test_prod_cons<1, 3, false>();
ea_t< ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast> pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
> el_arr_mmb; > el_arr_mmb;
benchmark_prod_cons<1, 3, LoopCount, cq_t>(&el_arr_mmb); benchmark_prod_cons<1, 3, LoopCount, cq_t>(&el_arr_mmb);
benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_mmb); benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_mmb);
} }
void Unit::test_prod_cons_performance() { void Unit::test_prod_cons_performance() {
ea_t< ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast> pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast>
> el_arr_smu; > el_arr_smu;
ipc::detail::static_for<8>([&el_arr_smu](auto index) { ipc::detail::static_for<8>([&el_arr_smu](auto index) {
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_smu); benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_smu);
}); });
ipc::detail::static_for<8>([](auto index) { ipc::detail::static_for<8>([](auto index) {
test_prod_cons<1, decltype(index)::value + 1, false>(); test_prod_cons<1, decltype(index)::value + 1, false>();
}); });
test_prod_cons<1, 8>(); // test & verify test_prod_cons<1, 8>(); // test & verify
ea_t< ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast> pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast>
> el_arr_mmu; > el_arr_mmu;
ipc::detail::static_for<8>([&el_arr_mmu](auto index) { ipc::detail::static_for<8>([&el_arr_mmu](auto index) {
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmu); benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmu);
}); });
ipc::detail::static_for<8>([&el_arr_mmu](auto index) { ipc::detail::static_for<8>([&el_arr_mmu](auto index) {
benchmark_prod_cons<decltype(index)::value + 1, 1, LoopCount, void>(&el_arr_mmu); benchmark_prod_cons<decltype(index)::value + 1, 1, LoopCount, void>(&el_arr_mmu);
}); });
ipc::detail::static_for<8>([&el_arr_mmu](auto index) { ipc::detail::static_for<8>([&el_arr_mmu](auto index) {
benchmark_prod_cons<decltype(index)::value + 1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmu); benchmark_prod_cons<decltype(index)::value + 1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmu);
}); });
ea_t< ea_t<
sizeof(msg_t), sizeof(msg_t),
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast> pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
> el_arr_mmb; > el_arr_mmb;
ipc::detail::static_for<8>([&el_arr_mmb](auto index) { ipc::detail::static_for<8>([&el_arr_mmb](auto index) {
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmb); benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmb);
}); });
ipc::detail::static_for<8>([&el_arr_mmb](auto index) { ipc::detail::static_for<8>([&el_arr_mmb](auto index) {
benchmark_prod_cons<decltype(index)::value + 1, 1, LoopCount, void>(&el_arr_mmb); benchmark_prod_cons<decltype(index)::value + 1, 1, LoopCount, void>(&el_arr_mmb);
}); });
ipc::detail::static_for<8>([&el_arr_mmb](auto index) { ipc::detail::static_for<8>([&el_arr_mmb](auto index) {
benchmark_prod_cons<decltype(index)::value + 1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmb); benchmark_prod_cons<decltype(index)::value + 1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmb);
}); });
} }
void Unit::test_queue() { void Unit::test_queue() {
using queue_t = ipc::queue<msg_t, ipc::policy::choose< using queue_t = ipc::queue<msg_t, ipc::policy::choose<
ipc::circ::elem_array, ipc::circ::elem_array,
ipc::wr<ipc::relat::single, ipc::relat::multi, ipc::trans::broadcast> ipc::wr<ipc::relat::single, ipc::relat::multi, ipc::trans::broadcast>
>>; >>;
queue_t queue; queue_t queue;
QVERIFY(!queue.push(msg_t { 1, 2 })); QVERIFY(!queue.push(msg_t { 1, 2 }));
msg_t msg {}; msg_t msg {};
QVERIFY(!queue.pop(msg)); QVERIFY(!queue.pop(msg));
QCOMPARE(msg, (msg_t {})); QCOMPARE(msg, (msg_t {}));
QVERIFY(sizeof(decltype(queue)::elems_t) <= sizeof(*cq__)); QVERIFY(sizeof(decltype(queue)::elems_t) <= sizeof(*cq__));
ipc::detail::static_for<16>([](auto index) { ipc::detail::static_for<16>([](auto index) {
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount>((queue_t*)nullptr); benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount>((queue_t*)nullptr);
}); });
} }
} // internal-linkage } // internal-linkage

964
test/test_ipc.cpp Executable file → Normal file
View File

@ -1,482 +1,482 @@
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <type_traits> #include <type_traits>
#include <iostream> #include <iostream>
#include <shared_mutex> #include <shared_mutex>
#include <mutex> #include <mutex>
#include <typeinfo> #include <typeinfo>
#include <memory> #include <memory>
#include <string> #include <string>
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <limits> #include <limits>
#include <utility> #include <utility>
#include "stopwatch.hpp" #include "stopwatch.hpp"
#include "spin_lock.hpp" #include "spin_lock.hpp"
#include "random.hpp" #include "random.hpp"
#include "ipc.h" #include "ipc.h"
#include "rw_lock.h" #include "rw_lock.h"
#include "memory/resource.h" #include "memory/resource.h"
#include "test.h" #include "test.h"
namespace { namespace {
std::vector<ipc::buff_t> datas__; std::vector<ipc::buff_t> datas__;
constexpr int DataMin = 2; constexpr int DataMin = 2;
constexpr int DataMax = 256; constexpr int DataMax = 256;
constexpr int LoopCount = 100000; constexpr int LoopCount = 100000;
//constexpr int LoopCount = 1000; //constexpr int LoopCount = 1000;
} // internal-linkage } // internal-linkage
template <typename T> template <typename T>
struct test_verify { struct test_verify {
std::vector<std::vector<ipc::buff_t>> list_; std::vector<std::vector<ipc::buff_t>> list_;
test_verify(int M) test_verify(int M)
: list_(static_cast<std::size_t>(M)) : list_(static_cast<std::size_t>(M))
{} {}
void prepare(void* /*pt*/) {} void prepare(void* /*pt*/) {}
void push_data(int cid, ipc::buff_t & msg) { void push_data(int cid, ipc::buff_t & msg) {
list_[cid].emplace_back(std::move(msg)); list_[cid].emplace_back(std::move(msg));
} }
void verify(int /*N*/, int /*Loops*/) { void verify(int /*N*/, int /*Loops*/) {
std::cout << "verifying..." << std::endl; std::cout << "verifying..." << std::endl;
for (auto& c_dats : list_) { for (auto& c_dats : list_) {
QCOMPARE(datas__.size(), c_dats.size()); QCOMPARE(datas__.size(), c_dats.size());
std::size_t i = 0; std::size_t i = 0;
for (auto& d : c_dats) { for (auto& d : c_dats) {
QCOMPARE(datas__[i++], d); QCOMPARE(datas__[i++], d);
} }
} }
} }
}; };
template <> template <>
struct test_cq<ipc::route> { struct test_cq<ipc::route> {
using cn_t = ipc::route; using cn_t = ipc::route;
std::string conn_name_; std::string conn_name_;
test_cq(void*) test_cq(void*)
: conn_name_("test-ipc-route") { : conn_name_("test-ipc-route") {
} }
cn_t connect() { cn_t connect() {
return cn_t { conn_name_.c_str() }; return cn_t { conn_name_.c_str() };
} }
void disconnect(cn_t& cn) { void disconnect(cn_t& cn) {
cn.disconnect(); cn.disconnect();
} }
void wait_start(int M) { void wait_start(int M) {
cn_t::wait_for_recv(conn_name_.c_str(), static_cast<std::size_t>(M)); cn_t::wait_for_recv(conn_name_.c_str(), static_cast<std::size_t>(M));
} }
template <typename F> template <typename F>
void recv(cn_t& cn, F&& proc) { void recv(cn_t& cn, F&& proc) {
do { do {
auto msg = cn.recv(); auto msg = cn.recv();
if (msg.size() < 2) { if (msg.size() < 2) {
QCOMPARE(msg, ipc::buff_t('\0')); QCOMPARE(msg, ipc::buff_t('\0'));
return; return;
} }
proc(msg); proc(msg);
} while(1); } while(1);
} }
cn_t connect_send() { cn_t connect_send() {
return connect(); return connect();
} }
void send(cn_t& cn, const std::array<int, 2>& info) { void send(cn_t& cn, const std::array<int, 2>& info) {
int n = info[1]; int n = info[1];
if (n < 0) { if (n < 0) {
/*QVERIFY*/(cn.send(ipc::buff_t('\0'))); /*QVERIFY*/(cn.send(ipc::buff_t('\0')));
} }
else /*QVERIFY*/(cn.send(datas__[static_cast<decltype(datas__)::size_type>(n)])); else /*QVERIFY*/(cn.send(datas__[static_cast<decltype(datas__)::size_type>(n)]));
} }
}; };
template <> template <>
struct test_cq<ipc::channel> { struct test_cq<ipc::channel> {
using cn_t = ipc::channel; using cn_t = ipc::channel;
std::string conn_name_; std::string conn_name_;
int m_ = 0; int m_ = 0;
test_cq(void*) test_cq(void*)
: conn_name_("test-ipc-channel") { : conn_name_("test-ipc-channel") {
} }
cn_t connect() { cn_t connect() {
return cn_t { conn_name_.c_str() }; return cn_t { conn_name_.c_str() };
} }
void disconnect(cn_t& cn) { void disconnect(cn_t& cn) {
cn.disconnect(); cn.disconnect();
} }
void wait_start(int M) { m_ = M; } void wait_start(int M) { m_ = M; }
template <typename F> template <typename F>
void recv(cn_t& cn, F&& proc) { void recv(cn_t& cn, F&& proc) {
do { do {
auto msg = cn.recv(); auto msg = cn.recv();
if (msg.size() < 2) { if (msg.size() < 2) {
QCOMPARE(msg, ipc::buff_t('\0')); QCOMPARE(msg, ipc::buff_t('\0'));
return; return;
} }
proc(msg); proc(msg);
} while(1); } while(1);
} }
cn_t connect_send() { cn_t connect_send() {
return connect(); return connect();
} }
void send(cn_t& cn, const std::array<int, 2>& info) { void send(cn_t& cn, const std::array<int, 2>& info) {
thread_local struct s_dummy { thread_local struct s_dummy {
s_dummy(cn_t& cn, int m) { s_dummy(cn_t& cn, int m) {
cn.wait_for_recv(static_cast<std::size_t>(m)); cn.wait_for_recv(static_cast<std::size_t>(m));
// std::printf("start to send: %d.\n", m); // std::printf("start to send: %d.\n", m);
} }
} _(cn, m_); } _(cn, m_);
int n = info[1]; int n = info[1];
if (n < 0) { if (n < 0) {
/*QVERIFY*/(cn.send(ipc::buff_t('\0'))); /*QVERIFY*/(cn.send(ipc::buff_t('\0')));
} }
else /*QVERIFY*/(cn.send(datas__[static_cast<decltype(datas__)::size_type>(n)])); else /*QVERIFY*/(cn.send(datas__[static_cast<decltype(datas__)::size_type>(n)]));
} }
}; };
namespace { namespace {
class Unit : public TestSuite { class Unit : public TestSuite {
Q_OBJECT Q_OBJECT
const char* name() const { const char* name() const {
return "test_ipc"; return "test_ipc";
} }
private slots: private slots:
void initTestCase(); void initTestCase();
void cleanupTestCase(); void cleanupTestCase();
void test_rw_lock(); void test_rw_lock();
void test_route(); void test_route();
void test_route_rtt(); void test_route_rtt();
void test_route_performance(); void test_route_performance();
void test_channel(); void test_channel();
void test_channel_rtt(); void test_channel_rtt();
void test_channel_performance(); void test_channel_performance();
} unit__; } unit__;
#include "test_ipc.moc" #include "test_ipc.moc"
void Unit::initTestCase() { void Unit::initTestCase() {
TestSuite::initTestCase(); TestSuite::initTestCase();
capo::random<> rdm { DataMin, DataMax }; capo::random<> rdm { DataMin, DataMax };
capo::random<> bit { 0, (std::numeric_limits<ipc::byte_t>::max)() }; capo::random<> bit { 0, (std::numeric_limits<ipc::byte_t>::max)() };
for (int i = 0; i < LoopCount; ++i) { for (int i = 0; i < LoopCount; ++i) {
std::size_t n = static_cast<std::size_t>(rdm()); std::size_t n = static_cast<std::size_t>(rdm());
ipc::buff_t buff { ipc::buff_t buff {
new ipc::byte_t[n], n, new ipc::byte_t[n], n,
[](void* p, std::size_t) { [](void* p, std::size_t) {
delete [] static_cast<ipc::byte_t*>(p); delete [] static_cast<ipc::byte_t*>(p);
} }
}; };
for (std::size_t k = 0; k < buff.size(); ++k) { for (std::size_t k = 0; k < buff.size(); ++k) {
static_cast<ipc::byte_t*>(buff.data())[k] = static_cast<ipc::byte_t>(bit()); static_cast<ipc::byte_t*>(buff.data())[k] = static_cast<ipc::byte_t>(bit());
} }
datas__.emplace_back(std::move(buff)); datas__.emplace_back(std::move(buff));
} }
} }
void Unit::cleanupTestCase() { void Unit::cleanupTestCase() {
datas__.clear(); datas__.clear();
} }
template <typename T> template <typename T>
constexpr T acc(T b, T e) { constexpr T acc(T b, T e) {
return (e + b) * (e - b + 1) / 2; return (e + b) * (e - b + 1) / 2;
} }
template <typename Mutex> template <typename Mutex>
struct lc_wrapper : Mutex { struct lc_wrapper : Mutex {
void lock_shared () { Mutex::lock (); } void lock_shared () { Mutex::lock (); }
void unlock_shared() { Mutex::unlock(); } void unlock_shared() { Mutex::unlock(); }
}; };
template <typename Lc, int W, int R, int Loops = LoopCount> template <typename Lc, int W, int R, int Loops = LoopCount>
void benchmark_lc() { void benchmark_lc() {
std::thread w_trd[W]; std::thread w_trd[W];
std::thread r_trd[R]; std::thread r_trd[R];
std::atomic_int fini { 0 }; std::atomic_int fini { 0 };
// std::atomic_bool wf { false }; // std::atomic_bool wf { false };
std::vector<int> datas; std::vector<int> datas;
Lc lc; Lc lc;
test_stopwatch sw; test_stopwatch sw;
std::cout << std::endl << type_name<Lc>() << std::endl; std::cout << std::endl << type_name<Lc>() << std::endl;
for (auto& t : r_trd) { for (auto& t : r_trd) {
t = std::thread([&] { t = std::thread([&] {
std::vector<int> seq; std::vector<int> seq;
std::size_t cnt = 0; std::size_t cnt = 0;
while (1) { while (1) {
int x = -1; int x = -1;
{ {
std::shared_lock<Lc> guard { lc }; std::shared_lock<Lc> guard { lc };
// QVERIFY(!wf); // QVERIFY(!wf);
if (cnt < datas.size()) { if (cnt < datas.size()) {
x = datas[cnt]; x = datas[cnt];
} }
// std::this_thread::sleep_for(std::chrono::milliseconds(1)); // std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (x == 0) break; // quit if (x == 0) break; // quit
if (x != -1) { if (x != -1) {
seq.push_back(x); seq.push_back(x);
++cnt; ++cnt;
} }
} }
std::this_thread::yield(); std::this_thread::yield();
} }
if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == R) { if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == R) {
sw.print_elapsed(W, R, Loops); sw.print_elapsed(W, R, Loops);
} }
std::uint64_t sum = 0; std::uint64_t sum = 0;
for (int i : seq) sum += static_cast<std::uint64_t>(i); for (int i : seq) sum += static_cast<std::uint64_t>(i);
QCOMPARE(sum, acc<std::uint64_t>(1, Loops) * W); QCOMPARE(sum, acc<std::uint64_t>(1, Loops) * W);
}); });
} }
for (auto& t : w_trd) { for (auto& t : w_trd) {
t = std::thread([&] { t = std::thread([&] {
sw.start(); sw.start();
for (int i = 1; i <= Loops; ++i) { for (int i = 1; i <= Loops; ++i) {
{ {
std::unique_lock<Lc> guard { lc }; std::unique_lock<Lc> guard { lc };
// wf = true; // wf = true;
datas.push_back(i); datas.push_back(i);
// std::this_thread::sleep_for(std::chrono::milliseconds(1)); // std::this_thread::sleep_for(std::chrono::milliseconds(1));
// wf = false; // wf = false;
} }
std::this_thread::yield(); std::this_thread::yield();
} }
}); });
} }
for (auto& t : w_trd) t.join(); for (auto& t : w_trd) t.join();
lc.lock(); lc.lock();
datas.push_back(0); datas.push_back(0);
lc.unlock(); lc.unlock();
for (auto& t : r_trd) t.join(); for (auto& t : r_trd) t.join();
} }
template <int W, int R> template <int W, int R>
void test_lock_performance() { void test_lock_performance() {
std::cout << std::endl std::cout << std::endl
<< "test_lock_performance: [" << W << ":" << R << "]" << "test_lock_performance: [" << W << ":" << R << "]"
<< std::endl; << std::endl;
benchmark_lc<ipc::rw_lock , W, R>(); benchmark_lc<ipc::rw_lock , W, R>();
benchmark_lc<lc_wrapper< ipc::spin_lock>, W, R>(); benchmark_lc<lc_wrapper< ipc::spin_lock>, W, R>();
benchmark_lc<lc_wrapper<capo::spin_lock>, W, R>(); benchmark_lc<lc_wrapper<capo::spin_lock>, W, R>();
benchmark_lc<lc_wrapper<std::mutex> , W, R>(); benchmark_lc<lc_wrapper<std::mutex> , W, R>();
benchmark_lc<std::shared_timed_mutex , W, R>(); benchmark_lc<std::shared_timed_mutex , W, R>();
} }
void Unit::test_rw_lock() { void Unit::test_rw_lock() {
// test_lock_performance<1, 1>(); // test_lock_performance<1, 1>();
// test_lock_performance<4, 4>(); // test_lock_performance<4, 4>();
// test_lock_performance<1, 8>(); // test_lock_performance<1, 8>();
// test_lock_performance<8, 1>(); // test_lock_performance<8, 1>();
} }
template <typename T, int N, int M, bool V = true, int Loops = LoopCount> template <typename T, int N, int M, bool V = true, int Loops = LoopCount>
void test_prod_cons() { void test_prod_cons() {
benchmark_prod_cons<N, M, Loops, std::conditional_t<V, T, void>>((T*)nullptr); benchmark_prod_cons<N, M, Loops, std::conditional_t<V, T, void>>((T*)nullptr);
} }
void Unit::test_route() { void Unit::test_route() {
//return; //return;
std::vector<char const *> const datas = { std::vector<char const *> const datas = {
"hello!", "hello!",
"foo", "foo",
"bar", "bar",
"ISO/IEC", "ISO/IEC",
"14882:2011", "14882:2011",
"ISO/IEC 14882:2017 Information technology - Programming languages - C++", "ISO/IEC 14882:2017 Information technology - Programming languages - C++",
"ISO/IEC 14882:2020", "ISO/IEC 14882:2020",
"Modern C++ Design: Generic Programming and Design Patterns Applied" "Modern C++ Design: Generic Programming and Design Patterns Applied"
}; };
std::thread t1 {[&] { std::thread t1 {[&] {
ipc::route cc { "my-ipc-route" }; ipc::route cc { "my-ipc-route" };
for (std::size_t i = 0; i < datas.size(); ++i) { for (std::size_t i = 0; i < datas.size(); ++i) {
ipc::buff_t dd = cc.recv(); ipc::buff_t dd = cc.recv();
std::cout << "recv: " << (char*)dd.data() << std::endl; std::cout << "recv: " << (char*)dd.data() << std::endl;
QCOMPARE(dd.size(), std::strlen(datas[i]) + 1); QCOMPARE(dd.size(), std::strlen(datas[i]) + 1);
QVERIFY(std::memcmp(dd.data(), datas[i], dd.size()) == 0); QVERIFY(std::memcmp(dd.data(), datas[i], dd.size()) == 0);
} }
}}; }};
std::thread t2 {[&] { std::thread t2 {[&] {
ipc::route cc { "my-ipc-route" }; ipc::route cc { "my-ipc-route" };
while (cc.recv_count() == 0) { while (cc.recv_count() == 0) {
std::this_thread::yield(); std::this_thread::yield();
} }
for (std::size_t i = 0; i < datas.size(); ++i) { for (std::size_t i = 0; i < datas.size(); ++i) {
std::cout << "sending: " << datas[i] << std::endl; std::cout << "sending: " << datas[i] << std::endl;
QVERIFY(cc.send(datas[i])); QVERIFY(cc.send(datas[i]));
} }
}}; }};
t1.join(); t1.join();
t2.join(); t2.join();
test_prod_cons<ipc::route, 1, 1>(); // test & verify test_prod_cons<ipc::route, 1, 1>(); // test & verify
} }
void Unit::test_route_rtt() { void Unit::test_route_rtt() {
//return; //return;
test_stopwatch sw; test_stopwatch sw;
std::thread t1 {[&] { std::thread t1 {[&] {
ipc::route cc { "my-ipc-route-1" }; ipc::route cc { "my-ipc-route-1" };
ipc::route cr { "my-ipc-route-2" }; ipc::route cr { "my-ipc-route-2" };
for (std::size_t i = 0;; ++i) { for (std::size_t i = 0;; ++i) {
auto dd = cc.recv(); auto dd = cc.recv();
if (dd.size() < 2) return; if (dd.size() < 2) return;
//std::cout << "recv: " << i << "-[" << dd.size() << "]" << std::endl; //std::cout << "recv: " << i << "-[" << dd.size() << "]" << std::endl;
while (!cr.send(ipc::buff_t('a'))) { while (!cr.send(ipc::buff_t('a'))) {
std::this_thread::yield(); std::this_thread::yield();
} }
} }
}}; }};
std::thread t2 {[&] { std::thread t2 {[&] {
ipc::route cc { "my-ipc-route-1" }; ipc::route cc { "my-ipc-route-1" };
ipc::route cr { "my-ipc-route-2" }; ipc::route cr { "my-ipc-route-2" };
while (cc.recv_count() == 0) { while (cc.recv_count() == 0) {
std::this_thread::yield(); std::this_thread::yield();
} }
sw.start(); sw.start();
for (std::size_t i = 0; i < LoopCount; ++i) { for (std::size_t i = 0; i < LoopCount; ++i) {
cc.send(datas__[i]); cc.send(datas__[i]);
//std::cout << "sent: " << i << "-[" << datas__[i].size() << "]" << std::endl; //std::cout << "sent: " << i << "-[" << datas__[i].size() << "]" << std::endl;
/*auto dd = */cr.recv(); /*auto dd = */cr.recv();
// if (dd.size() != 1 || dd[0] != 'a') { // if (dd.size() != 1 || dd[0] != 'a') {
// QVERIFY(false); // QVERIFY(false);
// } // }
} }
cc.send(ipc::buff_t('\0')); cc.send(ipc::buff_t('\0'));
t1.join(); t1.join();
sw.print_elapsed(1, 1, LoopCount); sw.print_elapsed(1, 1, LoopCount);
}}; }};
t2.join(); t2.join();
} }
void Unit::test_route_performance() { void Unit::test_route_performance() {
//return; //return;
ipc::detail::static_for<8>([](auto index) { ipc::detail::static_for<8>([](auto index) {
test_prod_cons<ipc::route, 1, decltype(index)::value + 1, false>(); test_prod_cons<ipc::route, 1, decltype(index)::value + 1, false>();
}); });
test_prod_cons<ipc::route, 1, 8>(); // test & verify test_prod_cons<ipc::route, 1, 8>(); // test & verify
} }
void Unit::test_channel() { void Unit::test_channel() {
//return; //return;
std::thread t1 {[&] { std::thread t1 {[&] {
ipc::channel cc { "my-ipc-channel" }; ipc::channel cc { "my-ipc-channel" };
for (std::size_t i = 0;; ++i) { for (std::size_t i = 0;; ++i) {
ipc::buff_t dd = cc.recv(); ipc::buff_t dd = cc.recv();
if (dd.size() < 2) return; if (dd.size() < 2) return;
QCOMPARE(dd, datas__[i]); QCOMPARE(dd, datas__[i]);
} }
}}; }};
std::thread t2 {[&] { std::thread t2 {[&] {
ipc::channel cc { "my-ipc-channel" }; ipc::channel cc { "my-ipc-channel" };
cc.wait_for_recv(1); cc.wait_for_recv(1);
for (std::size_t i = 0; i < static_cast<std::size_t>((std::min)(100, LoopCount)); ++i) { for (std::size_t i = 0; i < static_cast<std::size_t>((std::min)(100, LoopCount)); ++i) {
std::cout << "sending: " << i << "-[" << datas__[i].size() << "]" << std::endl; std::cout << "sending: " << i << "-[" << datas__[i].size() << "]" << std::endl;
cc.send(datas__[i]); cc.send(datas__[i]);
} }
cc.send(ipc::buff_t('\0')); cc.send(ipc::buff_t('\0'));
t1.join(); t1.join();
}}; }};
t2.join(); t2.join();
} }
void Unit::test_channel_rtt() { void Unit::test_channel_rtt() {
test_stopwatch sw; test_stopwatch sw;
std::thread t1 {[&] { std::thread t1 {[&] {
ipc::channel cc { "my-ipc-channel" }; ipc::channel cc { "my-ipc-channel" };
bool recv_2 = false; bool recv_2 = false;
for (std::size_t i = 0;; ++i) { for (std::size_t i = 0;; ++i) {
auto dd = cc.recv(); auto dd = cc.recv();
if (dd.size() < 2) return; if (dd.size() < 2) return;
//if (i % 1000 == 0) { //if (i % 1000 == 0) {
// std::cout << "recv: " << i << "-[" << dd.size() << "]" << std::endl; // std::cout << "recv: " << i << "-[" << dd.size() << "]" << std::endl;
//} //}
while (!recv_2) { while (!recv_2) {
recv_2 = cc.wait_for_recv(2); recv_2 = cc.wait_for_recv(2);
} }
cc.send(ipc::buff_t('a')); cc.send(ipc::buff_t('a'));
} }
}}; }};
std::thread t2 {[&] { std::thread t2 {[&] {
ipc::channel cc { "my-ipc-channel" }; ipc::channel cc { "my-ipc-channel" };
cc.wait_for_recv(1); cc.wait_for_recv(1);
sw.start(); sw.start();
for (std::size_t i = 0; i < LoopCount; ++i) { for (std::size_t i = 0; i < LoopCount; ++i) {
//if (i % 1000 == 0) { //if (i % 1000 == 0) {
// std::cout << "send: " << i << "-[" << datas__[i].size() << "]" << std::endl; // std::cout << "send: " << i << "-[" << datas__[i].size() << "]" << std::endl;
//} //}
cc.send(datas__[i]); cc.send(datas__[i]);
/*auto dd = */cc.recv(); /*auto dd = */cc.recv();
//if (dd.size() != 1 || dd.data<char>()[0] != 'a') { //if (dd.size() != 1 || dd.data<char>()[0] != 'a') {
// std::cout << "recv ack fail: " << i << "-[" << dd.size() << "]" << std::endl; // std::cout << "recv ack fail: " << i << "-[" << dd.size() << "]" << std::endl;
// QVERIFY(false); // QVERIFY(false);
//} //}
} }
cc.send(ipc::buff_t('\0')); cc.send(ipc::buff_t('\0'));
t1.join(); t1.join();
sw.print_elapsed(1, 1, LoopCount); sw.print_elapsed(1, 1, LoopCount);
}}; }};
t2.join(); t2.join();
} }
void Unit::test_channel_performance() { void Unit::test_channel_performance() {
ipc::detail::static_for<8>([](auto index) { ipc::detail::static_for<8>([](auto index) {
test_prod_cons<ipc::channel, 1, decltype(index)::value + 1, false>(); test_prod_cons<ipc::channel, 1, decltype(index)::value + 1, false>();
}); });
ipc::detail::static_for<8>([](auto index) { ipc::detail::static_for<8>([](auto index) {
test_prod_cons<ipc::channel, decltype(index)::value + 1, 1, false>(); test_prod_cons<ipc::channel, decltype(index)::value + 1, 1, false>();
}); });
ipc::detail::static_for<8>([](auto index) { ipc::detail::static_for<8>([](auto index) {
test_prod_cons<ipc::channel, decltype(index)::value + 1, test_prod_cons<ipc::channel, decltype(index)::value + 1,
decltype(index)::value + 1, false>(); decltype(index)::value + 1, false>();
}); });
} }
} // internal-linkage } // internal-linkage

375
test/test_mem.cpp Executable file → Normal file
View File

@ -1,186 +1,189 @@
#include <vector> #include <vector>
#include <thread> #include <thread>
#include <atomic> #include <atomic>
#include <cstddef> #include <cstddef>
#include "random.hpp" #include "random.hpp"
#include "memory/resource.h" #include "memory/resource.h"
#include "pool_alloc.h" #include "pool_alloc.h"
#include "test.h" #include "test.h"
namespace { namespace {
class Unit : public TestSuite { class Unit : public TestSuite {
Q_OBJECT Q_OBJECT
const char* name() const { const char* name() const {
return "test_mem"; return "test_mem";
} }
private slots: private slots:
void initTestCase(); void initTestCase();
void test_alloc_free(); void test_alloc_free();
void test_static(); void test_static();
void test_pool(); void test_pool();
} /*unit__*/; } unit__;
#include "test_mem.moc" #include "test_mem.moc"
constexpr int DataMin = sizeof(void*); constexpr int DataMin = sizeof(void*);
constexpr int DataMax = sizeof(void*) * 16; constexpr int DataMax = sizeof(void*) * 16;
constexpr int LoopCount = 1000000; constexpr int LoopCount = 1000000;
std::vector<std::size_t> sizes__; std::vector<std::size_t> sizes__;
template <typename M> template <typename M>
struct alloc_ix_t { struct alloc_ix_t {
static std::vector<int> ix_[2]; static std::vector<int> ix_[2];
static bool inited_; static bool inited_;
}; };
template <typename M> template <typename M>
std::vector<int> alloc_ix_t<M>::ix_[2] = { std::vector<int>(LoopCount), std::vector<int>(LoopCount) }; std::vector<int> alloc_ix_t<M>::ix_[2] = { std::vector<int>(LoopCount), std::vector<int>(LoopCount) };
template <typename M> template <typename M>
bool alloc_ix_t<M>::inited_ = false; bool alloc_ix_t<M>::inited_ = false;
struct alloc_random : alloc_ix_t<alloc_random> { struct alloc_random : alloc_ix_t<alloc_random> {
alloc_random() { alloc_random() {
if (inited_) return; if (inited_) return;
inited_ = true; inited_ = true;
capo::random<> rdm_index(0, LoopCount - 1); capo::random<> rdm_index(0, LoopCount - 1);
for (int i = 0; i < LoopCount; ++i) { for (int i = 0; i < LoopCount; ++i) {
ix_[0][static_cast<std::size_t>(i)] = ix_[0][static_cast<std::size_t>(i)] =
ix_[1][static_cast<std::size_t>(i)] = rdm_index(); ix_[1][static_cast<std::size_t>(i)] = rdm_index();
} }
} }
}; };
struct alloc_LIFO : alloc_ix_t<alloc_LIFO> { struct alloc_LIFO : alloc_ix_t<alloc_LIFO> {
alloc_LIFO() { alloc_LIFO() {
if (inited_) return; if (inited_) return;
inited_ = true; inited_ = true;
for (int i = 0, n = LoopCount - 1; i < LoopCount; ++i, --n) { for (int i = 0, n = LoopCount - 1; i < LoopCount; ++i, --n) {
ix_[0][static_cast<std::size_t>(i)] = ix_[0][static_cast<std::size_t>(i)] =
ix_[1][static_cast<std::size_t>(n)] = i; ix_[1][static_cast<std::size_t>(n)] = i;
} }
} }
}; };
struct alloc_FIFO : alloc_ix_t<alloc_FIFO> { struct alloc_FIFO : alloc_ix_t<alloc_FIFO> {
alloc_FIFO() { alloc_FIFO() {
if (inited_) return; if (inited_) return;
inited_ = true; inited_ = true;
for (int i = 0; i < LoopCount; ++i) { for (int i = 0; i < LoopCount; ++i) {
ix_[0][static_cast<std::size_t>(i)] = ix_[0][static_cast<std::size_t>(i)] =
ix_[1][static_cast<std::size_t>(i)] = i; ix_[1][static_cast<std::size_t>(i)] = i;
} }
} }
}; };
void Unit::initTestCase() { void Unit::initTestCase() {
TestSuite::initTestCase(); TestSuite::initTestCase();
capo::random<> rdm { DataMin, DataMax }; capo::random<> rdm { DataMin, DataMax };
for (int i = 0; i < LoopCount; ++i) { for (int i = 0; i < LoopCount; ++i) {
sizes__.emplace_back(static_cast<std::size_t>(rdm())); sizes__.emplace_back(static_cast<std::size_t>(rdm()));
} }
} }
template <typename AllocT> template <typename AllocT>
void benchmark_alloc() { void benchmark_alloc() {
std::cout << std::endl << type_name<AllocT>() << std::endl; std::cout << std::endl << type_name<AllocT>() << std::endl;
test_stopwatch sw; test_stopwatch sw;
sw.start(); sw.start();
for (std::size_t x = 0; x < 10; ++x) for (std::size_t x = 0; x < 10; ++x)
for (std::size_t n = 0; n < LoopCount; ++n) { for (std::size_t n = 0; n < LoopCount; ++n) {
std::size_t s = sizes__[n]; std::size_t s = sizes__[n];
AllocT::free(AllocT::alloc(s), s); AllocT::free(AllocT::alloc(s), s);
} }
sw.print_elapsed<1>(DataMin, DataMax, LoopCount * 10); sw.print_elapsed<1>(DataMin, DataMax, LoopCount * 10);
} }
void Unit::test_alloc_free() { void Unit::test_alloc_free() {
benchmark_alloc<ipc::mem::static_alloc>(); benchmark_alloc<ipc::mem::static_alloc>();
benchmark_alloc<ipc::mem::async_pool_alloc>(); benchmark_alloc<ipc::mem::async_pool_alloc>();
} }
template <typename AllocT, typename ModeT, int ThreadsN> template <typename AllocT, typename ModeT, int ThreadsN>
void benchmark_alloc() { void benchmark_alloc() {
std::cout << std::endl std::cout << std::endl
<< "[Threads: " << ThreadsN << ", Mode: " << type_name<ModeT>() << "] " << "[Threads: " << ThreadsN << ", Mode: " << type_name<ModeT>() << "] "
<< type_name<AllocT>() << std::endl; << type_name<AllocT>() << std::endl;
std::vector<void*> ptrs[ThreadsN]; std::vector<void*> ptrs[ThreadsN];
for (auto& vec : ptrs) { for (auto& vec : ptrs) {
vec.resize(LoopCount); vec.resize(LoopCount);
} }
ModeT mode; ModeT mode;
std::atomic_int fini { 0 }; std::atomic_int fini { 0 };
test_stopwatch sw; test_stopwatch sw;
std::thread works[ThreadsN]; std::thread works[ThreadsN];
int pid = 0; int pid = 0;
for (auto& w : works) { for (auto& w : works) {
w = std::thread {[&, pid] { w = std::thread {[&, pid] {
sw.start(); sw.start();
for (std::size_t x = 0; x < 2; ++x) { for (std::size_t x = 0; x < 2; ++x) {
for(std::size_t n = 0; n < LoopCount; ++n) { for(std::size_t n = 0; n < LoopCount; ++n) {
int m = mode.ix_[x][n]; int m = mode.ix_[x][n];
void*& p = ptrs[pid][static_cast<std::size_t>(m)]; void*& p = ptrs[pid][static_cast<std::size_t>(m)];
std::size_t s = sizes__[static_cast<std::size_t>(m)]; std::size_t s = sizes__[static_cast<std::size_t>(m)];
if (p == nullptr) { if (p == nullptr) {
p = AllocT::alloc(s); p = AllocT::alloc(s);
} }
else { else {
AllocT::free(p, s); AllocT::free(p, s);
p = nullptr; p = nullptr;
} }
} }
} }
if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == ThreadsN) { if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == ThreadsN) {
sw.print_elapsed<1>(DataMin, DataMax, LoopCount * ThreadsN); sw.print_elapsed<1>(DataMin, DataMax, LoopCount * ThreadsN);
} }
}}; }};
++pid; ++pid;
} }
sw.start(); sw.start();
for (auto& w : works) w.join(); for (auto& w : works) w.join();
} }
template <typename AllocT, typename ModeT, int ThreadsN> template <typename AllocT, typename ModeT, int ThreadsN>
struct test_performance { struct test_performance {
static void start() { static void start() {
test_performance<AllocT, ModeT, ThreadsN - 1>::start(); test_performance<AllocT, ModeT, ThreadsN - 1>::start();
benchmark_alloc<AllocT, ModeT, ThreadsN>(); benchmark_alloc<AllocT, ModeT, ThreadsN>();
} }
}; };
template <typename AllocT, typename ModeT> template <typename AllocT, typename ModeT>
struct test_performance<AllocT, ModeT, 1> { struct test_performance<AllocT, ModeT, 1> {
static void start() { static void start() {
benchmark_alloc<AllocT, ModeT, 1>(); benchmark_alloc<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() {
test_performance<ipc::mem::pool_alloc, alloc_FIFO , 8>::start(); // test_performance<ipc::mem::pool_alloc, alloc_FIFO , 8>::start();
test_performance<ipc::mem::pool_alloc, alloc_LIFO , 8>::start(); // for (;;) {
test_performance<ipc::mem::pool_alloc, alloc_random, 8>::start(); test_performance<ipc::mem::pool_alloc, alloc_FIFO , 8>::start();
} test_performance<ipc::mem::pool_alloc, alloc_LIFO , 8>::start();
test_performance<ipc::mem::pool_alloc, alloc_random, 8>::start();
} // internal-linkage // }
}
} // internal-linkage

View File

@ -1,114 +1,114 @@
#include <cstring> #include <cstring>
#include <cstdint> #include <cstdint>
#include <thread> #include <thread>
#include "shm.h" #include "shm.h"
#include "test.h" #include "test.h"
using namespace ipc::shm; using namespace ipc::shm;
namespace { namespace {
class Unit : public TestSuite { class Unit : public TestSuite {
Q_OBJECT Q_OBJECT
const char* name() const { const char* name() const {
return "test_shm"; return "test_shm";
} }
private slots: private slots:
void cleanupTestCase(); void cleanupTestCase();
void test_acquire(); void test_acquire();
void test_release(); void test_release();
void test_get(); void test_get();
void test_hello(); void test_hello();
void test_mt(); void test_mt();
} unit__; } unit__;
#include "test_shm.moc" #include "test_shm.moc"
handle shm_hd__; handle shm_hd__;
void Unit::cleanupTestCase() { void Unit::cleanupTestCase() {
shm_hd__.release(); shm_hd__.release();
} }
void Unit::test_acquire() { void Unit::test_acquire() {
QVERIFY(!shm_hd__.valid()); QVERIFY(!shm_hd__.valid());
QVERIFY(shm_hd__.acquire("my-test-1", 1024)); QVERIFY(shm_hd__.acquire("my-test-1", 1024));
QVERIFY(shm_hd__.valid()); QVERIFY(shm_hd__.valid());
QCOMPARE(shm_hd__.name(), "my-test-1"); QCOMPARE(shm_hd__.name(), "my-test-1");
QVERIFY(shm_hd__.acquire("my-test-2", 2048)); QVERIFY(shm_hd__.acquire("my-test-2", 2048));
QVERIFY(shm_hd__.valid()); QVERIFY(shm_hd__.valid());
QCOMPARE(shm_hd__.name(), "my-test-2"); QCOMPARE(shm_hd__.name(), "my-test-2");
QVERIFY(shm_hd__.acquire("my-test-3", 4096)); QVERIFY(shm_hd__.acquire("my-test-3", 4096));
QVERIFY(shm_hd__.valid()); QVERIFY(shm_hd__.valid());
QCOMPARE(shm_hd__.name(), "my-test-3"); QCOMPARE(shm_hd__.name(), "my-test-3");
} }
void Unit::test_release() { void Unit::test_release() {
QVERIFY(shm_hd__.valid()); QVERIFY(shm_hd__.valid());
shm_hd__.release(); shm_hd__.release();
QVERIFY(!shm_hd__.valid()); QVERIFY(!shm_hd__.valid());
} }
void Unit::test_get() { void Unit::test_get() {
QVERIFY(shm_hd__.get() == nullptr); QVERIFY(shm_hd__.get() == nullptr);
QVERIFY(shm_hd__.acquire("my-test", 1024)); QVERIFY(shm_hd__.acquire("my-test", 1024));
auto mem = shm_hd__.get(); auto mem = shm_hd__.get();
QVERIFY(mem != nullptr); QVERIFY(mem != nullptr);
QVERIFY(mem == shm_hd__.get()); QVERIFY(mem == shm_hd__.get());
std::uint8_t buf[1024] = {}; std::uint8_t buf[1024] = {};
QVERIFY(memcmp(mem, buf, sizeof(buf)) == 0); QVERIFY(memcmp(mem, buf, sizeof(buf)) == 0);
handle shm_other(shm_hd__.name(), shm_hd__.size()); handle shm_other(shm_hd__.name(), shm_hd__.size());
QVERIFY(shm_other.get() != shm_hd__.get()); QVERIFY(shm_other.get() != shm_hd__.get());
} }
void Unit::test_hello() { void Unit::test_hello() {
auto mem = shm_hd__.get(); auto mem = shm_hd__.get();
QVERIFY(mem != nullptr); QVERIFY(mem != nullptr);
constexpr char hello[] = "hello!"; constexpr char hello[] = "hello!";
std::memcpy(mem, hello, sizeof(hello)); std::memcpy(mem, hello, sizeof(hello));
QCOMPARE((char*)shm_hd__.get(), hello); QCOMPARE((char*)shm_hd__.get(), hello);
shm_hd__.release(); shm_hd__.release();
QVERIFY(shm_hd__.get() == nullptr); QVERIFY(shm_hd__.get() == nullptr);
QVERIFY(shm_hd__.acquire("my-test", 1024)); QVERIFY(shm_hd__.acquire("my-test", 1024));
mem = shm_hd__.get(); mem = shm_hd__.get();
QVERIFY(mem != nullptr); QVERIFY(mem != nullptr);
std::uint8_t buf[1024] = {}; std::uint8_t buf[1024] = {};
QVERIFY(memcmp(mem, buf, sizeof(buf)) == 0); QVERIFY(memcmp(mem, buf, sizeof(buf)) == 0);
std::memcpy(mem, hello, sizeof(hello)); std::memcpy(mem, hello, sizeof(hello));
QCOMPARE((char*)shm_hd__.get(), hello); QCOMPARE((char*)shm_hd__.get(), hello);
} }
void Unit::test_mt() { void Unit::test_mt() {
std::thread { std::thread {
[] { [] {
handle shm_mt(shm_hd__.name(), shm_hd__.size()); handle shm_mt(shm_hd__.name(), shm_hd__.size());
shm_hd__.release(); shm_hd__.release();
constexpr char hello[] = "hello!"; constexpr char hello[] = "hello!";
QCOMPARE((char*)shm_mt.get(), hello); QCOMPARE((char*)shm_mt.get(), hello);
} }
}.join(); }.join();
QVERIFY(shm_hd__.get() == nullptr); QVERIFY(shm_hd__.get() == nullptr);
QVERIFY(!shm_hd__.valid()); QVERIFY(!shm_hd__.valid());
QVERIFY(shm_hd__.acquire("my-test", 1024)); QVERIFY(shm_hd__.acquire("my-test", 1024));
std::uint8_t buf[1024] = {}; std::uint8_t buf[1024] = {};
QVERIFY(memcmp(shm_hd__.get(), buf, sizeof(buf)) == 0); QVERIFY(memcmp(shm_hd__.get(), buf, sizeof(buf)) == 0);
} }
} // internal-linkage } // internal-linkage

View File

@ -1,46 +1,46 @@
#include <thread> #include <thread>
#include <iostream> #include <iostream>
#include "platform/waiter_wrapper.h" #include "platform/waiter_wrapper.h"
#include "test.h" #include "test.h"
namespace { namespace {
class Unit : public TestSuite { class Unit : public TestSuite {
Q_OBJECT Q_OBJECT
const char* name() const { const char* name() const {
return "test_waiter"; return "test_waiter";
} }
private slots: private slots:
void test_broadcast(); void test_broadcast();
} unit__; } unit__;
#include "test_waiter.moc" #include "test_waiter.moc"
void Unit::test_broadcast() { void Unit::test_broadcast() {
ipc::detail::waiter w; ipc::detail::waiter w;
std::thread ts[10]; std::thread ts[10];
for (auto& t : ts) { for (auto& t : ts) {
t = std::thread([&w] { t = std::thread([&w] {
ipc::detail::waiter_wrapper wp { &w }; ipc::detail::waiter_wrapper wp { &w };
QVERIFY(wp.open("test-ipc-waiter")); QVERIFY(wp.open("test-ipc-waiter"));
QVERIFY(wp.wait_if([] { return true; })); QVERIFY(wp.wait_if([] { return true; }));
wp.close(); wp.close();
}); });
} }
ipc::detail::waiter_wrapper wp { &w }; ipc::detail::waiter_wrapper wp { &w };
QVERIFY(wp.open("test-ipc-waiter")); QVERIFY(wp.open("test-ipc-waiter"));
std::cout << "waiting for broadcast...\n"; std::cout << "waiting for broadcast...\n";
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
QVERIFY(wp.broadcast()); QVERIFY(wp.broadcast());
for (auto& t : ts) t.join(); for (auto& t : ts) t.join();
wp.close(); wp.close();
} }
} // internal-linkage } // internal-linkage