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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,183 +1,230 @@
#include "tls_pointer.h"
#include <Windows.h> // ::Tls...
#include <unordered_map> // std::unordered_map
namespace ipc {
/*
* <Remarks>
*
* Windows doesn't support a per-thread destructor with its TLS primitives.
* So, here will build it manually by inserting a function to be called on each thread's exit.
*
* <Reference>
* - 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
* - 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 {
using destructor_t = void(*)(void*);
using map_t = std::unordered_map<tls::key_t, tls_data>;
static DWORD& key() {
static DWORD rec_key = ::TlsAlloc();
return rec_key;
}
static map_t* records(map_t* rec) {
::TlsSetValue(key(), static_cast<LPVOID>(rec));
return rec;
}
static map_t* records() {
return static_cast<map_t*>(::TlsGetValue(key()));
}
tls::key_t key_ = tls::invalid_value;
destructor_t destructor_ = nullptr;
tls_data() = default;
tls_data(tls::key_t key, destructor_t destructor)
: key_ (key)
, destructor_(destructor)
{}
tls_data(tls_data&& rhs) : tls_data() {
(*this) = std::move(rhs);
}
tls_data& operator=(tls_data&& rhs) {
key_ = rhs.key_;
destructor_ = rhs.destructor_;
rhs.key_ = 0;
rhs.destructor_ = nullptr;
return *this;
}
~tls_data() {
if (destructor_) destructor_(tls::get(key_));
}
};
} // internal-linkage
namespace tls {
key_t create(destructor_t destructor) {
key_t key = static_cast<key_t>(::TlsAlloc());
if (key == TLS_OUT_OF_INDEXES) return invalid_value;
auto rec = tls_data::records();
if (rec == nullptr) rec = tls_data::records(new tls_data::map_t);
if (rec == nullptr) return key;
rec->emplace(key, tls_data{ key, destructor });
return key;
}
void release(key_t key) {
auto rec = tls_data::records();
if (rec == nullptr) return;
rec->erase(key);
::TlsFree(static_cast<DWORD>(key));
}
bool set(key_t key, void* ptr) {
return ::TlsSetValue(static_cast<DWORD>(key),
static_cast<LPVOID>(ptr)) == TRUE;
}
void* get(key_t key) {
return static_cast<void*>(::TlsGetValue(static_cast<DWORD>(key)));
}
} // namespace tls
namespace {
void OnThreadExit() {
auto rec = tls_data::records();
if (rec == nullptr) return;
delete rec;
tls_data::records(nullptr);
}
void NTAPI OnTlsCallback(PVOID, DWORD dwReason, PVOID) {
if (dwReason == DLL_THREAD_DETACH) OnThreadExit();
}
} // internal-linkage
////////////////////////////////////////////////////////////////
/// Call destructors on thread exit
////////////////////////////////////////////////////////////////
#if defined(_MSC_VER)
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
#pragma comment(linker, "/INCLUDE:_tls_used")
#pragma comment(linker, "/INCLUDE:_tls_xl_b__")
extern "C" {
# pragma const_seg(".CRT$XLB")
extern const PIMAGE_TLS_CALLBACK _tls_xl_b__;
const PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
# pragma const_seg()
}
#else /*!WIN64*/
#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
#include "tls_pointer.h"
#include "log.h"
#include <Windows.h> // ::Tls...
#include <atomic>
#include <unordered_set> // std::unordered_set
namespace ipc {
/*
* <Remarks>
*
* Windows doesn't support a per-thread destructor with its TLS primitives.
* So, here will build it manually by inserting a function to be called on each thread's exit.
*
* <Reference>
* - 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
* - 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 {
using destructor_t = void(*)(void*);
DWORD win_key_;
destructor_t destructor_;
void destruct(void* data) {
if ((destructor_ != nullptr) && (data != nullptr)) {
destructor_(data);
}
}
};
using rec_t = std::unordered_set<tls_data*>;
DWORD& record_key() {
struct key_gen {
DWORD rec_key_;
key_gen() : rec_key_(::TlsAlloc()) {
if (rec_key_ == TLS_OUT_OF_INDEXES) {
ipc::error("[record_key] TlsAlloc failed[%lu].\n", ::GetLastError());
}
}
~key_gen() { ::TlsFree(rec_key_); }
};
static key_gen gen;
return gen.rec_key_;
}
bool record(tls_data* tls) {
auto rec = static_cast<rec_t*>(::TlsGetValue(record_key()));
if (rec == nullptr) {
if (FALSE == ::TlsSetValue(record_key(), static_cast<LPVOID>(rec = new rec_t))) {
ipc::error("[record] TlsSetValue failed[%lu].\n", ::GetLastError());
return false;
}
}
rec->insert(tls);
return true;
}
static void erase_record(tls_data* tls) {
auto rec = static_cast<rec_t*>(::TlsGetValue(record_key()));
if (rec == nullptr) return;
rec->erase(tls);
}
static void clear_all_records() {
auto rec = static_cast<rec_t*>(::TlsGetValue(record_key()));
if (rec == nullptr) return;
for (auto tls : *rec) {
if (tls != nullptr) {
tls->destruct(::TlsGetValue(tls->win_key_));
}
}
delete rec;
::TlsSetValue(record_key(), static_cast<LPVOID>(nullptr));
}
} // internal-linkage
namespace tls {
key_t create(destructor_t destructor) {
record_key(); // gen record-key
auto tls_dat = new tls_data { ::TlsAlloc(), destructor };
std::atomic_thread_fence(std::memory_order_seq_cst);
if (tls_dat->win_key_ == TLS_OUT_OF_INDEXES) {
ipc::error("[tls::create] TlsAlloc failed[%lu].\n", ::GetLastError());
delete tls_dat;
return invalid_value;
}
return reinterpret_cast<key_t>(tls_dat);
}
void release(key_t tls_key) {
if (tls_key == invalid_value) {
ipc::error("[tls::release] tls_key is invalid_value.\n");
return;
}
auto tls_dat = reinterpret_cast<tls_data*>(tls_key);
if (tls_dat == nullptr) {
ipc::error("[tls::release] tls_dat is nullptr.\n");
return;
}
erase_record(tls_dat);
::TlsFree(tls_dat->win_key_);
delete tls_dat;
}
bool set(key_t tls_key, void* ptr) {
if (tls_key == invalid_value) {
ipc::error("[tls::set] tls_key is invalid_value.\n");
return false;
}
auto tls_dat = reinterpret_cast<tls_data*>(tls_key);
if (tls_dat == nullptr) {
ipc::error("[tls::set] tls_dat is nullptr.\n");
return false;
}
if (FALSE == ::TlsSetValue(tls_dat->win_key_, static_cast<LPVOID>(ptr))) {
ipc::error("[tls::set] TlsSetValue failed[%lu].\n", ::GetLastError());
return false;
}
record(tls_dat);
return true;
}
void* get(key_t tls_key) {
if (tls_key == invalid_value) {
ipc::error("[tls::get] tls_key is invalid_value.\n");
return nullptr;
}
auto tls_dat = reinterpret_cast<tls_data*>(tls_key);
if (tls_dat == nullptr) {
ipc::error("[tls::get] tls_dat is nullptr.\n");
return nullptr;
}
return ::TlsGetValue(tls_dat->win_key_);
}
} // namespace tls
namespace {
void OnThreadExit() {
clear_all_records();
}
void NTAPI OnTlsCallback(PVOID, DWORD dwReason, PVOID) {
if (dwReason == DLL_THREAD_DETACH) OnThreadExit();
}
} // internal-linkage
////////////////////////////////////////////////////////////////
/// Call destructors on thread exit
////////////////////////////////////////////////////////////////
#if defined(_MSC_VER)
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
#pragma comment(linker, "/INCLUDE:_tls_used")
#pragma comment(linker, "/INCLUDE:_tls_xl_b__")
extern "C" {
# pragma const_seg(".CRT$XLB")
extern const PIMAGE_TLS_CALLBACK _tls_xl_b__;
const PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
# pragma const_seg()
}
#else /*!WIN64*/
#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
#include <tchar.h>
#include <type_traits>
#include <string>
#include <locale>
#include <codecvt>
#include <cstring>
#include "concept.h"
#include "platform/detail.h"
#include "memory/resource.h"
namespace ipc::detail {
struct has_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, typename U, typename = decltype(has_value_type_::check<U>(nullptr))>
struct is_same_char : std::is_same<T, U> {};
template <typename T, typename U>
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>
using IsSameChar = ipc::require<is_same_char<T, S>::value, R>;
////////////////////////////////////////////////////////////////
/// to_tchar implementation
////////////////////////////////////////////////////////////////
template <typename T = TCHAR>
constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::string, ipc::string &&> {
return std::move(str);
}
template <typename T = TCHAR>
constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::wstring> {
return std::wstring_convert<
std::codecvt_utf8_utf16<wchar_t>,
wchar_t,
ipc::mem::allocator<wchar_t>,
ipc::mem::allocator<char>
>{}.from_bytes(std::move(str));
}
template <typename T>
inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, char, void> {
std::memcpy(dst, src, size);
}
template <typename T>
inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, wchar_t, void> {
auto wstr = std::wstring_convert<
std::codecvt_utf8_utf16<wchar_t>,
wchar_t,
ipc::mem::allocator<wchar_t>,
ipc::mem::allocator<char>
>{}.from_bytes(src, src + size);
std::memcpy(dst, wstr.data(), (ipc::detail::min)(wstr.size(), size));
}
} // namespace ipc::detail
#pragma once
#include <tchar.h>
#include <type_traits>
#include <string>
#include <locale>
#include <codecvt>
#include <cstring>
#include "concept.h"
#include "platform/detail.h"
#include "memory/resource.h"
namespace ipc::detail {
struct has_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, typename U, typename = decltype(has_value_type_::check<U>(nullptr))>
struct is_same_char : std::is_same<T, U> {};
template <typename T, typename U>
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>
using IsSameChar = ipc::require<is_same_char<T, S>::value, R>;
////////////////////////////////////////////////////////////////
/// to_tchar implementation
////////////////////////////////////////////////////////////////
template <typename T = TCHAR>
constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::string, ipc::string &&> {
return std::move(str);
}
template <typename T = TCHAR>
constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::wstring> {
return std::wstring_convert<
std::codecvt_utf8_utf16<wchar_t>,
wchar_t,
ipc::mem::allocator<wchar_t>,
ipc::mem::allocator<char>
>{}.from_bytes(std::move(str));
}
template <typename T>
inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, char, void> {
std::memcpy(dst, src, size);
}
template <typename T>
inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, wchar_t, void> {
auto wstr = std::wstring_convert<
std::codecvt_utf8_utf16<wchar_t>,
wchar_t,
ipc::mem::allocator<wchar_t>,
ipc::mem::allocator<char>
>{}.from_bytes(src, src + size);
std::memcpy(dst, wstr.data(), (ipc::detail::min)(wstr.size(), size));
}
} // namespace ipc::detail

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View File

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

View File

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