mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2026-02-14 06:09:49 +08:00
fix tls bugs (win); modify data structure
This commit is contained in:
parent
9bc6604faa
commit
6b7c561496
104
include/export.h
104
include/export.h
@ -1,52 +1,52 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
#if defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
||||||
|
|
||||||
# define IPC_DECL_EXPORT Q_DECL_EXPORT
|
# define IPC_DECL_EXPORT Q_DECL_EXPORT
|
||||||
# define IPC_DECL_IMPORT Q_DECL_IMPORT
|
# define IPC_DECL_IMPORT Q_DECL_IMPORT
|
||||||
|
|
||||||
#else // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
#else // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compiler & system detection for IPC_DECL_EXPORT & IPC_DECL_IMPORT.
|
* Compiler & system detection for IPC_DECL_EXPORT & IPC_DECL_IMPORT.
|
||||||
* Not using QtCore cause it shouldn't depend on Qt.
|
* Not using QtCore cause it shouldn't depend on Qt.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
# define IPC_DECL_EXPORT __declspec(dllexport)
|
# define IPC_DECL_EXPORT __declspec(dllexport)
|
||||||
# define IPC_DECL_IMPORT __declspec(dllimport)
|
# define IPC_DECL_IMPORT __declspec(dllimport)
|
||||||
#elif defined(__ARMCC__) || defined(__CC_ARM)
|
#elif defined(__ARMCC__) || defined(__CC_ARM)
|
||||||
# if defined(ANDROID) || defined(__linux__) || defined(__linux)
|
# if defined(ANDROID) || defined(__linux__) || defined(__linux)
|
||||||
# define IPC_DECL_EXPORT __attribute__((visibility("default")))
|
# define IPC_DECL_EXPORT __attribute__((visibility("default")))
|
||||||
# define IPC_DECL_IMPORT __attribute__((visibility("default")))
|
# define IPC_DECL_IMPORT __attribute__((visibility("default")))
|
||||||
# else
|
# else
|
||||||
# define IPC_DECL_EXPORT __declspec(dllexport)
|
# define IPC_DECL_EXPORT __declspec(dllexport)
|
||||||
# define IPC_DECL_IMPORT __declspec(dllimport)
|
# define IPC_DECL_IMPORT __declspec(dllimport)
|
||||||
# endif
|
# endif
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
|
# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
|
||||||
defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
|
defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
|
||||||
# define IPC_DECL_EXPORT __declspec(dllexport)
|
# define IPC_DECL_EXPORT __declspec(dllexport)
|
||||||
# define IPC_DECL_IMPORT __declspec(dllimport)
|
# define IPC_DECL_IMPORT __declspec(dllimport)
|
||||||
# else
|
# else
|
||||||
# define IPC_DECL_EXPORT __attribute__((visibility("default")))
|
# define IPC_DECL_EXPORT __attribute__((visibility("default")))
|
||||||
# define IPC_DECL_IMPORT __attribute__((visibility("default")))
|
# define IPC_DECL_IMPORT __attribute__((visibility("default")))
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
# define IPC_DECL_EXPORT __attribute__((visibility("default")))
|
# define IPC_DECL_EXPORT __attribute__((visibility("default")))
|
||||||
# define IPC_DECL_IMPORT __attribute__((visibility("default")))
|
# define IPC_DECL_IMPORT __attribute__((visibility("default")))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
#endif // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define IPC_EXPORT for exporting function & class.
|
* Define IPC_EXPORT for exporting function & class.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef IPC_EXPORT
|
#ifndef IPC_EXPORT
|
||||||
#if defined(__IPC_LIBRARY__)
|
#if defined(__IPC_LIBRARY__)
|
||||||
# define IPC_EXPORT IPC_DECL_EXPORT
|
# define IPC_EXPORT IPC_DECL_EXPORT
|
||||||
#else
|
#else
|
||||||
# define IPC_EXPORT IPC_DECL_IMPORT
|
# define IPC_EXPORT IPC_DECL_IMPORT
|
||||||
#endif
|
#endif
|
||||||
#endif /*IPC_EXPORT*/
|
#endif /*IPC_EXPORT*/
|
||||||
|
|||||||
302
include/ipc.h
302
include/ipc.h
@ -1,151 +1,151 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "export.h"
|
#include "export.h"
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
using handle_t = void*;
|
using handle_t = void*;
|
||||||
using buff_t = buffer;
|
using buff_t = buffer;
|
||||||
|
|
||||||
enum : unsigned {
|
enum : unsigned {
|
||||||
sender,
|
sender,
|
||||||
receiver
|
receiver
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
struct IPC_EXPORT chan_impl {
|
struct IPC_EXPORT chan_impl {
|
||||||
static handle_t connect (char const * name, unsigned mode);
|
static handle_t connect (char const * name, unsigned mode);
|
||||||
static void disconnect(handle_t h);
|
static void disconnect(handle_t h);
|
||||||
|
|
||||||
static char const * name(handle_t h);
|
static char const * name(handle_t h);
|
||||||
|
|
||||||
static std::size_t recv_count(handle_t h);
|
static std::size_t recv_count(handle_t h);
|
||||||
static bool wait_for_recv(handle_t h, std::size_t r_count, std::size_t tm);
|
static bool wait_for_recv(handle_t h, std::size_t r_count, std::size_t tm);
|
||||||
|
|
||||||
static bool send(handle_t h, void const * data, std::size_t size);
|
static bool send(handle_t h, void const * data, std::size_t size);
|
||||||
static buff_t recv(handle_t h, std::size_t tm);
|
static buff_t recv(handle_t h, std::size_t tm);
|
||||||
|
|
||||||
static bool try_send(handle_t h, void const * data, std::size_t size);
|
static bool try_send(handle_t h, void const * data, std::size_t size);
|
||||||
static buff_t try_recv(handle_t h);
|
static buff_t try_recv(handle_t h);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
class chan_wrapper {
|
class chan_wrapper {
|
||||||
private:
|
private:
|
||||||
using detail_t = chan_impl<Flag>;
|
using detail_t = chan_impl<Flag>;
|
||||||
handle_t h_ = nullptr;
|
handle_t h_ = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
chan_wrapper() = default;
|
chan_wrapper() = default;
|
||||||
|
|
||||||
explicit chan_wrapper(char const * name, unsigned mode = sender) {
|
explicit chan_wrapper(char const * name, unsigned mode = sender) {
|
||||||
this->connect(name, mode);
|
this->connect(name, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
chan_wrapper(chan_wrapper&& rhs) {
|
chan_wrapper(chan_wrapper&& rhs) {
|
||||||
swap(rhs);
|
swap(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
~chan_wrapper() {
|
~chan_wrapper() {
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap(chan_wrapper& rhs) {
|
void swap(chan_wrapper& rhs) {
|
||||||
std::swap(h_, rhs.h_);
|
std::swap(h_, rhs.h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
chan_wrapper& operator=(chan_wrapper rhs) {
|
chan_wrapper& operator=(chan_wrapper rhs) {
|
||||||
swap(rhs);
|
swap(rhs);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
char const * name() const {
|
char const * name() const {
|
||||||
return detail_t::name(h_);
|
return detail_t::name(h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_t handle() const {
|
handle_t handle() const {
|
||||||
return h_;
|
return h_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool valid() const {
|
bool valid() const {
|
||||||
return (handle() != nullptr);
|
return (handle() != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
chan_wrapper clone() const {
|
chan_wrapper clone() const {
|
||||||
return chan_wrapper { name() };
|
return chan_wrapper { name() };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool connect(char const * name, unsigned mode = sender | receiver) {
|
bool connect(char const * name, unsigned mode = sender | receiver) {
|
||||||
if (name == nullptr || name[0] == '\0') return false;
|
if (name == nullptr || name[0] == '\0') return false;
|
||||||
this->disconnect();
|
this->disconnect();
|
||||||
h_ = detail_t::connect(name, mode);
|
h_ = detail_t::connect(name, mode);
|
||||||
return valid();
|
return valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnect() {
|
void disconnect() {
|
||||||
if (!valid()) return;
|
if (!valid()) return;
|
||||||
detail_t::disconnect(h_);
|
detail_t::disconnect(h_);
|
||||||
h_ = nullptr;
|
h_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t recv_count() const {
|
std::size_t recv_count() const {
|
||||||
return detail_t::recv_count(h_);
|
return detail_t::recv_count(h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wait_for_recv(std::size_t r_count, std::size_t tm = invalid_value) const {
|
bool wait_for_recv(std::size_t r_count, std::size_t tm = invalid_value) const {
|
||||||
return detail_t::wait_for_recv(h_, r_count, tm);
|
return detail_t::wait_for_recv(h_, r_count, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool wait_for_recv(char const * name, std::size_t r_count, std::size_t tm = invalid_value) {
|
static bool wait_for_recv(char const * name, std::size_t r_count, std::size_t tm = invalid_value) {
|
||||||
return chan_wrapper(name).wait_for_recv(r_count, tm);
|
return chan_wrapper(name).wait_for_recv(r_count, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool send (void const * data, std::size_t size) { return detail_t::send(h_, data, size) ; }
|
bool send (void const * data, std::size_t size) { return detail_t::send(h_, data, size) ; }
|
||||||
bool send (buff_t const & buff) { return this->send(buff.data(), buff.size()) ; }
|
bool send (buff_t const & buff) { return this->send(buff.data(), buff.size()) ; }
|
||||||
bool send (std::string const & str) { return this->send(str.c_str(), str.size() + 1); }
|
bool send (std::string const & str) { return this->send(str.c_str(), str.size() + 1); }
|
||||||
|
|
||||||
bool try_send(void const * data, std::size_t size) { return detail_t::try_send(h_, data, size) ; }
|
bool try_send(void const * data, std::size_t size) { return detail_t::try_send(h_, data, size) ; }
|
||||||
bool try_send(buff_t const & buff) { return this->try_send(buff.data(), buff.size()) ; }
|
bool try_send(buff_t const & buff) { return this->try_send(buff.data(), buff.size()) ; }
|
||||||
bool try_send(std::string const & str) { return this->try_send(str.c_str(), str.size() + 1); }
|
bool try_send(std::string const & str) { return this->try_send(str.c_str(), str.size() + 1); }
|
||||||
|
|
||||||
buff_t recv(std::size_t tm = invalid_value) {
|
buff_t recv(std::size_t tm = invalid_value) {
|
||||||
return detail_t::recv(h_, tm);
|
return detail_t::recv(h_, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
buff_t try_recv() {
|
buff_t try_recv() {
|
||||||
return detail_t::try_recv(h_);
|
return detail_t::try_recv(h_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
using chan = chan_wrapper<Flag>;
|
using chan = chan_wrapper<Flag>;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* class route
|
* class route
|
||||||
*
|
*
|
||||||
* You could use one producer/server/sender for sending messages to a route,
|
* You could use one producer/server/sender for sending messages to a route,
|
||||||
* then all the consumers/clients/receivers which are receiving with this route,
|
* then all the consumers/clients/receivers which are receiving with this route,
|
||||||
* would receive your sent messages.
|
* would receive your sent messages.
|
||||||
*
|
*
|
||||||
* A route could only be used in 1 to N
|
* A route could only be used in 1 to N
|
||||||
* (one producer/writer to multi consumers/readers)
|
* (one producer/writer to multi consumers/readers)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using route = chan<ipc::wr<relat::single, relat::multi, trans::broadcast>>;
|
using route = chan<ipc::wr<relat::single, relat::multi, trans::broadcast>>;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* class channel
|
* class channel
|
||||||
*
|
*
|
||||||
* You could use multi producers/writers for sending messages to a channel,
|
* You could use multi producers/writers for sending messages to a channel,
|
||||||
* then all the consumers/readers which are receiving with this channel,
|
* then all the consumers/readers which are receiving with this channel,
|
||||||
* would receive your sent messages.
|
* would receive your sent messages.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using channel = chan<ipc::wr<relat::multi, relat::multi, trans::broadcast>>;
|
using channel = chan<ipc::wr<relat::multi, relat::multi, trans::broadcast>>;
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,103 +1,103 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "export.h"
|
#include "export.h"
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace mem {
|
namespace mem {
|
||||||
|
|
||||||
class IPC_EXPORT pool_alloc {
|
class IPC_EXPORT pool_alloc {
|
||||||
public:
|
public:
|
||||||
static void clear();
|
static void clear();
|
||||||
static void* alloc(std::size_t size);
|
static void* alloc(std::size_t size);
|
||||||
static void free(void* p, std::size_t size);
|
static void free(void* p, std::size_t size);
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// construct/destruct an object
|
/// construct/destruct an object
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct impl {
|
struct impl {
|
||||||
template <typename... P>
|
template <typename... P>
|
||||||
static T* construct(T* p, P&&... params) {
|
static T* construct(T* p, P&&... params) {
|
||||||
::new (p) T(std::forward<P>(params)...);
|
::new (p) T(std::forward<P>(params)...);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destruct(T* p) {
|
static void destruct(T* p) {
|
||||||
reinterpret_cast<T*>(p)->~T();
|
reinterpret_cast<T*>(p)->~T();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, size_t N>
|
template <typename T, size_t N>
|
||||||
struct impl<T[N]> {
|
struct impl<T[N]> {
|
||||||
using type = T[N];
|
using type = T[N];
|
||||||
|
|
||||||
template <typename... P>
|
template <typename... P>
|
||||||
static type* construct(type* p, P&&... params) {
|
static type* construct(type* p, P&&... params) {
|
||||||
for (size_t i = 0; i < N; ++i) {
|
for (size_t i = 0; i < N; ++i) {
|
||||||
impl<T>::construct(&((*p)[i]), std::forward<P>(params)...);
|
impl<T>::construct(&((*p)[i]), std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destruct(type* p) {
|
static void destruct(type* p) {
|
||||||
for (size_t i = 0; i < N; ++i) {
|
for (size_t i = 0; i < N; ++i) {
|
||||||
impl<T>::destruct(&((*p)[i]));
|
impl<T>::destruct(&((*p)[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename T, typename... P>
|
template <typename T, typename... P>
|
||||||
T* construct(T* p, P&&... params) {
|
T* construct(T* p, P&&... params) {
|
||||||
return detail::impl<T>::construct(p, std::forward<P>(params)...);
|
return detail::impl<T>::construct(p, std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... P>
|
template <typename T, typename... P>
|
||||||
T* construct(void* p, P&&... params) {
|
T* construct(void* p, P&&... params) {
|
||||||
return construct(static_cast<T*>(p), std::forward<P>(params)...);
|
return construct(static_cast<T*>(p), std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void destruct(T* p) {
|
void destruct(T* p) {
|
||||||
return detail::impl<T>::destruct(p);
|
return detail::impl<T>::destruct(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void destruct(void* p) {
|
void destruct(void* p) {
|
||||||
destruct(static_cast<T*>(p));
|
destruct(static_cast<T*>(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// general alloc/free
|
/// general alloc/free
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
inline void* alloc(std::size_t size) {
|
inline void* alloc(std::size_t size) {
|
||||||
return pool_alloc::alloc(size);
|
return pool_alloc::alloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... P>
|
template <typename T, typename... P>
|
||||||
T* alloc(P&&... params) {
|
T* alloc(P&&... params) {
|
||||||
return construct<T>(pool_alloc::alloc(sizeof(T)), std::forward<P>(params)...);
|
return construct<T>(pool_alloc::alloc(sizeof(T)), std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void free(void* p, std::size_t size) {
|
inline void free(void* p, std::size_t size) {
|
||||||
pool_alloc::free(p, size);
|
pool_alloc::free(p, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void free(T* p) {
|
void free(T* p) {
|
||||||
destruct(p);
|
destruct(p);
|
||||||
pool_alloc::free(p, sizeof(T));
|
pool_alloc::free(p, sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mem
|
} // namespace mem
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,170 +1,170 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// Gives hint to processor that improves performance of spin-wait loops.
|
/// Gives hint to processor that improves performance of spin-wait loops.
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#pragma push_macro("IPC_LOCK_PAUSE_")
|
#pragma push_macro("IPC_LOCK_PAUSE_")
|
||||||
#undef IPC_LOCK_PAUSE_
|
#undef IPC_LOCK_PAUSE_
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
#include <windows.h> // YieldProcessor
|
#include <windows.h> // YieldProcessor
|
||||||
/*
|
/*
|
||||||
See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms687419(v=vs.85).aspx
|
See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms687419(v=vs.85).aspx
|
||||||
Not for intel c++ compiler, so ignore http://software.intel.com/en-us/forums/topic/296168
|
Not for intel c++ compiler, so ignore http://software.intel.com/en-us/forums/topic/296168
|
||||||
*/
|
*/
|
||||||
# define IPC_LOCK_PAUSE_() YieldProcessor()
|
# define IPC_LOCK_PAUSE_() YieldProcessor()
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
#if defined(__i386__) || defined(__x86_64__)
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
/*
|
/*
|
||||||
See: Intel(R) 64 and IA-32 Architectures Software Developer's Manual V2
|
See: Intel(R) 64 and IA-32 Architectures Software Developer's Manual V2
|
||||||
PAUSE-Spin Loop Hint, 4-57
|
PAUSE-Spin Loop Hint, 4-57
|
||||||
http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.html?wapkw=instruction+set+reference
|
http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.html?wapkw=instruction+set+reference
|
||||||
*/
|
*/
|
||||||
# define IPC_LOCK_PAUSE_() __asm__ __volatile__("pause")
|
# define IPC_LOCK_PAUSE_() __asm__ __volatile__("pause")
|
||||||
#elif defined(__ia64__) || defined(__ia64)
|
#elif defined(__ia64__) || defined(__ia64)
|
||||||
/*
|
/*
|
||||||
See: Intel(R) Itanium(R) Architecture Developer's Manual, Vol.3
|
See: Intel(R) Itanium(R) Architecture Developer's Manual, Vol.3
|
||||||
hint - Performance Hint, 3:145
|
hint - Performance Hint, 3:145
|
||||||
http://www.intel.com/content/www/us/en/processors/itanium/itanium-architecture-vol-3-manual.html
|
http://www.intel.com/content/www/us/en/processors/itanium/itanium-architecture-vol-3-manual.html
|
||||||
*/
|
*/
|
||||||
# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("hint @pause")
|
# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("hint @pause")
|
||||||
#elif defined(__arm__)
|
#elif defined(__arm__)
|
||||||
/*
|
/*
|
||||||
See: ARM Architecture Reference Manuals (YIELD)
|
See: ARM Architecture Reference Manuals (YIELD)
|
||||||
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.architecture.reference/index.html
|
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.architecture.reference/index.html
|
||||||
*/
|
*/
|
||||||
# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("yield")
|
# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("yield")
|
||||||
#endif
|
#endif
|
||||||
#endif/*compilers*/
|
#endif/*compilers*/
|
||||||
|
|
||||||
#if !defined(IPC_LOCK_PAUSE_)
|
#if !defined(IPC_LOCK_PAUSE_)
|
||||||
/*
|
/*
|
||||||
Just use a compiler fence, prevent compiler from optimizing loop
|
Just use a compiler fence, prevent compiler from optimizing loop
|
||||||
*/
|
*/
|
||||||
# define IPC_LOCK_PAUSE_() std::atomic_signal_fence(std::memory_order_seq_cst)
|
# define IPC_LOCK_PAUSE_() std::atomic_signal_fence(std::memory_order_seq_cst)
|
||||||
#endif/*!defined(IPC_LOCK_PAUSE_)*/
|
#endif/*!defined(IPC_LOCK_PAUSE_)*/
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// Yield to other threads
|
/// Yield to other threads
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
template <typename K>
|
template <typename K>
|
||||||
inline void yield(K& k) noexcept {
|
inline void yield(K& k) noexcept {
|
||||||
if (k < 4) { /* Do nothing */ }
|
if (k < 4) { /* Do nothing */ }
|
||||||
else
|
else
|
||||||
if (k < 16) { IPC_LOCK_PAUSE_(); }
|
if (k < 16) { IPC_LOCK_PAUSE_(); }
|
||||||
else {
|
else {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
++k;
|
++k;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t N = 4096, typename K, typename F>
|
template <std::size_t N = 4096, typename K, typename F>
|
||||||
inline void sleep(K& k, F&& f) {
|
inline void sleep(K& k, F&& f) {
|
||||||
if (k < static_cast<K>(N)) {
|
if (k < static_cast<K>(N)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
else if (std::forward<F>(f)()) {
|
else if (std::forward<F>(f)()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
++k;
|
++k;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t N = 4096, typename K>
|
template <std::size_t N = 4096, typename K>
|
||||||
inline void sleep(K& k) {
|
inline void sleep(K& k) {
|
||||||
sleep<N>(k, [] { return false; });
|
sleep<N>(k, [] { return false; });
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|
||||||
#pragma pop_macro("IPC_LOCK_PAUSE_")
|
#pragma pop_macro("IPC_LOCK_PAUSE_")
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
class spin_lock {
|
class spin_lock {
|
||||||
std::atomic<unsigned> lc_ { 0 };
|
std::atomic<unsigned> lc_ { 0 };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void lock(void) noexcept {
|
void lock(void) noexcept {
|
||||||
for (unsigned k = 0;
|
for (unsigned k = 0;
|
||||||
lc_.exchange(1, std::memory_order_acquire);
|
lc_.exchange(1, std::memory_order_acquire);
|
||||||
yield(k)) ;
|
yield(k)) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void unlock(void) noexcept {
|
void unlock(void) noexcept {
|
||||||
lc_.store(0, std::memory_order_release);
|
lc_.store(0, std::memory_order_release);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class rw_lock {
|
class rw_lock {
|
||||||
using lc_ui_t = unsigned;
|
using lc_ui_t = unsigned;
|
||||||
|
|
||||||
std::atomic<lc_ui_t> lc_ { 0 };
|
std::atomic<lc_ui_t> lc_ { 0 };
|
||||||
|
|
||||||
enum : lc_ui_t {
|
enum : lc_ui_t {
|
||||||
w_mask = (std::numeric_limits<std::make_signed_t<lc_ui_t>>::max)(), // b 0111 1111
|
w_mask = (std::numeric_limits<std::make_signed_t<lc_ui_t>>::max)(), // b 0111 1111
|
||||||
w_flag = w_mask + 1 // b 1000 0000
|
w_flag = w_mask + 1 // b 1000 0000
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
rw_lock() = default;
|
rw_lock() = default;
|
||||||
|
|
||||||
rw_lock(const rw_lock&) = delete;
|
rw_lock(const rw_lock&) = delete;
|
||||||
rw_lock& operator=(const rw_lock&) = delete;
|
rw_lock& operator=(const rw_lock&) = delete;
|
||||||
rw_lock(rw_lock&&) = delete;
|
rw_lock(rw_lock&&) = delete;
|
||||||
rw_lock& operator=(rw_lock&&) = delete;
|
rw_lock& operator=(rw_lock&&) = delete;
|
||||||
|
|
||||||
void lock() noexcept {
|
void lock() noexcept {
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
auto old = lc_.fetch_or(w_flag, std::memory_order_acq_rel);
|
auto old = lc_.fetch_or(w_flag, std::memory_order_acq_rel);
|
||||||
if (!old) return; // got w-lock
|
if (!old) return; // got w-lock
|
||||||
if (!(old & w_flag)) break; // other thread having r-lock
|
if (!(old & w_flag)) break; // other thread having r-lock
|
||||||
yield(k); // other thread having w-lock
|
yield(k); // other thread having w-lock
|
||||||
}
|
}
|
||||||
// wait for reading finished
|
// wait for reading finished
|
||||||
for (unsigned k = 0;
|
for (unsigned k = 0;
|
||||||
lc_.load(std::memory_order_acquire) & w_mask;
|
lc_.load(std::memory_order_acquire) & w_mask;
|
||||||
yield(k)) ;
|
yield(k)) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void unlock() noexcept {
|
void unlock() noexcept {
|
||||||
lc_.store(0, std::memory_order_release);
|
lc_.store(0, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lock_shared() noexcept {
|
void lock_shared() noexcept {
|
||||||
auto old = lc_.load(std::memory_order_acquire);
|
auto old = lc_.load(std::memory_order_acquire);
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
// if w_flag set, just continue
|
// if w_flag set, just continue
|
||||||
if (old & w_flag) {
|
if (old & w_flag) {
|
||||||
yield(k);
|
yield(k);
|
||||||
old = lc_.load(std::memory_order_acquire);
|
old = lc_.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
// otherwise try cas lc + 1 (set r-lock)
|
// otherwise try cas lc + 1 (set r-lock)
|
||||||
else if (lc_.compare_exchange_weak(old, old + 1, std::memory_order_release)) {
|
else if (lc_.compare_exchange_weak(old, old + 1, std::memory_order_release)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// set r-lock failed, old has been updated
|
// set r-lock failed, old has been updated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void unlock_shared() noexcept {
|
void unlock_shared() noexcept {
|
||||||
lc_.fetch_sub(1, std::memory_order_release);
|
lc_.fetch_sub(1, std::memory_order_release);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
104
include/shm.h
104
include/shm.h
@ -1,52 +1,52 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include "export.h"
|
#include "export.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace shm {
|
namespace shm {
|
||||||
|
|
||||||
using id_t = void*;
|
using id_t = void*;
|
||||||
|
|
||||||
enum : unsigned {
|
enum : unsigned {
|
||||||
create = 0x01,
|
create = 0x01,
|
||||||
open = 0x02
|
open = 0x02
|
||||||
};
|
};
|
||||||
|
|
||||||
IPC_EXPORT id_t acquire(char const * name, std::size_t size, unsigned mode = create | open);
|
IPC_EXPORT id_t acquire(char const * name, std::size_t size, unsigned mode = create | open);
|
||||||
IPC_EXPORT void * get_mem(id_t id, std::size_t * size);
|
IPC_EXPORT void * get_mem(id_t id, std::size_t * size);
|
||||||
IPC_EXPORT void release(id_t id);
|
IPC_EXPORT void release(id_t id);
|
||||||
IPC_EXPORT void remove (id_t id);
|
IPC_EXPORT void remove (id_t id);
|
||||||
IPC_EXPORT void remove (char const * name);
|
IPC_EXPORT void remove (char const * name);
|
||||||
|
|
||||||
class IPC_EXPORT handle {
|
class IPC_EXPORT handle {
|
||||||
public:
|
public:
|
||||||
handle();
|
handle();
|
||||||
handle(char const * name, std::size_t size, unsigned mode = create | open);
|
handle(char const * name, std::size_t size, unsigned mode = create | open);
|
||||||
handle(handle&& rhs);
|
handle(handle&& rhs);
|
||||||
|
|
||||||
~handle();
|
~handle();
|
||||||
|
|
||||||
void swap(handle& rhs);
|
void swap(handle& rhs);
|
||||||
handle& operator=(handle rhs);
|
handle& operator=(handle rhs);
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
std::size_t size () const;
|
std::size_t size () const;
|
||||||
char const * name () const;
|
char const * name () const;
|
||||||
|
|
||||||
bool acquire(char const * name, std::size_t size, unsigned mode = create | open);
|
bool acquire(char const * name, std::size_t size, unsigned mode = create | open);
|
||||||
void release();
|
void release();
|
||||||
|
|
||||||
void* get() const;
|
void* get() const;
|
||||||
|
|
||||||
void attach(id_t);
|
void attach(id_t);
|
||||||
id_t detach();
|
id_t detach();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class handle_;
|
class handle_;
|
||||||
handle_* p_;
|
handle_* p_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shm
|
} // namespace shm
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,86 +1,90 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include "export.h"
|
#include "export.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace tls {
|
namespace tls {
|
||||||
|
|
||||||
using key_t = std::uint64_t;
|
using key_t = std::uint64_t;
|
||||||
using destructor_t = void (*)(void*);
|
using destructor_t = void (*)(void*);
|
||||||
|
|
||||||
enum : key_t {
|
enum : key_t {
|
||||||
invalid_value = (std::numeric_limits<key_t>::max)()
|
invalid_value = (std::numeric_limits<key_t>::max)()
|
||||||
};
|
};
|
||||||
|
|
||||||
IPC_EXPORT key_t create (destructor_t destructor = nullptr);
|
IPC_EXPORT key_t create (destructor_t destructor = nullptr);
|
||||||
IPC_EXPORT void release(key_t key);
|
IPC_EXPORT void release(key_t key);
|
||||||
|
|
||||||
IPC_EXPORT bool set(key_t key, void* ptr);
|
IPC_EXPORT bool set(key_t key, void* ptr);
|
||||||
IPC_EXPORT void* get(key_t key);
|
IPC_EXPORT void* get(key_t key);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// Thread-local pointer
|
/// Thread-local pointer
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
<Remarks>
|
* <Remarks>
|
||||||
|
*
|
||||||
1. In Windows, if you do not compile thread_local_ptr.cpp,
|
* 1. In Windows, if you do not compile thread_local_ptr.cpp,
|
||||||
use thread_local_ptr will cause memory leaks.
|
* use thread_local_ptr will cause memory leaks.
|
||||||
|
*
|
||||||
2. You need to set the thread_local_ptr's storage manually:
|
* 2. You need to set the thread_local_ptr's storage manually:
|
||||||
```
|
* ```
|
||||||
thread_local_ptr<int> p;
|
* tls::pointer<int> p;
|
||||||
if (!p) p = new int(123);
|
* if (!p) p = new int(123);
|
||||||
```
|
* ```
|
||||||
Just like an ordinary pointer. Or you could just call create:
|
* Just like an ordinary pointer. Or you could just call create:
|
||||||
```
|
* ```
|
||||||
thread_local_ptr<int> p;
|
* tls::pointer<int> p;
|
||||||
p.create(123);
|
* p.create(123);
|
||||||
```
|
* ```
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class pointer {
|
class pointer {
|
||||||
key_t key_;
|
key_t key_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
|
||||||
pointer() {
|
pointer()
|
||||||
key_ = tls::create([](void* p) { delete static_cast<T*>(p); });
|
: key_(tls::create([](void* p) { delete static_cast<T*>(p); })) {
|
||||||
}
|
}
|
||||||
|
|
||||||
~pointer() {
|
~pointer() {
|
||||||
tls::release(key_);
|
tls::release(key_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... P>
|
template <typename... P>
|
||||||
T* create(P&&... params) {
|
T* create(P&&... params) {
|
||||||
thread_local auto ptr = static_cast<T*>(*this);
|
thread_local auto ptr = static_cast<T*>(get(key_));
|
||||||
if (ptr == nullptr) {
|
if (ptr == nullptr) {
|
||||||
return ptr = (*this) = new T(std::forward<P>(params)...);
|
ptr = new T(std::forward<P>(params)...);
|
||||||
}
|
if (!set(key_, ptr)) {
|
||||||
return ptr;
|
delete ptr;
|
||||||
}
|
return nullptr;
|
||||||
|
}
|
||||||
T* operator=(T* ptr) {
|
}
|
||||||
set(key_, ptr);
|
return ptr;
|
||||||
return ptr;
|
}
|
||||||
}
|
|
||||||
|
T* operator=(T* ptr) {
|
||||||
operator T*() const { return static_cast<T*>(get(key_)); }
|
set(key_, ptr);
|
||||||
|
return ptr;
|
||||||
T& operator* () { return *static_cast<T*>(*this); }
|
}
|
||||||
const T& operator* () const { return *static_cast<T*>(*this); }
|
|
||||||
|
operator T*() const { return static_cast<T*>(get(key_)); }
|
||||||
T* operator->() { return static_cast<T*>(*this); }
|
|
||||||
const T* operator->() const { return static_cast<T*>(*this); }
|
T& operator* () { return *static_cast<T*>(*this); }
|
||||||
};
|
const T& operator* () const { return *static_cast<T*>(*this); }
|
||||||
|
|
||||||
} // namespace tls
|
T* operator->() { return static_cast<T*>(*this); }
|
||||||
} // namespace ipc
|
const T* operator->() const { return static_cast<T*>(*this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tls
|
||||||
|
} // namespace ipc
|
||||||
|
|||||||
186
include/waiter.h
186
include/waiter.h
@ -1,93 +1,93 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "export.h"
|
#include "export.h"
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
class condition;
|
class condition;
|
||||||
class IPC_EXPORT mutex {
|
class IPC_EXPORT mutex {
|
||||||
public:
|
public:
|
||||||
mutex();
|
mutex();
|
||||||
explicit mutex(char const * name);
|
explicit mutex(char const * name);
|
||||||
mutex(mutex&& rhs);
|
mutex(mutex&& rhs);
|
||||||
|
|
||||||
~mutex();
|
~mutex();
|
||||||
|
|
||||||
static void remove(char const * name);
|
static void remove(char const * name);
|
||||||
|
|
||||||
void swap(mutex& rhs);
|
void swap(mutex& rhs);
|
||||||
mutex& operator=(mutex rhs);
|
mutex& operator=(mutex rhs);
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
char const * name () const;
|
char const * name () const;
|
||||||
|
|
||||||
bool open (char const * name);
|
bool open (char const * name);
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
bool lock ();
|
bool lock ();
|
||||||
bool unlock();
|
bool unlock();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class mutex_;
|
class mutex_;
|
||||||
mutex_* p_;
|
mutex_* p_;
|
||||||
|
|
||||||
friend class condition;
|
friend class condition;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IPC_EXPORT semaphore {
|
class IPC_EXPORT semaphore {
|
||||||
public:
|
public:
|
||||||
semaphore();
|
semaphore();
|
||||||
explicit semaphore(char const * name);
|
explicit semaphore(char const * name);
|
||||||
semaphore(semaphore&& rhs);
|
semaphore(semaphore&& rhs);
|
||||||
|
|
||||||
~semaphore();
|
~semaphore();
|
||||||
|
|
||||||
static void remove(char const * name);
|
static void remove(char const * name);
|
||||||
|
|
||||||
void swap(semaphore& rhs);
|
void swap(semaphore& rhs);
|
||||||
semaphore& operator=(semaphore rhs);
|
semaphore& operator=(semaphore rhs);
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
char const * name () const;
|
char const * name () const;
|
||||||
|
|
||||||
bool open (char const * name, long count = 0);
|
bool open (char const * name, long count = 0);
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
bool wait(std::size_t tm = invalid_value);
|
bool wait(std::size_t tm = invalid_value);
|
||||||
bool post(long count = 1);
|
bool post(long count = 1);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class semaphore_;
|
class semaphore_;
|
||||||
semaphore_* p_;
|
semaphore_* p_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IPC_EXPORT condition {
|
class IPC_EXPORT condition {
|
||||||
public:
|
public:
|
||||||
condition();
|
condition();
|
||||||
explicit condition(char const * name);
|
explicit condition(char const * name);
|
||||||
condition(condition&& rhs);
|
condition(condition&& rhs);
|
||||||
|
|
||||||
~condition();
|
~condition();
|
||||||
|
|
||||||
static void remove(char const * name);
|
static void remove(char const * name);
|
||||||
|
|
||||||
void swap(condition& rhs);
|
void swap(condition& rhs);
|
||||||
condition& operator=(condition rhs);
|
condition& operator=(condition rhs);
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
char const * name () const;
|
char const * name () const;
|
||||||
|
|
||||||
bool open (char const * name);
|
bool open (char const * name);
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
bool wait(mutex&, std::size_t tm = invalid_value);
|
bool wait(mutex&, std::size_t tm = invalid_value);
|
||||||
bool notify();
|
bool notify();
|
||||||
bool broadcast();
|
bool broadcast();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class condition_;
|
class condition_;
|
||||||
condition_* p_;
|
condition_* p_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
166
src/buffer.cpp
166
src/buffer.cpp
@ -1,83 +1,83 @@
|
|||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "pimpl.h"
|
#include "pimpl.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
bool operator==(buffer const & b1, buffer const & b2) {
|
bool operator==(buffer const & b1, buffer const & b2) {
|
||||||
return (b1.size() == b2.size()) && (std::memcmp(b1.data(), b2.data(), b1.size()) == 0);
|
return (b1.size() == b2.size()) && (std::memcmp(b1.data(), b2.data(), b1.size()) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
class buffer::buffer_ : public pimpl<buffer_> {
|
class buffer::buffer_ : public pimpl<buffer_> {
|
||||||
public:
|
public:
|
||||||
void* p_;
|
void* p_;
|
||||||
std::size_t s_;
|
std::size_t s_;
|
||||||
void* a_;
|
void* a_;
|
||||||
buffer::destructor_t d_;
|
buffer::destructor_t d_;
|
||||||
|
|
||||||
buffer_(void* p, std::size_t s, buffer::destructor_t d, void* a)
|
buffer_(void* p, std::size_t s, buffer::destructor_t d, void* a)
|
||||||
: p_(p), s_(s), a_(a), d_(d) {
|
: p_(p), s_(s), a_(a), d_(d) {
|
||||||
}
|
}
|
||||||
|
|
||||||
~buffer_() {
|
~buffer_() {
|
||||||
if (d_ == nullptr) return;
|
if (d_ == nullptr) return;
|
||||||
d_((a_ == nullptr) ? p_ : a_, s_);
|
d_((a_ == nullptr) ? p_ : a_, s_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
buffer::buffer()
|
buffer::buffer()
|
||||||
: buffer(nullptr, 0, nullptr, nullptr) {
|
: buffer(nullptr, 0, nullptr, nullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer::buffer(void* p, std::size_t s, destructor_t d)
|
buffer::buffer(void* p, std::size_t s, destructor_t d)
|
||||||
: p_(p_->make(p, s, d, nullptr)) {
|
: p_(p_->make(p, s, d, nullptr)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer::buffer(void* p, std::size_t s, destructor_t d, void* additional)
|
buffer::buffer(void* p, std::size_t s, destructor_t d, void* additional)
|
||||||
: p_(p_->make(p, s, d, additional)) {
|
: p_(p_->make(p, s, d, additional)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer::buffer(void* p, std::size_t s)
|
buffer::buffer(void* p, std::size_t s)
|
||||||
: buffer(p, s, nullptr) {
|
: buffer(p, s, nullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer::buffer(char const & c)
|
buffer::buffer(char const & c)
|
||||||
: buffer(const_cast<char*>(&c), 1) {
|
: buffer(const_cast<char*>(&c), 1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer::buffer(buffer&& rhs)
|
buffer::buffer(buffer&& rhs)
|
||||||
: buffer() {
|
: buffer() {
|
||||||
swap(rhs);
|
swap(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer::~buffer() {
|
buffer::~buffer() {
|
||||||
p_->clear();
|
p_->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void buffer::swap(buffer& rhs) {
|
void buffer::swap(buffer& rhs) {
|
||||||
std::swap(p_, rhs.p_);
|
std::swap(p_, rhs.p_);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer& buffer::operator=(buffer rhs) {
|
buffer& buffer::operator=(buffer rhs) {
|
||||||
swap(rhs);
|
swap(rhs);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool buffer::empty() const noexcept {
|
bool buffer::empty() const noexcept {
|
||||||
return (impl(p_)->p_ == nullptr) || (impl(p_)->s_ == 0);
|
return (impl(p_)->p_ == nullptr) || (impl(p_)->s_ == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* buffer::data() noexcept {
|
void* buffer::data() noexcept {
|
||||||
return impl(p_)->p_;
|
return impl(p_)->p_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void const * buffer::data() const noexcept {
|
void const * buffer::data() const noexcept {
|
||||||
return impl(p_)->p_;
|
return impl(p_)->p_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t buffer::size() const noexcept {
|
std::size_t buffer::size() const noexcept {
|
||||||
return impl(p_)->s_;
|
return impl(p_)->s_;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,60 +1,60 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
|
|
||||||
#include "circ/elem_def.h"
|
#include "circ/elem_def.h"
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace circ {
|
namespace circ {
|
||||||
|
|
||||||
template <typename Policy,
|
template <typename Policy,
|
||||||
std::size_t DataSize,
|
std::size_t DataSize,
|
||||||
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))>
|
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))>
|
||||||
class elem_array : public ipc::circ::conn_head {
|
class elem_array : public ipc::circ::conn_head {
|
||||||
public:
|
public:
|
||||||
using base_t = ipc::circ::conn_head;
|
using base_t = ipc::circ::conn_head;
|
||||||
using policy_t = Policy;
|
using policy_t = Policy;
|
||||||
using cursor_t = decltype(std::declval<policy_t>().cursor());
|
using cursor_t = decltype(std::declval<policy_t>().cursor());
|
||||||
using elem_t = typename policy_t::template elem_t<DataSize, AlignSize>;
|
using elem_t = typename policy_t::template elem_t<DataSize, AlignSize>;
|
||||||
|
|
||||||
enum : std::size_t {
|
enum : std::size_t {
|
||||||
head_size = sizeof(base_t) + sizeof(policy_t),
|
head_size = sizeof(base_t) + sizeof(policy_t),
|
||||||
data_size = DataSize,
|
data_size = DataSize,
|
||||||
elem_max = (std::numeric_limits<uint_t<8>>::max)() + 1, // default is 255 + 1
|
elem_max = (std::numeric_limits<uint_t<8>>::max)() + 1, // default is 255 + 1
|
||||||
elem_size = sizeof(elem_t),
|
elem_size = sizeof(elem_t),
|
||||||
block_size = elem_size * elem_max
|
block_size = elem_size * elem_max
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
policy_t head_;
|
policy_t head_;
|
||||||
elem_t block_[elem_max] {};
|
elem_t block_[elem_max] {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cursor_t cursor() const noexcept {
|
cursor_t cursor() const noexcept {
|
||||||
return head_.cursor();
|
return head_.cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool push(F&& f) {
|
bool push(F&& f) {
|
||||||
return head_.push(this, std::forward<F>(f), block_);
|
return head_.push(this, std::forward<F>(f), block_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool force_push(F&& f) {
|
bool force_push(F&& f) {
|
||||||
return head_.force_push(this, std::forward<F>(f), block_);
|
return head_.force_push(this, std::forward<F>(f), block_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool pop(cursor_t* cur, F&& f) {
|
bool pop(cursor_t* cur, F&& f) {
|
||||||
if (cur == nullptr) return false;
|
if (cur == nullptr) return false;
|
||||||
return head_.pop(this, *cur, std::forward<F>(f), block_);
|
return head_.pop(this, *cur, std::forward<F>(f), block_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace circ
|
} // namespace circ
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,75 +1,75 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
|
||||||
#include "rw_lock.h"
|
#include "rw_lock.h"
|
||||||
|
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace circ {
|
namespace circ {
|
||||||
|
|
||||||
using u1_t = ipc::uint_t<8>;
|
using u1_t = ipc::uint_t<8>;
|
||||||
using u2_t = ipc::uint_t<32>;
|
using u2_t = ipc::uint_t<32>;
|
||||||
|
|
||||||
constexpr u1_t index_of(u2_t c) noexcept {
|
constexpr u1_t index_of(u2_t c) noexcept {
|
||||||
return static_cast<u1_t>(c);
|
return static_cast<u1_t>(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
class conn_head {
|
class conn_head {
|
||||||
std::atomic<std::size_t> cc_ { 0 }; // connection counter
|
std::atomic<u2_t> cc_ { 0 }; // connection counter
|
||||||
|
|
||||||
ipc::spin_lock lc_;
|
ipc::spin_lock lc_;
|
||||||
std::atomic<bool> constructed_;
|
std::atomic<bool> constructed_;
|
||||||
|
|
||||||
std::atomic<bool> dis_flag_;
|
std::atomic<bool> dis_flag_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void init() {
|
void init() {
|
||||||
/* DCLP */
|
/* DCLP */
|
||||||
if (!constructed_.load(std::memory_order_acquire)) {
|
if (!constructed_.load(std::memory_order_acquire)) {
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_);
|
||||||
if (!constructed_.load(std::memory_order_relaxed)) {
|
if (!constructed_.load(std::memory_order_relaxed)) {
|
||||||
::new (this) conn_head;
|
::new (this) conn_head;
|
||||||
constructed_.store(true, std::memory_order_release);
|
constructed_.store(true, std::memory_order_release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conn_head() = default;
|
conn_head() = default;
|
||||||
conn_head(const conn_head&) = delete;
|
conn_head(const conn_head&) = delete;
|
||||||
conn_head& operator=(const conn_head&) = delete;
|
conn_head& operator=(const conn_head&) = delete;
|
||||||
|
|
||||||
std::size_t connect() noexcept {
|
std::size_t connect() noexcept {
|
||||||
return cc_.fetch_add(1, std::memory_order_acq_rel);
|
return cc_.fetch_add(1, std::memory_order_acq_rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t disconnect() noexcept {
|
std::size_t disconnect() noexcept {
|
||||||
return cc_.fetch_sub(1, std::memory_order_acq_rel);
|
return cc_.fetch_sub(1, std::memory_order_acq_rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void try_disconnect() noexcept {
|
void try_disconnect() noexcept {
|
||||||
if (!dis_flag_.load(std::memory_order_acquire)) {
|
if (!dis_flag_.load(std::memory_order_acquire)) {
|
||||||
cc_.fetch_sub(1, std::memory_order_relaxed);
|
cc_.fetch_sub(1, std::memory_order_relaxed);
|
||||||
dis_flag_.store(true, std::memory_order_release);
|
dis_flag_.store(true, std::memory_order_release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_dis_flag(std::memory_order order = std::memory_order_release) noexcept {
|
void clear_dis_flag(std::memory_order order = std::memory_order_release) noexcept {
|
||||||
dis_flag_.store(false, order);
|
dis_flag_.store(false, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dis_flag(std::memory_order order = std::memory_order_acquire) const noexcept {
|
bool dis_flag(std::memory_order order = std::memory_order_acquire) const noexcept {
|
||||||
return dis_flag_.load(order);
|
return dis_flag_.load(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t conn_count(std::memory_order order = std::memory_order_acquire) const noexcept {
|
std::size_t conn_count(std::memory_order order = std::memory_order_acquire) const noexcept {
|
||||||
return cc_.load(order);
|
return cc_.load(order);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace circ
|
} // namespace circ
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,29 +1,29 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
// concept helpers
|
// concept helpers
|
||||||
|
|
||||||
template <bool Cond, typename R = void>
|
template <bool Cond, typename R = void>
|
||||||
using require = std::enable_if_t<Cond, R>;
|
using require = std::enable_if_t<Cond, R>;
|
||||||
|
|
||||||
#ifdef IPC_CONCEPT_
|
#ifdef IPC_CONCEPT_
|
||||||
# error "IPC_CONCEPT_ has been defined."
|
# error "IPC_CONCEPT_ has been defined."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define IPC_CONCEPT_(NAME, WHAT) \
|
#define IPC_CONCEPT_(NAME, WHAT) \
|
||||||
template <typename T> \
|
template <typename T> \
|
||||||
class NAME { \
|
class NAME { \
|
||||||
private: \
|
private: \
|
||||||
template <typename Type> \
|
template <typename Type> \
|
||||||
static std::true_type check(decltype(std::declval<Type>().WHAT)*); \
|
static std::true_type check(decltype(std::declval<Type>().WHAT)*); \
|
||||||
template <typename Type> \
|
template <typename Type> \
|
||||||
static std::false_type check(...); \
|
static std::false_type check(...); \
|
||||||
public: \
|
public: \
|
||||||
using type = decltype(check<T>(nullptr)); \
|
using type = decltype(check<T>(nullptr)); \
|
||||||
constexpr static auto value = type::value; \
|
constexpr static auto value = type::value; \
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
970
src/ipc.cpp
970
src/ipc.cpp
@ -1,485 +1,485 @@
|
|||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
#include "tls_pointer.h"
|
#include "tls_pointer.h"
|
||||||
#include "pool_alloc.h"
|
#include "pool_alloc.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "policy.h"
|
#include "policy.h"
|
||||||
#include "rw_lock.h"
|
#include "rw_lock.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
|
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
#include "platform/waiter_wrapper.h"
|
#include "platform/waiter_wrapper.h"
|
||||||
|
|
||||||
#include "circ/elem_array.h"
|
#include "circ/elem_array.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using namespace ipc;
|
using namespace ipc;
|
||||||
using msg_id_t = std::size_t;
|
using msg_id_t = std::uint32_t;
|
||||||
|
|
||||||
template <std::size_t DataSize, std::size_t AlignSize>
|
template <std::size_t DataSize, std::size_t AlignSize>
|
||||||
struct msg_t;
|
struct msg_t;
|
||||||
|
|
||||||
template <std::size_t AlignSize>
|
template <std::size_t AlignSize>
|
||||||
struct msg_t<0, AlignSize> {
|
struct msg_t<0, AlignSize> {
|
||||||
msg_id_t conn_;
|
msg_id_t conn_;
|
||||||
msg_id_t id_;
|
msg_id_t id_;
|
||||||
int remain_;
|
std::int32_t remain_;
|
||||||
bool storage_;
|
bool storage_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t DataSize, std::size_t AlignSize>
|
template <std::size_t DataSize, std::size_t AlignSize>
|
||||||
struct msg_t : msg_t<0, AlignSize> {
|
struct msg_t : msg_t<0, AlignSize> {
|
||||||
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
||||||
|
|
||||||
msg_t() = default;
|
msg_t() = default;
|
||||||
msg_t(msg_id_t c, msg_id_t i, int r, void const * d, std::size_t s)
|
msg_t(msg_id_t c, msg_id_t i, std::int32_t r, void const * d, std::size_t s)
|
||||||
: msg_t<0, AlignSize> { c, i, r, (d == nullptr) || (s == 0) } {
|
: msg_t<0, AlignSize> { c, i, r, (d == nullptr) || (s == 0) } {
|
||||||
if (!this->storage_) {
|
if (!this->storage_) {
|
||||||
std::memcpy(&data_, d, s);
|
std::memcpy(&data_, d, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
buff_t make_cache(T& data, std::size_t size) {
|
buff_t make_cache(T& data, std::size_t size) {
|
||||||
auto ptr = mem::alloc(size);
|
auto ptr = mem::alloc(size);
|
||||||
std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size));
|
std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size));
|
||||||
return { ptr, size, mem::free };
|
return { ptr, size, mem::free };
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cache_t {
|
struct cache_t {
|
||||||
std::size_t fill_;
|
std::size_t fill_;
|
||||||
buff_t buff_;
|
buff_t buff_;
|
||||||
|
|
||||||
cache_t(std::size_t f, buff_t&& b)
|
cache_t(std::size_t f, buff_t&& b)
|
||||||
: fill_(f), buff_(std::move(b))
|
: fill_(f), buff_(std::move(b))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void append(void const * data, std::size_t size) {
|
void append(void const * data, std::size_t size) {
|
||||||
if (fill_ >= buff_.size() || data == nullptr || size == 0) return;
|
if (fill_ >= buff_.size() || data == nullptr || size == 0) return;
|
||||||
auto new_fill = (ipc::detail::min)(fill_ + size, buff_.size());
|
auto new_fill = (ipc::detail::min)(fill_ + size, buff_.size());
|
||||||
std::memcpy(static_cast<byte_t*>(buff_.data()) + fill_, data, new_fill - fill_);
|
std::memcpy(static_cast<byte_t*>(buff_.data()) + fill_, data, new_fill - fill_);
|
||||||
fill_ = new_fill;
|
fill_ = new_fill;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct conn_info_head {
|
struct conn_info_head {
|
||||||
using acc_t = std::atomic<msg_id_t>;
|
using acc_t = std::atomic<msg_id_t>;
|
||||||
|
|
||||||
static auto cc_acc() {
|
static auto cc_acc() {
|
||||||
static shm::handle acc_h("__CA_CONN__", sizeof(acc_t));
|
static shm::handle acc_h("__CA_CONN__", sizeof(acc_t));
|
||||||
return static_cast<acc_t*>(acc_h.get());
|
return static_cast<acc_t*>(acc_h.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc::string name_;
|
ipc::string name_;
|
||||||
msg_id_t cc_id_; // connection-info id
|
msg_id_t cc_id_; // connection-info id
|
||||||
waiter cc_waiter_, wt_waiter_, rd_waiter_;
|
waiter cc_waiter_, wt_waiter_, rd_waiter_;
|
||||||
shm::handle acc_h_;
|
shm::handle acc_h_;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* <Remarks> thread_local may have some bugs.
|
* <Remarks> thread_local may have some bugs.
|
||||||
*
|
*
|
||||||
* <Reference>
|
* <Reference>
|
||||||
* - https://sourceforge.net/p/mingw-w64/bugs/727/
|
* - https://sourceforge.net/p/mingw-w64/bugs/727/
|
||||||
* - https://sourceforge.net/p/mingw-w64/bugs/527/
|
* - https://sourceforge.net/p/mingw-w64/bugs/527/
|
||||||
* - https://github.com/Alexpux/MINGW-packages/issues/2519
|
* - https://github.com/Alexpux/MINGW-packages/issues/2519
|
||||||
* - https://github.com/ChaiScript/ChaiScript/issues/402
|
* - https://github.com/ChaiScript/ChaiScript/issues/402
|
||||||
* - https://developercommunity.visualstudio.com/content/problem/124121/thread-local-variables-fail-to-be-initialized-when.html
|
* - https://developercommunity.visualstudio.com/content/problem/124121/thread-local-variables-fail-to-be-initialized-when.html
|
||||||
* - https://software.intel.com/en-us/forums/intel-c-compiler/topic/684827
|
* - https://software.intel.com/en-us/forums/intel-c-compiler/topic/684827
|
||||||
*/
|
*/
|
||||||
tls::pointer<ipc::unordered_map<msg_id_t, cache_t>> recv_cache_;
|
tls::pointer<ipc::unordered_map<msg_id_t, cache_t>> recv_cache_;
|
||||||
|
|
||||||
struct simple_push {
|
struct simple_push {
|
||||||
|
|
||||||
template <std::size_t, std::size_t>
|
template <std::size_t, std::size_t>
|
||||||
using elem_t = shm::id_t;
|
using elem_t = shm::id_t;
|
||||||
|
|
||||||
circ::u2_t wt_; // write index
|
circ::u2_t wt_; // write index
|
||||||
|
|
||||||
constexpr circ::u2_t cursor() const noexcept {
|
constexpr circ::u2_t cursor() const noexcept {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool push(W* /*wrapper*/, F&& f, E* elems) {
|
bool push(W* /*wrapper*/, F&& f, E* elems) {
|
||||||
std::forward<F>(f)(&(elems[circ::index_of(wt_)]));
|
std::forward<F>(f)(&(elems[circ::index_of(wt_)]));
|
||||||
++ wt_;
|
++ wt_;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
circ::elem_array<simple_push, sizeof(shm::id_t)> msg_datas_;
|
circ::elem_array<simple_push, sizeof(shm::id_t)> msg_datas_;
|
||||||
|
|
||||||
conn_info_head(char const * name)
|
conn_info_head(char const * name)
|
||||||
: name_ (name)
|
: name_ (name)
|
||||||
, cc_id_ ((cc_acc() == nullptr) ? 0 : cc_acc()->fetch_add(1, std::memory_order_relaxed))
|
, cc_id_ ((cc_acc() == nullptr) ? 0 : cc_acc()->fetch_add(1, std::memory_order_relaxed))
|
||||||
, cc_waiter_(("__CC_CONN__" + name_).c_str())
|
, cc_waiter_(("__CC_CONN__" + name_).c_str())
|
||||||
, wt_waiter_(("__WT_CONN__" + name_).c_str())
|
, wt_waiter_(("__WT_CONN__" + name_).c_str())
|
||||||
, rd_waiter_(("__RD_CONN__" + name_).c_str())
|
, rd_waiter_(("__RD_CONN__" + name_).c_str())
|
||||||
, acc_h_ (("__AC_CONN__" + name_).c_str(), sizeof(acc_t)) {
|
, acc_h_ (("__AC_CONN__" + name_).c_str(), sizeof(acc_t)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto acc() {
|
auto acc() {
|
||||||
return static_cast<acc_t*>(acc_h_.get());
|
return static_cast<acc_t*>(acc_h_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& recv_cache() {
|
auto& recv_cache() {
|
||||||
return *recv_cache_.create();
|
return *recv_cache_.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
shm::id_t apply_storage(msg_id_t msg_id, std::size_t size) {
|
shm::id_t apply_storage(msg_id_t msg_id, std::size_t size) {
|
||||||
return shm::acquire(
|
return shm::acquire(
|
||||||
("__ST_CONN__" + ipc::to_string(cc_id_) +
|
("__ST_CONN__" + ipc::to_string(cc_id_) +
|
||||||
"__" + ipc::to_string(msg_id)).c_str(), size, shm::create);
|
"__" + ipc::to_string(msg_id)).c_str(), size, shm::create);
|
||||||
}
|
}
|
||||||
|
|
||||||
static shm::id_t acquire_storage(msg_id_t cc_id, msg_id_t msg_id) {
|
static shm::id_t acquire_storage(msg_id_t cc_id, msg_id_t msg_id) {
|
||||||
return shm::acquire(
|
return shm::acquire(
|
||||||
("__ST_CONN__" + ipc::to_string(cc_id) +
|
("__ST_CONN__" + ipc::to_string(cc_id) +
|
||||||
"__" + ipc::to_string(msg_id)).c_str(), 0, shm::open);
|
"__" + ipc::to_string(msg_id)).c_str(), 0, shm::open);
|
||||||
}
|
}
|
||||||
|
|
||||||
void store(shm::id_t dat) {
|
void store(shm::id_t dat) {
|
||||||
msg_datas_.push([dat](shm::id_t * id) {
|
msg_datas_.push([dat](shm::id_t * id) {
|
||||||
(*id) = dat;
|
(*id) = dat;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_store() {
|
void clear_store() {
|
||||||
msg_datas_.push([](shm::id_t * id) {
|
msg_datas_.push([](shm::id_t * id) {
|
||||||
if (*id == nullptr) return;
|
if (*id == nullptr) return;
|
||||||
shm::remove(*id);
|
shm::remove(*id);
|
||||||
(*id) = nullptr;
|
(*id) = nullptr;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename W, typename F>
|
template <typename W, typename F>
|
||||||
bool wait_for(W& waiter, F&& pred, std::size_t tm) {
|
bool wait_for(W& waiter, F&& pred, std::size_t tm) {
|
||||||
if (tm == 0) return !pred();
|
if (tm == 0) return !pred();
|
||||||
for (unsigned k = 0; pred();) {
|
for (unsigned k = 0; pred();) {
|
||||||
bool loop = true, ret = true;
|
bool loop = true, ret = true;
|
||||||
ipc::sleep(k, [&k, &loop, &ret, &waiter, &pred, tm] {
|
ipc::sleep(k, [&k, &loop, &ret, &waiter, &pred, tm] {
|
||||||
ret = waiter.wait_if([&loop, &pred] {
|
ret = waiter.wait_if([&loop, &pred] {
|
||||||
return loop = pred();
|
return loop = pred();
|
||||||
}, tm);
|
}, tm);
|
||||||
k = 0;
|
k = 0;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if (!ret ) return false; // timeout or fail
|
if (!ret ) return false; // timeout or fail
|
||||||
if (!loop) break;
|
if (!loop) break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Policy,
|
template <typename Policy,
|
||||||
std::size_t DataSize = data_length,
|
std::size_t DataSize = data_length,
|
||||||
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))>
|
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))>
|
||||||
struct queue_generator {
|
struct queue_generator {
|
||||||
|
|
||||||
using queue_t = ipc::queue<msg_t<DataSize, AlignSize>, Policy>;
|
using queue_t = ipc::queue<msg_t<DataSize, AlignSize>, Policy>;
|
||||||
|
|
||||||
struct conn_info_t : conn_info_head {
|
struct conn_info_t : conn_info_head {
|
||||||
queue_t que_;
|
queue_t que_;
|
||||||
|
|
||||||
conn_info_t(char const * name)
|
conn_info_t(char const * name)
|
||||||
: conn_info_head(name)
|
: conn_info_head(name)
|
||||||
, que_(("__QU_CONN__" +
|
, que_(("__QU_CONN__" +
|
||||||
ipc::to_string(DataSize) + "__" +
|
ipc::to_string(DataSize) + "__" +
|
||||||
ipc::to_string(AlignSize) + "__" + name).c_str()) {
|
ipc::to_string(AlignSize) + "__" + name).c_str()) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Policy>
|
template <typename Policy>
|
||||||
struct detail_impl {
|
struct detail_impl {
|
||||||
|
|
||||||
using queue_t = typename queue_generator<Policy>::queue_t;
|
using queue_t = typename queue_generator<Policy>::queue_t;
|
||||||
using conn_info_t = typename queue_generator<Policy>::conn_info_t;
|
using conn_info_t = typename queue_generator<Policy>::conn_info_t;
|
||||||
|
|
||||||
constexpr static conn_info_t* info_of(ipc::handle_t h) {
|
constexpr static conn_info_t* info_of(ipc::handle_t h) {
|
||||||
return static_cast<conn_info_t*>(h);
|
return static_cast<conn_info_t*>(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr static queue_t* queue_of(ipc::handle_t h) {
|
constexpr static queue_t* queue_of(ipc::handle_t h) {
|
||||||
return (info_of(h) == nullptr) ? nullptr : &(info_of(h)->que_);
|
return (info_of(h) == nullptr) ? nullptr : &(info_of(h)->que_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* API implementations */
|
/* API implementations */
|
||||||
|
|
||||||
static ipc::handle_t connect(char const * name, bool start) {
|
static ipc::handle_t connect(char const * name, bool start) {
|
||||||
auto h = mem::alloc<conn_info_t>(name);
|
auto h = mem::alloc<conn_info_t>(name);
|
||||||
auto que = queue_of(h);
|
auto que = queue_of(h);
|
||||||
if (que == nullptr) {
|
if (que == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (start) {
|
if (start) {
|
||||||
if (que->connect()) { // wouldn't connect twice
|
if (que->connect()) { // wouldn't connect twice
|
||||||
info_of(h)->cc_waiter_.broadcast();
|
info_of(h)->cc_waiter_.broadcast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disconnect(ipc::handle_t h) {
|
static void disconnect(ipc::handle_t h) {
|
||||||
auto que = queue_of(h);
|
auto que = queue_of(h);
|
||||||
if (que == nullptr) {
|
if (que == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (que->disconnect()) {
|
if (que->disconnect()) {
|
||||||
info_of(h)->cc_waiter_.broadcast();
|
info_of(h)->cc_waiter_.broadcast();
|
||||||
}
|
}
|
||||||
mem::free(info_of(h));
|
mem::free(info_of(h));
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::size_t recv_count(ipc::handle_t h) {
|
static std::size_t recv_count(ipc::handle_t h) {
|
||||||
auto que = queue_of(h);
|
auto que = queue_of(h);
|
||||||
if (que == nullptr) {
|
if (que == nullptr) {
|
||||||
return invalid_value;
|
return invalid_value;
|
||||||
}
|
}
|
||||||
return que->conn_count();
|
return que->conn_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) {
|
static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) {
|
||||||
auto que = queue_of(h);
|
auto que = queue_of(h);
|
||||||
if (que == nullptr) {
|
if (que == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return wait_for(info_of(h)->cc_waiter_, [que, r_count] {
|
return wait_for(info_of(h)->cc_waiter_, [que, r_count] {
|
||||||
return que->conn_count() < r_count;
|
return que->conn_count() < r_count;
|
||||||
}, tm);
|
}, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t size) {
|
static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t size) {
|
||||||
if (data == nullptr || size == 0) {
|
if (data == nullptr || size == 0) {
|
||||||
ipc::error("fail: send(%p, %zd)\n", data, size);
|
ipc::error("fail: send(%p, %zd)\n", data, size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto que = queue_of(h);
|
auto que = queue_of(h);
|
||||||
if (que == nullptr) {
|
if (que == nullptr) {
|
||||||
ipc::error("fail: send, queue_of(h) == nullptr\n");
|
ipc::error("fail: send, queue_of(h) == nullptr\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// calc a new message id
|
// calc a new message id
|
||||||
auto acc = info_of(h)->acc();
|
auto acc = info_of(h)->acc();
|
||||||
if (acc == nullptr) {
|
if (acc == nullptr) {
|
||||||
ipc::error("fail: send, info_of(h)->acc() == nullptr\n");
|
ipc::error("fail: send, info_of(h)->acc() == nullptr\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto msg_id = acc->fetch_add(1, std::memory_order_relaxed);
|
auto msg_id = acc->fetch_add(1, std::memory_order_relaxed);
|
||||||
auto try_push = std::forward<F>(gen_push)(info_of(h), que, msg_id);
|
auto try_push = std::forward<F>(gen_push)(info_of(h), que, msg_id);
|
||||||
if (size > small_msg_limit) {
|
if (size > small_msg_limit) {
|
||||||
auto dat = info_of(h)->apply_storage(msg_id, size);
|
auto dat = info_of(h)->apply_storage(msg_id, size);
|
||||||
void * buf = shm::get_mem(dat, nullptr);
|
void * buf = shm::get_mem(dat, nullptr);
|
||||||
if (buf != nullptr) {
|
if (buf != nullptr) {
|
||||||
std::memcpy(buf, data, size);
|
std::memcpy(buf, data, size);
|
||||||
info_of(h)->store(dat);
|
info_of(h)->store(dat);
|
||||||
return try_push(static_cast<int>(size) - static_cast<int>(data_length), nullptr, 0);
|
return try_push(static_cast<int>(size) - static_cast<int>(data_length), nullptr, 0);
|
||||||
}
|
}
|
||||||
// try using message fragment
|
// try using message fragment
|
||||||
ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd\n", msg_id, size);
|
ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd\n", msg_id, size);
|
||||||
}
|
}
|
||||||
// push message fragment
|
// push message fragment
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (int i = 0; i < static_cast<int>(size / data_length); ++i, offset += data_length) {
|
for (int i = 0; i < static_cast<int>(size / data_length); ++i, offset += data_length) {
|
||||||
if (!try_push(static_cast<int>(size) - offset - static_cast<int>(data_length),
|
if (!try_push(static_cast<int>(size) - offset - static_cast<int>(data_length),
|
||||||
static_cast<byte_t const *>(data) + offset, data_length)) {
|
static_cast<byte_t const *>(data) + offset, data_length)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
info_of(h)->clear_store();
|
info_of(h)->clear_store();
|
||||||
}
|
}
|
||||||
// if remain > 0, this is the last message fragment
|
// if remain > 0, this is the last message fragment
|
||||||
int remain = static_cast<int>(size) - offset;
|
int remain = static_cast<int>(size) - offset;
|
||||||
if (remain > 0) {
|
if (remain > 0) {
|
||||||
if (!try_push(remain - static_cast<int>(data_length),
|
if (!try_push(remain - static_cast<int>(data_length),
|
||||||
static_cast<byte_t const *>(data) + offset, static_cast<std::size_t>(remain))) {
|
static_cast<byte_t const *>(data) + offset, static_cast<std::size_t>(remain))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
info_of(h)->clear_store();
|
info_of(h)->clear_store();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool send(ipc::handle_t h, void const * data, std::size_t size) {
|
static bool send(ipc::handle_t h, void const * data, std::size_t size) {
|
||||||
return send([](auto info, auto que, auto msg_id) {
|
return send([](auto info, auto que, auto msg_id) {
|
||||||
return [info, que, msg_id](int remain, void const * data, std::size_t size) {
|
return [info, que, msg_id](int remain, void const * data, std::size_t size) {
|
||||||
if (!wait_for(info->wt_waiter_, [&] {
|
if (!wait_for(info->wt_waiter_, [&] {
|
||||||
return !que->push(info->cc_id_, msg_id, remain, data, size);
|
return !que->push(info->cc_id_, msg_id, remain, data, size);
|
||||||
}, que->dis_flag() ? 0 : static_cast<std::size_t>(default_timeut))) {
|
}, que->dis_flag() ? 0 : static_cast<std::size_t>(default_timeut))) {
|
||||||
ipc::log("force_push: msg_id = %zd, remain = %d, size = %zd\n", msg_id, remain, size);
|
ipc::log("force_push: msg_id = %zd, remain = %d, size = %zd\n", msg_id, remain, size);
|
||||||
if (!que->force_push(info->cc_id_, msg_id, remain, data, size)) {
|
if (!que->force_push(info->cc_id_, msg_id, remain, data, size)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info->rd_waiter_.broadcast();
|
info->rd_waiter_.broadcast();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}, h, data, size);
|
}, h, data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool try_send(ipc::handle_t h, void const * data, std::size_t size) {
|
static bool try_send(ipc::handle_t h, void const * data, std::size_t size) {
|
||||||
return send([](auto info, auto que, auto msg_id) {
|
return send([](auto info, auto que, auto msg_id) {
|
||||||
return [info, que, msg_id](int remain, void const * data, std::size_t size) {
|
return [info, que, msg_id](int remain, void const * data, std::size_t size) {
|
||||||
if (!wait_for(info->wt_waiter_, [&] {
|
if (!wait_for(info->wt_waiter_, [&] {
|
||||||
return !que->push(info->cc_id_, msg_id, remain, data, size);
|
return !que->push(info->cc_id_, msg_id, remain, data, size);
|
||||||
}, 0)) {
|
}, 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
info->rd_waiter_.broadcast();
|
info->rd_waiter_.broadcast();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}, h, data, size);
|
}, h, data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static buff_t recv(ipc::handle_t h, std::size_t tm) {
|
static buff_t recv(ipc::handle_t h, std::size_t tm) {
|
||||||
auto que = queue_of(h);
|
auto que = queue_of(h);
|
||||||
if (que == nullptr) {
|
if (que == nullptr) {
|
||||||
ipc::error("fail: recv, queue_of(h) == nullptr\n");
|
ipc::error("fail: recv, queue_of(h) == nullptr\n");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (que->connect()) { // wouldn't connect twice
|
if (que->connect()) { // wouldn't connect twice
|
||||||
info_of(h)->cc_waiter_.broadcast();
|
info_of(h)->cc_waiter_.broadcast();
|
||||||
}
|
}
|
||||||
auto& rc = info_of(h)->recv_cache();
|
auto& rc = info_of(h)->recv_cache();
|
||||||
while (1) {
|
while (1) {
|
||||||
// pop a new message
|
// pop a new message
|
||||||
typename queue_t::value_t msg;
|
typename queue_t::value_t msg;
|
||||||
if (!wait_for(info_of(h)->rd_waiter_, [que, &msg] { return !que->pop(msg); }, tm)) {
|
if (!wait_for(info_of(h)->rd_waiter_, [que, &msg] { return !que->pop(msg); }, tm)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
info_of(h)->wt_waiter_.broadcast();
|
info_of(h)->wt_waiter_.broadcast();
|
||||||
if ((info_of(h)->acc() != nullptr) && (msg.conn_ == info_of(h)->cc_id_)) {
|
if ((info_of(h)->acc() != nullptr) && (msg.conn_ == info_of(h)->cc_id_)) {
|
||||||
continue; // ignore message to self
|
continue; // ignore message to self
|
||||||
}
|
}
|
||||||
// msg.remain_ may minus & abs(msg.remain_) < data_length
|
// msg.remain_ may minus & abs(msg.remain_) < data_length
|
||||||
auto remain = static_cast<std::size_t>(static_cast<int>(data_length) + msg.remain_);
|
auto remain = static_cast<std::size_t>(static_cast<std::int32_t>(data_length) + msg.remain_);
|
||||||
// find cache with msg.id_
|
// find cache with msg.id_
|
||||||
auto cac_it = rc.find(msg.id_);
|
auto cac_it = rc.find(msg.id_);
|
||||||
if (cac_it == rc.end()) {
|
if (cac_it == rc.end()) {
|
||||||
if (remain <= data_length) {
|
if (remain <= data_length) {
|
||||||
return make_cache(msg.data_, remain);
|
return make_cache(msg.data_, remain);
|
||||||
}
|
}
|
||||||
if (msg.storage_) {
|
if (msg.storage_) {
|
||||||
auto dat = info_of(h)->acquire_storage(msg.conn_, msg.id_);
|
auto dat = info_of(h)->acquire_storage(msg.conn_, msg.id_);
|
||||||
std::size_t dat_sz = 0;
|
std::size_t dat_sz = 0;
|
||||||
void * buf = shm::get_mem(dat, &dat_sz);
|
void * buf = shm::get_mem(dat, &dat_sz);
|
||||||
if (buf != nullptr && remain <= dat_sz) {
|
if (buf != nullptr && remain <= dat_sz) {
|
||||||
return buff_t { buf, remain, [](void * dat, std::size_t) {
|
return buff_t { buf, remain, [](void * dat, std::size_t) {
|
||||||
shm::release(dat);
|
shm::release(dat);
|
||||||
}, dat };
|
}, dat };
|
||||||
}
|
}
|
||||||
else ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd, shm.size: %zd\n",
|
else ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd, shm.size: %zd\n",
|
||||||
msg.id_, remain, dat_sz);
|
msg.id_, remain, dat_sz);
|
||||||
}
|
}
|
||||||
// gc
|
// gc
|
||||||
if (rc.size() > 1024) {
|
if (rc.size() > 1024) {
|
||||||
std::vector<msg_id_t> need_del;
|
std::vector<msg_id_t> need_del;
|
||||||
for (auto const & pair : rc) {
|
for (auto const & pair : rc) {
|
||||||
auto cmp = std::minmax(msg.id_, pair.first);
|
auto cmp = std::minmax(msg.id_, pair.first);
|
||||||
if (cmp.second - cmp.first > 8192) {
|
if (cmp.second - cmp.first > 8192) {
|
||||||
need_del.push_back(pair.first);
|
need_del.push_back(pair.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto id : need_del) rc.erase(id);
|
for (auto id : need_del) rc.erase(id);
|
||||||
}
|
}
|
||||||
// cache the first message fragment
|
// cache the first message fragment
|
||||||
rc.emplace(msg.id_, cache_t { data_length, make_cache(msg.data_, remain) });
|
rc.emplace(msg.id_, cache_t { data_length, make_cache(msg.data_, remain) });
|
||||||
}
|
}
|
||||||
// has cached before this message
|
// has cached before this message
|
||||||
else {
|
else {
|
||||||
auto& cac = cac_it->second;
|
auto& cac = cac_it->second;
|
||||||
// this is the last message fragment
|
// this is the last message fragment
|
||||||
if (msg.remain_ <= 0) {
|
if (msg.remain_ <= 0) {
|
||||||
cac.append(&(msg.data_), remain);
|
cac.append(&(msg.data_), remain);
|
||||||
// finish this message, erase it from cache
|
// finish this message, erase it from cache
|
||||||
auto buff = std::move(cac.buff_);
|
auto buff = std::move(cac.buff_);
|
||||||
rc.erase(cac_it);
|
rc.erase(cac_it);
|
||||||
return buff;
|
return buff;
|
||||||
}
|
}
|
||||||
// there are remain datas after this message
|
// there are remain datas after this message
|
||||||
cac.append(&(msg.data_), data_length);
|
cac.append(&(msg.data_), data_length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static buff_t try_recv(ipc::handle_t h) {
|
static buff_t try_recv(ipc::handle_t h) {
|
||||||
return recv(h, 0);
|
return recv(h, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}; // detail_impl<Policy>
|
}; // detail_impl<Policy>
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
using policy_t = policy::choose<circ::elem_array, Flag>;
|
using policy_t = policy::choose<circ::elem_array, Flag>;
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
ipc::handle_t chan_impl<Flag>::connect(char const * name, unsigned mode) {
|
ipc::handle_t chan_impl<Flag>::connect(char const * name, unsigned mode) {
|
||||||
return detail_impl<policy_t<Flag>>::connect(name, mode & receiver);
|
return detail_impl<policy_t<Flag>>::connect(name, mode & receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
void chan_impl<Flag>::disconnect(ipc::handle_t h) {
|
void chan_impl<Flag>::disconnect(ipc::handle_t h) {
|
||||||
detail_impl<policy_t<Flag>>::disconnect(h);
|
detail_impl<policy_t<Flag>>::disconnect(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
char const * chan_impl<Flag>::name(ipc::handle_t h) {
|
char const * chan_impl<Flag>::name(ipc::handle_t h) {
|
||||||
auto info = detail_impl<policy_t<Flag>>::info_of(h);
|
auto info = detail_impl<policy_t<Flag>>::info_of(h);
|
||||||
return (info == nullptr) ? nullptr : info->name_.c_str();
|
return (info == nullptr) ? nullptr : info->name_.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
std::size_t chan_impl<Flag>::recv_count(ipc::handle_t h) {
|
std::size_t chan_impl<Flag>::recv_count(ipc::handle_t h) {
|
||||||
return detail_impl<policy_t<Flag>>::recv_count(h);
|
return detail_impl<policy_t<Flag>>::recv_count(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
bool chan_impl<Flag>::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) {
|
bool chan_impl<Flag>::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::size_t tm) {
|
||||||
return detail_impl<policy_t<Flag>>::wait_for_recv(h, r_count, tm);
|
return detail_impl<policy_t<Flag>>::wait_for_recv(h, r_count, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
bool chan_impl<Flag>::send(ipc::handle_t h, void const * data, std::size_t size) {
|
bool chan_impl<Flag>::send(ipc::handle_t h, void const * data, std::size_t size) {
|
||||||
return detail_impl<policy_t<Flag>>::send(h, data, size);
|
return detail_impl<policy_t<Flag>>::send(h, data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
buff_t chan_impl<Flag>::recv(ipc::handle_t h, std::size_t tm) {
|
buff_t chan_impl<Flag>::recv(ipc::handle_t h, std::size_t tm) {
|
||||||
return detail_impl<policy_t<Flag>>::recv(h, tm);
|
return detail_impl<policy_t<Flag>>::recv(h, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
bool chan_impl<Flag>::try_send(ipc::handle_t h, void const * data, std::size_t size) {
|
bool chan_impl<Flag>::try_send(ipc::handle_t h, void const * data, std::size_t size) {
|
||||||
return detail_impl<policy_t<Flag>>::try_send(h, data, size);
|
return detail_impl<policy_t<Flag>>::try_send(h, data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
buff_t chan_impl<Flag>::try_recv(ipc::handle_t h) {
|
buff_t chan_impl<Flag>::try_recv(ipc::handle_t h) {
|
||||||
return detail_impl<policy_t<Flag>>::try_recv(h);
|
return detail_impl<policy_t<Flag>>::try_recv(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
template struct chan_impl<ipc::wr<relat::single, relat::single, trans::unicast >>;
|
template struct chan_impl<ipc::wr<relat::single, relat::single, trans::unicast >>;
|
||||||
template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::unicast >>;
|
template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::unicast >>;
|
||||||
template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::unicast >>;
|
template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::unicast >>;
|
||||||
template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::broadcast>>;
|
template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::broadcast>>;
|
||||||
template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::broadcast>>;
|
template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::broadcast>>;
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
78
src/log.h
78
src/log.h
@ -1,39 +1,39 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename O>
|
template <typename O>
|
||||||
void print(O out, char const * fmt) {
|
void print(O out, char const * fmt) {
|
||||||
std::fprintf(out, "%s", fmt);
|
std::fprintf(out, "%s", fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename O, typename P1, typename... P>
|
template <typename O, typename P1, typename... P>
|
||||||
void print(O out, char const * fmt, P1&& p1, P&&... params) {
|
void print(O out, char const * fmt, P1&& p1, P&&... params) {
|
||||||
std::fprintf(out, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
std::fprintf(out, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
inline void log(char const * fmt) {
|
inline void log(char const * fmt) {
|
||||||
ipc::detail::print(stdout, fmt);
|
ipc::detail::print(stdout, fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename P1, typename... P>
|
template <typename P1, typename... P>
|
||||||
void log(char const * fmt, P1&& p1, P&&... params) {
|
void log(char const * fmt, P1&& p1, P&&... params) {
|
||||||
ipc::detail::print(stdout, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
ipc::detail::print(stdout, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void error(char const * fmt) {
|
inline void error(char const * fmt) {
|
||||||
ipc::detail::print(stderr, fmt);
|
ipc::detail::print(stderr, fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename P1, typename... P>
|
template <typename P1, typename... P>
|
||||||
void error(char const * fmt, P1&& p1, P&&... params) {
|
void error(char const * fmt, P1&& p1, P&&... params) {
|
||||||
ipc::detail::print(stderr, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
ipc::detail::print(stderr, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,431 +1,432 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "rw_lock.h"
|
#include "rw_lock.h"
|
||||||
#include "concept.h"
|
#include "concept.h"
|
||||||
|
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace mem {
|
namespace mem {
|
||||||
|
|
||||||
class static_alloc {
|
class static_alloc {
|
||||||
public:
|
public:
|
||||||
static void swap(static_alloc&) {}
|
static void swap(static_alloc&) {}
|
||||||
static void clear() {}
|
static void clear() {}
|
||||||
|
|
||||||
static void* alloc(std::size_t size) {
|
static void* alloc(std::size_t size) {
|
||||||
return size ? std::malloc(size) : nullptr;
|
return size ? std::malloc(size) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free(void* p) {
|
static void free(void* p) {
|
||||||
std::free(p);
|
std::free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free(void* p, std::size_t /*size*/) {
|
static void free(void* p, std::size_t /*size*/) {
|
||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// Scope allocation -- The destructor will release all allocated blocks.
|
/// Scope allocation -- The destructor will release all allocated blocks.
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept {
|
constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept {
|
||||||
return ((size - 1) & ~(alignment - 1)) + alignment;
|
return ((size - 1) & ~(alignment - 1)) + alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC_CONCEPT_(has_take, take(Type{}));
|
IPC_CONCEPT_(has_take, take(Type{}));
|
||||||
|
|
||||||
class scope_alloc_base {
|
class scope_alloc_base {
|
||||||
protected:
|
protected:
|
||||||
struct block_t {
|
struct block_t {
|
||||||
block_t * next_;
|
block_t * next_;
|
||||||
std::size_t size_;
|
std::size_t size_;
|
||||||
} * head_ = nullptr, * tail_ = nullptr;
|
} * head_ = nullptr, * tail_ = nullptr;
|
||||||
|
|
||||||
enum : std::size_t {
|
enum : std::size_t {
|
||||||
aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t))
|
aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t))
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void swap(scope_alloc_base & rhs) {
|
void swap(scope_alloc_base & rhs) {
|
||||||
std::swap(head_, rhs.head_);
|
std::swap(head_, rhs.head_);
|
||||||
std::swap(tail_, rhs.tail_);
|
std::swap(tail_, rhs.tail_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const noexcept {
|
bool empty() const noexcept {
|
||||||
return head_ == nullptr;
|
return head_ == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void take(scope_alloc_base && rhs) {
|
void take(scope_alloc_base && rhs) {
|
||||||
if (rhs.empty()) return;
|
if (rhs.empty()) return;
|
||||||
if (empty()) swap(rhs);
|
if (empty()) swap(rhs);
|
||||||
else {
|
else {
|
||||||
std::swap(tail_->next_, rhs.head_);
|
std::swap(tail_->next_, rhs.head_);
|
||||||
// rhs.head_ should be nullptr here
|
// rhs.head_ should be nullptr here
|
||||||
tail_ = rhs.tail_;
|
tail_ = rhs.tail_;
|
||||||
rhs.tail_ = nullptr;
|
rhs.tail_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void free(void* /*p*/) {}
|
void free(void* /*p*/) {}
|
||||||
void free(void* /*p*/, std::size_t) {}
|
void free(void* /*p*/, std::size_t) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename AllocP = static_alloc>
|
template <typename AllocP = static_alloc>
|
||||||
class scope_alloc : public detail::scope_alloc_base {
|
class scope_alloc : public detail::scope_alloc_base {
|
||||||
public:
|
public:
|
||||||
using base_t = detail::scope_alloc_base;
|
using base_t = detail::scope_alloc_base;
|
||||||
using alloc_policy = AllocP;
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
alloc_policy alloc_;
|
alloc_policy alloc_;
|
||||||
|
|
||||||
void free_all() {
|
void free_all() {
|
||||||
while (!empty()) {
|
while (!empty()) {
|
||||||
auto curr = head_;
|
auto curr = head_;
|
||||||
head_ = head_->next_;
|
head_ = head_->next_;
|
||||||
alloc_.free(curr, curr->size_);
|
alloc_.free(curr, curr->size_);
|
||||||
}
|
}
|
||||||
// now head_ is nullptr
|
// now head_ is nullptr
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
scope_alloc() = default;
|
scope_alloc() = default;
|
||||||
|
|
||||||
scope_alloc(scope_alloc&& rhs) { swap(rhs); }
|
scope_alloc(scope_alloc&& rhs) { swap(rhs); }
|
||||||
scope_alloc& operator=(scope_alloc&& rhs) { swap(rhs); return (*this); }
|
scope_alloc& operator=(scope_alloc rhs) { swap(rhs); return (*this); }
|
||||||
|
|
||||||
~scope_alloc() { free_all(); }
|
~scope_alloc() { free_all(); }
|
||||||
|
|
||||||
template <typename A>
|
template <typename A>
|
||||||
void set_allocator(A && alc) {
|
void set_allocator(A && alc) {
|
||||||
alloc_ = std::forward<A>(alc);
|
alloc_ = std::forward<A>(alc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap(scope_alloc& rhs) {
|
void swap(scope_alloc& rhs) {
|
||||||
alloc_.swap(rhs.alloc_);
|
alloc_.swap(rhs.alloc_);
|
||||||
base_t::swap(rhs);
|
base_t::swap(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename A = AllocP>
|
template <typename A = AllocP>
|
||||||
auto take(scope_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
auto take(scope_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||||
base_t::take(std::move(rhs));
|
base_t::take(std::move(rhs));
|
||||||
alloc_.take(std::move(rhs.alloc_));
|
alloc_.take(std::move(rhs.alloc_));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename A = AllocP>
|
template <typename A = AllocP>
|
||||||
auto take(scope_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
|
auto take(scope_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
|
||||||
base_t::take(std::move(rhs));
|
base_t::take(std::move(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
free_all();
|
free_all();
|
||||||
tail_ = nullptr;
|
tail_ = nullptr;
|
||||||
alloc_.~alloc_policy();
|
alloc_.~alloc_policy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void* alloc(std::size_t size) {
|
void* alloc(std::size_t size) {
|
||||||
auto curr = static_cast<block_t*>(alloc_.alloc(size += aligned_block_size));
|
auto curr = static_cast<block_t*>(alloc_.alloc(size += aligned_block_size));
|
||||||
curr->next_ = head_;
|
curr->next_ = head_;
|
||||||
curr->size_ = size;
|
curr->size_ = size;
|
||||||
head_ = curr;
|
head_ = curr;
|
||||||
if (tail_ == nullptr) {
|
if (tail_ == nullptr) {
|
||||||
tail_ = curr;
|
tail_ = curr;
|
||||||
}
|
}
|
||||||
return (reinterpret_cast<byte_t*>(curr) + aligned_block_size);
|
return (reinterpret_cast<byte_t*>(curr) + aligned_block_size);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// Fixed-size blocks allocation
|
/// Fixed-size blocks allocation
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class fixed_alloc_base {
|
class fixed_alloc_base {
|
||||||
protected:
|
protected:
|
||||||
std::size_t init_expand_;
|
std::size_t init_expand_;
|
||||||
void * cursor_;
|
void * cursor_;
|
||||||
|
|
||||||
void init(std::size_t init_expand) {
|
void init(std::size_t init_expand) {
|
||||||
init_expand_ = init_expand;
|
init_expand_ = init_expand;
|
||||||
cursor_ = nullptr;
|
cursor_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void** node_p(void* node) {
|
static void** node_p(void* node) {
|
||||||
return reinterpret_cast<void**>(node);
|
return reinterpret_cast<void**>(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto& next(void* node) {
|
static auto& next(void* node) {
|
||||||
return *node_p(node);
|
return *node_p(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void swap(fixed_alloc_base& rhs) {
|
void swap(fixed_alloc_base& rhs) {
|
||||||
std::swap(init_expand_, rhs.init_expand_);
|
std::swap(init_expand_, rhs.init_expand_);
|
||||||
std::swap(cursor_ , rhs.cursor_);
|
std::swap(cursor_ , rhs.cursor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const noexcept {
|
bool empty() const noexcept {
|
||||||
return cursor_ == nullptr;
|
return cursor_ == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void take(fixed_alloc_base && rhs) {
|
void take(fixed_alloc_base && rhs) {
|
||||||
init_expand_ = (ipc::detail::max)(init_expand_, rhs.init_expand_);
|
init_expand_ = (ipc::detail::max)(init_expand_, rhs.init_expand_);
|
||||||
if (rhs.empty()) return;
|
if (rhs.empty()) return;
|
||||||
auto curr = cursor_;
|
auto curr = cursor_;
|
||||||
if (curr != nullptr) while (1) {
|
if (curr != nullptr) while (1) {
|
||||||
auto next_cur = next(curr);
|
auto next_cur = next(curr);
|
||||||
if (next_cur == nullptr) {
|
if (next_cur == nullptr) {
|
||||||
std::swap(next(curr), rhs.cursor_);
|
std::swap(next(curr), rhs.cursor_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// next_cur != nullptr
|
// next_cur != nullptr
|
||||||
else curr = next_cur;
|
else curr = next_cur;
|
||||||
}
|
}
|
||||||
// curr == nullptr, means cursor_ == nullptr
|
// curr == nullptr, means cursor_ == nullptr
|
||||||
else std::swap(cursor_, rhs.cursor_);
|
else std::swap(cursor_, rhs.cursor_);
|
||||||
// rhs.cursor_ must be nullptr
|
// rhs.cursor_ must be nullptr
|
||||||
}
|
}
|
||||||
|
|
||||||
void free(void* p) {
|
void free(void* p) {
|
||||||
if (p == nullptr) return;
|
if (p == nullptr) return;
|
||||||
next(p) = cursor_;
|
next(p) = cursor_;
|
||||||
cursor_ = p;
|
cursor_ = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void free(void* p, std::size_t) {
|
void free(void* p, std::size_t) {
|
||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fixed_expand_policy {
|
} // namespace detail
|
||||||
|
|
||||||
enum : std::size_t {
|
struct fixed_expand_policy {
|
||||||
base_size = sizeof(void*) * 1024 / 2
|
|
||||||
};
|
enum : std::size_t {
|
||||||
|
base_size = sizeof(void*) * 1024
|
||||||
static std::size_t prev(std::size_t& e) {
|
};
|
||||||
if ((e /= 2) == 0) e = 1;
|
|
||||||
return e;
|
constexpr static std::size_t prev(std::size_t e) noexcept {
|
||||||
}
|
return ((e / 2) == 0) ? 1 : (e / 2);
|
||||||
|
}
|
||||||
static std::size_t next(std::size_t& e) {
|
|
||||||
return e *= 2;
|
constexpr static std::size_t next(std::size_t e) noexcept {
|
||||||
}
|
return e * 2;
|
||||||
|
}
|
||||||
template <std::size_t BlockSize>
|
|
||||||
static std::size_t next(std::size_t & e) {
|
template <std::size_t BlockSize>
|
||||||
return ipc::detail::max<std::size_t>(BlockSize, base_size) * next(e);
|
static std::size_t next(std::size_t & e) {
|
||||||
}
|
auto n = ipc::detail::max<std::size_t>(BlockSize, base_size) * e;
|
||||||
};
|
e = next(e);
|
||||||
|
return n;
|
||||||
} // namespace detail
|
}
|
||||||
|
};
|
||||||
template <std::size_t BlockSize,
|
|
||||||
typename AllocP = scope_alloc<>,
|
template <std::size_t BlockSize,
|
||||||
typename ExpandP = detail::fixed_expand_policy>
|
typename AllocP = scope_alloc<>,
|
||||||
class fixed_alloc : public detail::fixed_alloc_base {
|
typename ExpandP = fixed_expand_policy>
|
||||||
public:
|
class fixed_alloc : public detail::fixed_alloc_base {
|
||||||
using base_t = detail::fixed_alloc_base;
|
public:
|
||||||
using alloc_policy = AllocP;
|
using base_t = detail::fixed_alloc_base;
|
||||||
|
using alloc_policy = AllocP;
|
||||||
enum : std::size_t {
|
|
||||||
block_size = (ipc::detail::max)(BlockSize, sizeof(void*))
|
enum : std::size_t {
|
||||||
};
|
block_size = (ipc::detail::max)(BlockSize, sizeof(void*))
|
||||||
|
};
|
||||||
private:
|
|
||||||
alloc_policy alloc_;
|
private:
|
||||||
|
alloc_policy alloc_;
|
||||||
void* try_expand() {
|
|
||||||
if (empty()) {
|
void* try_expand() {
|
||||||
auto size = ExpandP::template next<block_size>(init_expand_);
|
if (empty()) {
|
||||||
auto p = node_p(cursor_ = alloc_.alloc(size));
|
auto size = ExpandP::template next<block_size>(init_expand_);
|
||||||
for (std::size_t i = 0; i < (size / block_size) - 1; ++i)
|
auto p = node_p(cursor_ = alloc_.alloc(size));
|
||||||
p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size);
|
for (std::size_t i = 0; i < (size / block_size) - 1; ++i)
|
||||||
(*p) = nullptr;
|
p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size);
|
||||||
}
|
(*p) = nullptr;
|
||||||
return cursor_;
|
}
|
||||||
}
|
return cursor_;
|
||||||
|
}
|
||||||
public:
|
|
||||||
explicit fixed_alloc(std::size_t init_expand = 1) {
|
public:
|
||||||
init(init_expand);
|
explicit fixed_alloc(std::size_t init_expand = 1) {
|
||||||
}
|
init(init_expand);
|
||||||
|
}
|
||||||
fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { swap(rhs); }
|
|
||||||
fixed_alloc& operator=(fixed_alloc&& rhs) { swap(rhs); return (*this); }
|
fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { swap(rhs); }
|
||||||
|
fixed_alloc& operator=(fixed_alloc rhs) { swap(rhs); return (*this); }
|
||||||
template <typename A>
|
|
||||||
void set_allocator(A && alc) {
|
template <typename A>
|
||||||
alloc_ = std::forward<A>(alc);
|
void set_allocator(A && alc) {
|
||||||
}
|
alloc_ = std::forward<A>(alc);
|
||||||
|
}
|
||||||
void swap(fixed_alloc& rhs) {
|
|
||||||
alloc_.swap(rhs.alloc_);
|
void swap(fixed_alloc& rhs) {
|
||||||
base_t::swap(rhs);
|
alloc_.swap(rhs.alloc_);
|
||||||
}
|
base_t::swap(rhs);
|
||||||
|
}
|
||||||
template <typename A = AllocP>
|
|
||||||
auto take(fixed_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
template <typename A = AllocP>
|
||||||
base_t::take(std::move(rhs));
|
auto take(fixed_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||||
alloc_.take(std::move(rhs.alloc_));
|
base_t::take(std::move(rhs));
|
||||||
}
|
alloc_.take(std::move(rhs.alloc_));
|
||||||
|
}
|
||||||
template <typename A = AllocP>
|
|
||||||
auto take(fixed_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
|
template <typename A = AllocP>
|
||||||
base_t::take(std::move(rhs));
|
auto take(fixed_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
|
||||||
}
|
base_t::take(std::move(rhs));
|
||||||
|
}
|
||||||
void clear() {
|
|
||||||
ExpandP::prev(init_expand_);
|
void clear() {
|
||||||
cursor_ = nullptr;
|
init_expand_ = ExpandP::prev(init_expand_);
|
||||||
alloc_.~alloc_policy();
|
cursor_ = nullptr;
|
||||||
}
|
alloc_.~alloc_policy();
|
||||||
|
}
|
||||||
void* alloc() {
|
|
||||||
void* p = try_expand();
|
void* alloc() {
|
||||||
cursor_ = next(p);
|
void* p = try_expand();
|
||||||
return p;
|
cursor_ = next(p);
|
||||||
}
|
return p;
|
||||||
|
}
|
||||||
void* alloc(std::size_t) {
|
|
||||||
return alloc();
|
void* alloc(std::size_t) {
|
||||||
}
|
return alloc();
|
||||||
};
|
}
|
||||||
|
};
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
/// Variable-size blocks allocation (without alignment)
|
////////////////////////////////////////////////////////////////
|
||||||
////////////////////////////////////////////////////////////////
|
/// Variable-size blocks allocation (without alignment)
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
namespace detail {
|
|
||||||
|
namespace detail {
|
||||||
class variable_alloc_base {
|
|
||||||
protected:
|
class variable_alloc_base {
|
||||||
struct head_t {
|
protected:
|
||||||
std::size_t free_;
|
struct head_t {
|
||||||
} * head_ = nullptr;
|
std::size_t free_;
|
||||||
|
} * head_ = nullptr;
|
||||||
std::map<std::size_t, head_t*, std::greater<std::size_t>> reserves_;
|
|
||||||
|
std::map<std::size_t, head_t*, std::greater<std::size_t>> reserves_;
|
||||||
enum : std::size_t {
|
|
||||||
aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t))
|
enum : std::size_t {
|
||||||
};
|
aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t))
|
||||||
|
};
|
||||||
static byte_t * buffer(head_t* p) {
|
|
||||||
return reinterpret_cast<byte_t*>(p) + aligned_head_size + p->free_;
|
static byte_t * buffer(head_t* p) {
|
||||||
}
|
return reinterpret_cast<byte_t*>(p) + aligned_head_size + p->free_;
|
||||||
|
}
|
||||||
std::size_t remain() const noexcept {
|
|
||||||
return (head_ == nullptr) ? 0 : head_->free_;
|
public:
|
||||||
}
|
void swap(variable_alloc_base& rhs) {
|
||||||
|
std::swap(head_, rhs.head_);
|
||||||
public:
|
}
|
||||||
void swap(variable_alloc_base& rhs) {
|
|
||||||
std::swap(head_, rhs.head_);
|
std::size_t remain() const noexcept {
|
||||||
}
|
return (head_ == nullptr) ? 0 : head_->free_;
|
||||||
|
}
|
||||||
bool empty() const noexcept {
|
|
||||||
return remain() == 0;
|
bool empty() const noexcept {
|
||||||
}
|
return remain() == 0;
|
||||||
|
}
|
||||||
void take(variable_alloc_base && rhs) {
|
|
||||||
if (rhs.remain() > remain()) {
|
void take(variable_alloc_base && rhs) {
|
||||||
if (!empty()) {
|
if (rhs.remain() > remain()) {
|
||||||
reserves_.emplace(head_->free_, head_);
|
if (!empty()) {
|
||||||
}
|
reserves_.emplace(head_->free_, head_);
|
||||||
head_ = rhs.head_;
|
}
|
||||||
}
|
head_ = rhs.head_;
|
||||||
else if (!rhs.empty()) {
|
}
|
||||||
reserves_.emplace(rhs.head_->free_, rhs.head_);
|
else if (!rhs.empty()) {
|
||||||
}
|
reserves_.emplace(rhs.head_->free_, rhs.head_);
|
||||||
rhs.head_ = nullptr;
|
}
|
||||||
}
|
rhs.head_ = nullptr;
|
||||||
|
}
|
||||||
void free(void* /*p*/) {}
|
|
||||||
void free(void* /*p*/, std::size_t) {}
|
void free(void* /*p*/) {}
|
||||||
};
|
void free(void* /*p*/, std::size_t) {}
|
||||||
|
};
|
||||||
} // namespace detail
|
|
||||||
|
} // namespace detail
|
||||||
template <std::size_t ChunkSize = (sizeof(void*) * 1024), typename AllocP = scope_alloc<>>
|
|
||||||
class variable_alloc : public detail::variable_alloc_base {
|
template <std::size_t ChunkSize = (sizeof(void*) * 1024), typename AllocP = scope_alloc<>>
|
||||||
public:
|
class variable_alloc : public detail::variable_alloc_base {
|
||||||
using base_t = detail::variable_alloc_base;
|
public:
|
||||||
using head_t = base_t::head_t;
|
using base_t = detail::variable_alloc_base;
|
||||||
using alloc_policy = AllocP;
|
using head_t = base_t::head_t;
|
||||||
|
using alloc_policy = AllocP;
|
||||||
private:
|
|
||||||
alloc_policy alloc_;
|
private:
|
||||||
|
alloc_policy alloc_;
|
||||||
public:
|
|
||||||
variable_alloc() = default;
|
public:
|
||||||
|
variable_alloc() = default;
|
||||||
variable_alloc(variable_alloc&& rhs) { swap(rhs); }
|
|
||||||
variable_alloc& operator=(variable_alloc&& rhs) { swap(rhs); return (*this); }
|
variable_alloc(variable_alloc&& rhs) { swap(rhs); }
|
||||||
|
variable_alloc& operator=(variable_alloc rhs) { swap(rhs); return (*this); }
|
||||||
template <typename A>
|
|
||||||
void set_allocator(A && alc) {
|
template <typename A>
|
||||||
alloc_ = std::forward<A>(alc);
|
void set_allocator(A && alc) {
|
||||||
}
|
alloc_ = std::forward<A>(alc);
|
||||||
|
}
|
||||||
void swap(variable_alloc& rhs) {
|
|
||||||
alloc_.swap(rhs.alloc_);
|
void swap(variable_alloc& rhs) {
|
||||||
base_t::swap(rhs);
|
alloc_.swap(rhs.alloc_);
|
||||||
}
|
base_t::swap(rhs);
|
||||||
|
}
|
||||||
template <typename A = AllocP>
|
|
||||||
auto take(variable_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
template <typename A = AllocP>
|
||||||
base_t::take(std::move(rhs));
|
auto take(variable_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||||
alloc_.take(std::move(rhs.alloc_));
|
base_t::take(std::move(rhs));
|
||||||
}
|
alloc_.take(std::move(rhs.alloc_));
|
||||||
|
}
|
||||||
template <typename A = AllocP>
|
|
||||||
auto take(variable_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
|
template <typename A = AllocP>
|
||||||
base_t::take(std::move(rhs));
|
auto take(variable_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
|
||||||
}
|
base_t::take(std::move(rhs));
|
||||||
|
}
|
||||||
void clear() {
|
|
||||||
alloc_.~alloc_policy();
|
void clear() {
|
||||||
}
|
alloc_.~alloc_policy();
|
||||||
|
}
|
||||||
void* alloc(std::size_t size) {
|
|
||||||
if (size >= (ChunkSize - aligned_head_size)) {
|
void* alloc(std::size_t size) {
|
||||||
return alloc_.alloc(size);
|
if (size >= ChunkSize) {
|
||||||
}
|
return alloc_.alloc(size);
|
||||||
if (remain() < size) {
|
}
|
||||||
auto it = reserves_.begin();
|
if (remain() < size) {
|
||||||
if ((it == reserves_.end()) || (it->first < size)) {
|
auto it = reserves_.begin();
|
||||||
head_ = static_cast<head_t*>(alloc_.alloc(ChunkSize));
|
if ((it == reserves_.end()) || (it->first < size)) {
|
||||||
head_->free_ = ChunkSize - aligned_head_size - size;
|
head_ = static_cast<head_t*>(alloc_.alloc(ChunkSize + aligned_head_size));
|
||||||
}
|
head_->free_ = ChunkSize - size;
|
||||||
else {
|
}
|
||||||
auto temp = it->second;
|
else {
|
||||||
temp->free_ -= size;
|
auto temp = it->second;
|
||||||
reserves_.erase(it);
|
temp->free_ -= size;
|
||||||
if (remain() < temp->free_) {
|
reserves_.erase(it);
|
||||||
head_ = temp;
|
if (remain() < temp->free_) {
|
||||||
}
|
head_ = temp;
|
||||||
else return base_t::buffer(temp);
|
}
|
||||||
}
|
else return base_t::buffer(temp);
|
||||||
}
|
}
|
||||||
// size shouldn't be 0 here, otherwise behavior is undefined
|
}
|
||||||
else head_->free_ -= size;
|
// size shouldn't be 0 here, otherwise behavior is undefined
|
||||||
return base_t::buffer(head_);
|
else head_->free_ -= size;
|
||||||
}
|
return base_t::buffer(head_);
|
||||||
};
|
}
|
||||||
|
};
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
|
|||||||
142
src/memory/resource.h
Executable file → Normal file
142
src/memory/resource.h
Executable file → Normal file
@ -1,68 +1,74 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
|
|
||||||
#include "memory/alloc.h"
|
#include "memory/alloc.h"
|
||||||
#include "memory/wrapper.h"
|
#include "memory/wrapper.h"
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace mem {
|
namespace mem {
|
||||||
|
|
||||||
using chunk_variable_alloc = variable_alloc<sizeof(void*) * 256 * 1024 /* 2MB(x64) */>;
|
namespace detail {
|
||||||
|
|
||||||
template <std::size_t Size>
|
using chunk_variable_alloc =
|
||||||
using static_async_fixed = static_wrapper<async_wrapper<fixed_alloc<Size, chunk_variable_alloc>>>;
|
static_wrapper<async_wrapper<variable_alloc<
|
||||||
|
sizeof(void*) * 1024 * 256 /* 2MB(x64) */ >>>;
|
||||||
using async_pool_alloc = static_alloc/*variable_wrapper<static_async_fixed>*/;
|
|
||||||
|
template <std::size_t Size>
|
||||||
template <typename T>
|
using static_async_fixed =
|
||||||
using allocator = allocator_wrapper<T, async_pool_alloc>;
|
static_wrapper<async_wrapper<fixed_alloc<
|
||||||
|
Size, chunk_variable_alloc >>>;
|
||||||
} // namespace mem
|
|
||||||
|
using async_pool_alloc = /*static_alloc*/variable_wrapper<static_async_fixed>;
|
||||||
namespace {
|
|
||||||
|
template <typename T>
|
||||||
constexpr char const * pf(int) { return "%d" ; }
|
using allocator = allocator_wrapper<T, async_pool_alloc>;
|
||||||
constexpr char const * pf(long) { return "%ld" ; }
|
|
||||||
constexpr char const * pf(long long) { return "%lld"; }
|
} // namespace mem
|
||||||
constexpr char const * pf(unsigned int) { return "%u" ; }
|
|
||||||
constexpr char const * pf(unsigned long) { return "%lu" ; }
|
namespace {
|
||||||
constexpr char const * pf(unsigned long long) { return "%llu"; }
|
|
||||||
constexpr char const * pf(float) { return "%f" ; }
|
constexpr char const * pf(int) { return "%d" ; }
|
||||||
constexpr char const * pf(double) { return "%f" ; }
|
constexpr char const * pf(long) { return "%ld" ; }
|
||||||
constexpr char const * pf(long double) { return "%Lf" ; }
|
constexpr char const * pf(long long) { return "%lld"; }
|
||||||
|
constexpr char const * pf(unsigned int) { return "%u" ; }
|
||||||
} // internal-linkage
|
constexpr char const * pf(unsigned long) { return "%lu" ; }
|
||||||
|
constexpr char const * pf(unsigned long long) { return "%llu"; }
|
||||||
template <typename Key, typename T>
|
constexpr char const * pf(float) { return "%f" ; }
|
||||||
using unordered_map = std::unordered_map<
|
constexpr char const * pf(double) { return "%f" ; }
|
||||||
Key, T, std::hash<Key>, std::equal_to<Key>, ipc::mem::allocator<std::pair<const Key, T>>
|
constexpr char const * pf(long double) { return "%Lf" ; }
|
||||||
>;
|
|
||||||
|
} // internal-linkage
|
||||||
template <typename Char>
|
|
||||||
using basic_string = std::basic_string<
|
template <typename Key, typename T>
|
||||||
Char, std::char_traits<Char>, ipc::mem::allocator<Char>
|
using unordered_map = std::unordered_map<
|
||||||
>;
|
Key, T, std::hash<Key>, std::equal_to<Key>, ipc::mem::allocator<std::pair<const Key, T>>
|
||||||
|
>;
|
||||||
using string = basic_string<char>;
|
|
||||||
using wstring = basic_string<wchar_t>;
|
template <typename Char>
|
||||||
|
using basic_string = std::basic_string<
|
||||||
template <typename T>
|
Char, std::char_traits<Char>, ipc::mem::allocator<Char>
|
||||||
ipc::string to_string(T val) {
|
>;
|
||||||
char buf[std::numeric_limits<T>::digits10 + 1] {};
|
|
||||||
if (std::snprintf(buf, sizeof(buf), pf(val), val) > 0) {
|
using string = basic_string<char>;
|
||||||
return buf;
|
using wstring = basic_string<wchar_t>;
|
||||||
}
|
|
||||||
return {};
|
template <typename T>
|
||||||
}
|
ipc::string to_string(T val) {
|
||||||
|
char buf[std::numeric_limits<T>::digits10 + 1] {};
|
||||||
} // namespace ipc
|
if (std::snprintf(buf, sizeof(buf), pf(val), val) > 0) {
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,379 +1,403 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "rw_lock.h"
|
#include "rw_lock.h"
|
||||||
#include "tls_pointer.h"
|
#include "tls_pointer.h"
|
||||||
#include "concept.h"
|
#include "concept.h"
|
||||||
|
|
||||||
#include "memory/alloc.h"
|
#include "memory/alloc.h"
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace mem {
|
namespace mem {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// The allocator wrapper class for STL
|
/// The allocator wrapper class for STL
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template <typename T, typename AllocP>
|
template <typename T, typename AllocP>
|
||||||
class allocator_wrapper {
|
class allocator_wrapper {
|
||||||
|
|
||||||
template <typename U, typename AllocU>
|
template <typename U, typename AllocU>
|
||||||
friend class allocator_wrapper;
|
friend class allocator_wrapper;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// type definitions
|
// type definitions
|
||||||
typedef T value_type;
|
typedef T value_type;
|
||||||
typedef value_type* pointer;
|
typedef value_type* pointer;
|
||||||
typedef const value_type* const_pointer;
|
typedef const value_type* const_pointer;
|
||||||
typedef value_type& reference;
|
typedef value_type& reference;
|
||||||
typedef const value_type& const_reference;
|
typedef const value_type& const_reference;
|
||||||
typedef std::size_t size_type;
|
typedef std::size_t size_type;
|
||||||
typedef std::ptrdiff_t difference_type;
|
typedef std::ptrdiff_t difference_type;
|
||||||
typedef AllocP alloc_policy;
|
typedef AllocP alloc_policy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
alloc_policy alloc_;
|
alloc_policy alloc_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
allocator_wrapper(void) noexcept = default;
|
allocator_wrapper(void) noexcept = default;
|
||||||
|
|
||||||
allocator_wrapper(const allocator_wrapper<T, AllocP>& rhs) noexcept
|
allocator_wrapper(const allocator_wrapper<T, AllocP>& rhs) noexcept
|
||||||
: alloc_(rhs.alloc_)
|
: alloc_(rhs.alloc_)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
allocator_wrapper(const allocator_wrapper<U, AllocP>& rhs) noexcept
|
allocator_wrapper(const allocator_wrapper<U, AllocP>& rhs) noexcept
|
||||||
: alloc_(rhs.alloc_)
|
: alloc_(rhs.alloc_)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
allocator_wrapper(allocator_wrapper<T, AllocP>&& rhs) noexcept
|
allocator_wrapper(allocator_wrapper<T, AllocP>&& rhs) noexcept
|
||||||
: alloc_(std::move(rhs.alloc_))
|
: alloc_(std::move(rhs.alloc_))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
allocator_wrapper(allocator_wrapper<U, AllocP>&& rhs) noexcept
|
allocator_wrapper(allocator_wrapper<U, AllocP>&& rhs) noexcept
|
||||||
: alloc_(std::move(rhs.alloc_))
|
: alloc_(std::move(rhs.alloc_))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
allocator_wrapper(const AllocP& rhs) noexcept
|
allocator_wrapper(const AllocP& rhs) noexcept
|
||||||
: alloc_(rhs)
|
: alloc_(rhs)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
allocator_wrapper(AllocP&& rhs) noexcept
|
allocator_wrapper(AllocP&& rhs) noexcept
|
||||||
: alloc_(std::move(rhs))
|
: alloc_(std::move(rhs))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// the other type of std_allocator
|
// the other type of std_allocator
|
||||||
template <typename U>
|
template <typename U>
|
||||||
struct rebind { typedef allocator_wrapper<U, AllocP> other; };
|
struct rebind { typedef allocator_wrapper<U, AllocP> other; };
|
||||||
|
|
||||||
constexpr size_type max_size(void) const noexcept {
|
constexpr size_type max_size(void) const noexcept {
|
||||||
return (std::numeric_limits<size_type>::max)() / sizeof(T);
|
return (std::numeric_limits<size_type>::max)() / sizeof(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
pointer allocate(size_type count) noexcept {
|
pointer allocate(size_type count) noexcept {
|
||||||
if (count == 0) return nullptr;
|
if (count == 0) return nullptr;
|
||||||
if (count > this->max_size()) return nullptr;
|
if (count > this->max_size()) return nullptr;
|
||||||
return static_cast<pointer>(alloc_.alloc(count * sizeof(T)));
|
return static_cast<pointer>(alloc_.alloc(count * sizeof(T)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void deallocate(pointer p, size_type count) noexcept {
|
void deallocate(pointer p, size_type count) noexcept {
|
||||||
alloc_.free(p, count * sizeof(T));
|
alloc_.free(p, count * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... P>
|
template <typename... P>
|
||||||
static void construct(pointer p, P&&... params) {
|
static void construct(pointer p, P&&... params) {
|
||||||
::new (static_cast<void*>(p)) T(std::forward<P>(params)...);
|
::new (static_cast<void*>(p)) T(std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroy(pointer p) {
|
static void destroy(pointer p) {
|
||||||
p->~T();
|
p->~T();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class AllocP>
|
template <class AllocP>
|
||||||
class allocator_wrapper<void, AllocP> {
|
class allocator_wrapper<void, AllocP> {
|
||||||
public:
|
public:
|
||||||
// type definitions
|
// type definitions
|
||||||
typedef void value_type;
|
typedef void value_type;
|
||||||
typedef value_type* pointer;
|
typedef value_type* pointer;
|
||||||
typedef const value_type* const_pointer;
|
typedef const value_type* const_pointer;
|
||||||
typedef std::size_t size_type;
|
typedef std::size_t size_type;
|
||||||
typedef std::ptrdiff_t difference_type;
|
typedef std::ptrdiff_t difference_type;
|
||||||
typedef AllocP alloc_policy;
|
typedef AllocP alloc_policy;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename U, class AllocP>
|
template <typename T, typename U, class AllocP>
|
||||||
constexpr bool operator==(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
|
constexpr bool operator==(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename U, class AllocP>
|
template <typename T, typename U, class AllocP>
|
||||||
constexpr bool operator!=(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
|
constexpr bool operator!=(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// Thread-safe allocation wrapper
|
/// Thread-safe allocation wrapper
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template <typename AllocP>
|
template <typename AllocP>
|
||||||
class default_alloc_recoverer {
|
class default_alloc_recycler {
|
||||||
public:
|
public:
|
||||||
using alloc_policy = AllocP;
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ipc::spin_lock master_lock_;
|
ipc::spin_lock master_lock_;
|
||||||
std::vector<alloc_policy> master_allocs_;
|
std::vector<alloc_policy> master_allocs_;
|
||||||
|
|
||||||
public:
|
IPC_CONCEPT_(has_remain, remain());
|
||||||
void swap(default_alloc_recoverer& rhs) {
|
IPC_CONCEPT_(has_empty , empty());
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
|
||||||
master_allocs_.swap(rhs.master_allocs_);
|
public:
|
||||||
}
|
void swap(default_alloc_recycler& rhs) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
void clear() {
|
master_allocs_.swap(rhs.master_allocs_);
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
}
|
||||||
master_allocs_.clear();
|
|
||||||
}
|
void clear() {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
void try_recover(alloc_policy & alc) {
|
master_allocs_.clear();
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
}
|
||||||
if (!master_allocs_.empty()) {
|
|
||||||
alc.swap(master_allocs_.back());
|
void try_recover(alloc_policy & alc) {
|
||||||
master_allocs_.pop_back();
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
}
|
if (!master_allocs_.empty()) {
|
||||||
}
|
alc.swap(master_allocs_.back());
|
||||||
|
master_allocs_.pop_back();
|
||||||
template <typename A = AllocP>
|
}
|
||||||
auto try_replenish(alloc_policy & alc) -> ipc::require<detail::has_take<A>::value> {
|
}
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
|
||||||
if (!master_allocs_.empty()) {
|
template <typename A = AllocP>
|
||||||
alc.take(std::move(master_allocs_.back()));
|
auto try_replenish(alloc_policy & alc, std::size_t size)
|
||||||
master_allocs_.pop_back();
|
-> ipc::require<detail::has_take<A>::value && has_remain<A>::value> {
|
||||||
}
|
if (alc.remain() >= size) return;
|
||||||
}
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
|
if (!master_allocs_.empty()) {
|
||||||
template <typename A = AllocP>
|
alc.take(std::move(master_allocs_.back()));
|
||||||
constexpr auto try_replenish(alloc_policy & /*alc*/) noexcept
|
master_allocs_.pop_back();
|
||||||
-> ipc::require<!detail::has_take<A>::value> {}
|
}
|
||||||
|
}
|
||||||
void collect(alloc_policy && alc) {
|
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
template <typename A = AllocP>
|
||||||
master_allocs_.emplace_back(std::move(alc));
|
auto try_replenish(alloc_policy & alc, std::size_t /*size*/)
|
||||||
}
|
-> ipc::require<detail::has_take<A>::value && !has_remain<A>::value && has_empty<A>::value> {
|
||||||
};
|
if (!alc.empty()) return;
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
template <typename AllocP,
|
if (!master_allocs_.empty()) {
|
||||||
template <typename> class RecovererP = default_alloc_recoverer>
|
alc.take(std::move(master_allocs_.back()));
|
||||||
class async_wrapper {
|
master_allocs_.pop_back();
|
||||||
public:
|
}
|
||||||
using alloc_policy = AllocP;
|
}
|
||||||
|
|
||||||
private:
|
template <typename A = AllocP>
|
||||||
RecovererP<alloc_policy> recoverer_;
|
constexpr auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) noexcept
|
||||||
|
-> ipc::require<!detail::has_take<A>::value || (!has_remain<A>::value && !has_empty<A>::value)> {}
|
||||||
class alloc_proxy : public AllocP {
|
|
||||||
async_wrapper * w_ = nullptr;
|
void collect(alloc_policy && alc) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
IPC_CONCEPT_(has_empty, empty());
|
master_allocs_.emplace_back(std::move(alc));
|
||||||
|
}
|
||||||
public:
|
};
|
||||||
alloc_proxy(alloc_proxy && rhs)
|
|
||||||
: AllocP(std::move(rhs))
|
template <typename AllocP>
|
||||||
{}
|
class empty_alloc_recycler {
|
||||||
|
public:
|
||||||
alloc_proxy(async_wrapper* w)
|
using alloc_policy = AllocP;
|
||||||
: AllocP(), w_(w) {
|
|
||||||
if (w_ == nullptr) return;
|
constexpr static void swap(empty_alloc_recycler&) noexcept {}
|
||||||
w_->recoverer_.try_recover(*this);
|
constexpr static void clear() noexcept {}
|
||||||
}
|
constexpr static void try_recover(alloc_policy&) noexcept {}
|
||||||
|
constexpr static auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
||||||
~alloc_proxy() {
|
constexpr static void collect(alloc_policy&&) noexcept {}
|
||||||
if (w_ == nullptr) return;
|
};
|
||||||
w_->recoverer_.collect(std::move(*this));
|
|
||||||
}
|
template <typename AllocP,
|
||||||
|
template <typename> class RecyclerP = default_alloc_recycler>
|
||||||
template <typename A = AllocP>
|
class async_wrapper {
|
||||||
auto alloc(std::size_t size) -> ipc::require<has_empty<A>::value, void*> {
|
public:
|
||||||
auto p = AllocP::alloc(size);
|
using alloc_policy = AllocP;
|
||||||
if (AllocP::empty() && (w_ != nullptr)) {
|
|
||||||
w_->recoverer_.try_replenish(*this);
|
private:
|
||||||
}
|
RecyclerP<alloc_policy> recycler_;
|
||||||
return p;
|
|
||||||
}
|
class alloc_proxy : public AllocP {
|
||||||
|
async_wrapper * w_ = nullptr;
|
||||||
template <typename A = AllocP>
|
|
||||||
auto alloc(std::size_t size) -> ipc::require<!has_empty<A>::value, void*> {
|
public:
|
||||||
return AllocP::alloc(size);
|
alloc_proxy(alloc_proxy && rhs)
|
||||||
}
|
: AllocP(std::move(rhs))
|
||||||
};
|
{}
|
||||||
|
|
||||||
friend class alloc_proxy;
|
alloc_proxy(async_wrapper* w)
|
||||||
|
: AllocP(), w_(w) {
|
||||||
auto& get_alloc() {
|
if (w_ == nullptr) return;
|
||||||
static tls::pointer<alloc_proxy> tls_alc;
|
w_->recycler_.try_recover(*this);
|
||||||
return *tls_alc.create(this);
|
}
|
||||||
}
|
|
||||||
|
~alloc_proxy() {
|
||||||
public:
|
if (w_ == nullptr) return;
|
||||||
void swap(async_wrapper& rhs) {
|
w_->recycler_.collect(std::move(*this));
|
||||||
recoverer_.swap(rhs.recoverer_);
|
}
|
||||||
}
|
|
||||||
|
auto alloc(std::size_t size) {
|
||||||
void clear() {
|
if (w_ != nullptr) {
|
||||||
recoverer_.clear();
|
w_->recycler_.try_replenish(*this, size);
|
||||||
}
|
}
|
||||||
|
return AllocP::alloc(size);
|
||||||
void* alloc(std::size_t size) {
|
}
|
||||||
return get_alloc().alloc(size);
|
};
|
||||||
}
|
|
||||||
|
friend class alloc_proxy;
|
||||||
void free(void* p, std::size_t size) {
|
|
||||||
get_alloc().free(p, size);
|
auto& get_alloc() {
|
||||||
}
|
static tls::pointer<alloc_proxy> tls_alc;
|
||||||
};
|
return *tls_alc.create(this);
|
||||||
|
}
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
/// Thread-safe allocation wrapper (with spin_lock)
|
public:
|
||||||
////////////////////////////////////////////////////////////////
|
void swap(async_wrapper& rhs) {
|
||||||
|
recycler_.swap(rhs.recycler_);
|
||||||
template <typename AllocP, typename MutexT = ipc::spin_lock>
|
}
|
||||||
class sync_wrapper {
|
|
||||||
public:
|
void clear() {
|
||||||
using alloc_policy = AllocP;
|
recycler_.clear();
|
||||||
using mutex_type = MutexT;
|
}
|
||||||
|
|
||||||
private:
|
void* alloc(std::size_t size) {
|
||||||
mutex_type lock_;
|
return get_alloc().alloc(size);
|
||||||
alloc_policy alloc_;
|
}
|
||||||
|
|
||||||
public:
|
void free(void* p, std::size_t size) {
|
||||||
void swap(sync_wrapper& rhs) {
|
get_alloc().free(p, size);
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
}
|
||||||
alloc_.swap(rhs.alloc_);
|
};
|
||||||
}
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
void clear() {
|
/// Thread-safe allocation wrapper (with spin_lock)
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
////////////////////////////////////////////////////////////////
|
||||||
alloc_.~alloc_policy();
|
|
||||||
}
|
template <typename AllocP, typename MutexT = ipc::spin_lock>
|
||||||
|
class sync_wrapper {
|
||||||
void* alloc(std::size_t size) {
|
public:
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
using alloc_policy = AllocP;
|
||||||
return alloc_.alloc(size);
|
using mutex_type = MutexT;
|
||||||
}
|
|
||||||
|
private:
|
||||||
void free(void* p, std::size_t size) {
|
mutex_type lock_;
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
alloc_policy alloc_;
|
||||||
alloc_.free(p, size);
|
|
||||||
}
|
public:
|
||||||
};
|
void swap(sync_wrapper& rhs) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
////////////////////////////////////////////////////////////////
|
alloc_.swap(rhs.alloc_);
|
||||||
/// Static allocation wrapper
|
}
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
void clear() {
|
||||||
template <typename AllocP>
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
class static_wrapper {
|
alloc_.~alloc_policy();
|
||||||
public:
|
}
|
||||||
using alloc_policy = AllocP;
|
|
||||||
|
void* alloc(std::size_t size) {
|
||||||
static alloc_policy& instance() {
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
static alloc_policy alloc;
|
return alloc_.alloc(size);
|
||||||
return alloc;
|
}
|
||||||
}
|
|
||||||
|
void free(void* p, std::size_t size) {
|
||||||
static void swap(static_wrapper&) {}
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
|
alloc_.free(p, size);
|
||||||
static void clear() {
|
}
|
||||||
instance().clear();
|
};
|
||||||
}
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
static void* alloc(std::size_t size) {
|
/// Static allocation wrapper
|
||||||
return instance().alloc(size);
|
////////////////////////////////////////////////////////////////
|
||||||
}
|
|
||||||
|
template <typename AllocP>
|
||||||
static void free(void* p, std::size_t size) {
|
class static_wrapper {
|
||||||
instance().free(p, size);
|
public:
|
||||||
}
|
using alloc_policy = AllocP;
|
||||||
};
|
|
||||||
|
static alloc_policy& instance() {
|
||||||
////////////////////////////////////////////////////////////////
|
static alloc_policy alloc;
|
||||||
/// Variable memory allocation wrapper
|
return alloc;
|
||||||
////////////////////////////////////////////////////////////////
|
}
|
||||||
|
|
||||||
template <std::size_t BaseSize = sizeof(void*)>
|
static void swap(static_wrapper&) {}
|
||||||
struct default_mapping_policy {
|
|
||||||
|
static void clear() {
|
||||||
enum : std::size_t {
|
instance().clear();
|
||||||
base_size = BaseSize,
|
}
|
||||||
classes_size = 32
|
|
||||||
};
|
static void* alloc(std::size_t size) {
|
||||||
|
return instance().alloc(size);
|
||||||
static const std::size_t table[classes_size];
|
}
|
||||||
|
|
||||||
constexpr static std::size_t classify(std::size_t size) {
|
static void free(void* p, std::size_t size) {
|
||||||
return (((size - 1) / base_size) < classes_size) ?
|
instance().free(p, size);
|
||||||
// always uses default_mapping_policy<sizeof(void*)>::table
|
}
|
||||||
default_mapping_policy<>::table[((size - 1) / base_size)] : classes_size;
|
};
|
||||||
}
|
|
||||||
};
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// Variable memory allocation wrapper
|
||||||
template <std::size_t B>
|
////////////////////////////////////////////////////////////////
|
||||||
const std::size_t default_mapping_policy<B>::table[default_mapping_policy<B>::classes_size] = {
|
|
||||||
/* 1 - 8 ~ 32 */
|
template <std::size_t BaseSize = sizeof(void*)>
|
||||||
0 , 1 , 2 , 3 ,
|
struct default_mapping_policy {
|
||||||
/* 2 - 48 ~ 256 */
|
|
||||||
5 , 5 , 7 , 7 , 9 , 9 , 11, 11, 13, 13, 15, 15, 17, 17,
|
enum : std::size_t {
|
||||||
19, 19, 21, 21, 23, 23, 25, 25, 27, 27, 29, 29, 31, 31
|
base_size = BaseSize,
|
||||||
};
|
classes_size = 32
|
||||||
|
};
|
||||||
template <template <std::size_t> class Fixed,
|
|
||||||
typename MappingP = default_mapping_policy<>,
|
static const std::size_t table[classes_size];
|
||||||
typename StaticAlloc = mem::static_alloc>
|
|
||||||
class variable_wrapper {
|
IPC_CONSTEXPR_ static std::size_t classify(std::size_t size) noexcept {
|
||||||
|
auto index = (size - 1) / base_size;
|
||||||
template <typename F>
|
return (index < classes_size) ?
|
||||||
constexpr static auto choose(std::size_t size, F&& f) {
|
// always uses default_mapping_policy<sizeof(void*)>::table
|
||||||
return ipc::detail::static_switch<MappingP::classes_size>(MappingP::classify(size), [&f](auto index) {
|
default_mapping_policy<>::table[index] : classes_size;
|
||||||
return f(Fixed<(decltype(index)::value + 1) * MappingP::base_size>{});
|
}
|
||||||
}, [&f] {
|
|
||||||
return f(StaticAlloc{});
|
constexpr static std::size_t block_size(std::size_t value) noexcept {
|
||||||
});
|
return (value + 1) * base_size;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
public:
|
|
||||||
static void swap(variable_wrapper&) {}
|
template <std::size_t B>
|
||||||
|
const std::size_t default_mapping_policy<B>::table[default_mapping_policy<B>::classes_size] = {
|
||||||
static void clear() {
|
/* 1 - 8 ~ 32 */
|
||||||
ipc::detail::static_for<MappingP::classes_size>([](auto index) {
|
0 , 1 , 2 , 3 ,
|
||||||
Fixed<(decltype(index)::value + 1) * MappingP::base_size>::clear();
|
/* 2 - 48 ~ 256 */
|
||||||
});
|
5 , 5 , 7 , 7 , 9 , 9 , 11, 11, 13, 13, 15, 15, 17, 17,
|
||||||
StaticAlloc::clear();
|
19, 19, 21, 21, 23, 23, 25, 25, 27, 27, 29, 29, 31, 31
|
||||||
}
|
};
|
||||||
|
|
||||||
static void* alloc(std::size_t size) {
|
template <template <std::size_t> class Fixed,
|
||||||
return choose(size, [size](auto&& alc) { return alc.alloc(size); });
|
typename MappingP = default_mapping_policy<>,
|
||||||
}
|
typename StaticAlloc = mem::static_alloc>
|
||||||
|
class variable_wrapper {
|
||||||
static void free(void* p, std::size_t size) {
|
|
||||||
choose(size, [p, size](auto&& alc) { alc.free(p, size); });
|
template <typename F>
|
||||||
}
|
constexpr static auto choose(std::size_t size, F&& f) {
|
||||||
};
|
return ipc::detail::static_switch<MappingP::classes_size>(MappingP::classify(size), [&f](auto index) {
|
||||||
|
return f(Fixed<MappingP::block_size(decltype(index)::value)>{});
|
||||||
} // namespace mem
|
}, [&f] {
|
||||||
} // namespace ipc
|
return f(StaticAlloc{});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void swap(variable_wrapper&) {}
|
||||||
|
|
||||||
|
static void clear() {
|
||||||
|
ipc::detail::static_for<MappingP::classes_size>([](auto index) {
|
||||||
|
Fixed<MappingP::block_size(decltype(index)::value)>::clear();
|
||||||
|
});
|
||||||
|
StaticAlloc::clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* alloc(std::size_t size) {
|
||||||
|
return choose(size, [size](auto&& alc) { return alc.alloc(size); });
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free(void* p, std::size_t size) {
|
||||||
|
choose(size, [p, size](auto&& alc) { alc.free(p, size); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
|
|||||||
134
src/pimpl.h
134
src/pimpl.h
@ -1,67 +1,67 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "concept.h"
|
#include "concept.h"
|
||||||
#include "pool_alloc.h"
|
#include "pool_alloc.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
// pimpl small object optimization helpers
|
// pimpl small object optimization helpers
|
||||||
|
|
||||||
template <typename T, typename R = T*>
|
template <typename T, typename R = T*>
|
||||||
using IsImplComfortable = ipc::require<(sizeof(T) <= sizeof(T*)), R>;
|
using IsImplComfortable = ipc::require<(sizeof(T) <= sizeof(T*)), R>;
|
||||||
|
|
||||||
template <typename T, typename R = T*>
|
template <typename T, typename R = T*>
|
||||||
using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>;
|
using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>;
|
||||||
|
|
||||||
template <typename T, typename... P>
|
template <typename T, typename... P>
|
||||||
constexpr auto make_impl(P&&... params) -> IsImplComfortable<T> {
|
constexpr auto make_impl(P&&... params) -> IsImplComfortable<T> {
|
||||||
T* buf {};
|
T* buf {};
|
||||||
::new (&buf) T { std::forward<P>(params)... };
|
::new (&buf) T { std::forward<P>(params)... };
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto impl(T* const (& p)) -> IsImplComfortable<T> {
|
constexpr auto impl(T* const (& p)) -> IsImplComfortable<T> {
|
||||||
return reinterpret_cast<T*>(&const_cast<char &>(reinterpret_cast<char const &>(p)));
|
return reinterpret_cast<T*>(&const_cast<char &>(reinterpret_cast<char const &>(p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto clear_impl(T* p) -> IsImplComfortable<T, void> {
|
constexpr auto clear_impl(T* p) -> IsImplComfortable<T, void> {
|
||||||
if (p != nullptr) impl(p)->~T();
|
if (p != nullptr) impl(p)->~T();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... P>
|
template <typename T, typename... P>
|
||||||
constexpr auto make_impl(P&&... params) -> IsImplUncomfortable<T> {
|
constexpr auto make_impl(P&&... params) -> IsImplUncomfortable<T> {
|
||||||
return mem::alloc<T>(std::forward<P>(params)...);
|
return mem::alloc<T>(std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto clear_impl(T* p) -> IsImplUncomfortable<T, void> {
|
constexpr auto clear_impl(T* p) -> IsImplUncomfortable<T, void> {
|
||||||
mem::free(p);
|
mem::free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto impl(T* const (& p)) -> IsImplUncomfortable<T> {
|
constexpr auto impl(T* const (& p)) -> IsImplUncomfortable<T> {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct pimpl {
|
struct pimpl {
|
||||||
template <typename... P>
|
template <typename... P>
|
||||||
constexpr static T* make(P&&... params) {
|
constexpr static T* make(P&&... params) {
|
||||||
return make_impl<T>(std::forward<P>(params)...);
|
return make_impl<T>(std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __cplusplus >= 201703L
|
#if __cplusplus >= 201703L
|
||||||
constexpr void clear() {
|
constexpr void clear() {
|
||||||
#else /*__cplusplus < 201703L*/
|
#else /*__cplusplus < 201703L*/
|
||||||
void clear() {
|
void clear() {
|
||||||
#endif/*__cplusplus < 201703L*/
|
#endif/*__cplusplus < 201703L*/
|
||||||
clear_impl(static_cast<T*>(const_cast<pimpl*>(this)));
|
clear_impl(static_cast<T*>(const_cast<pimpl*>(this)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,149 +1,149 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "export.h"
|
#include "export.h"
|
||||||
|
|
||||||
// pre-defined
|
// pre-defined
|
||||||
|
|
||||||
#ifdef IPC_UNUSED_
|
#ifdef IPC_UNUSED_
|
||||||
# error "IPC_UNUSED_ has been defined."
|
# error "IPC_UNUSED_ has been defined."
|
||||||
#endif
|
#endif
|
||||||
#ifdef IPC_FALLTHROUGH_
|
#ifdef IPC_FALLTHROUGH_
|
||||||
# error "IPC_FALLTHROUGH_ has been defined."
|
# error "IPC_FALLTHROUGH_ has been defined."
|
||||||
#endif
|
#endif
|
||||||
#ifdef IPC_STBIND_
|
#ifdef IPC_STBIND_
|
||||||
# error "IPC_STBIND_ has been defined."
|
# error "IPC_STBIND_ has been defined."
|
||||||
#endif
|
#endif
|
||||||
#ifdef IPC_CONSTEXPR_
|
#ifdef IPC_CONSTEXPR_
|
||||||
# error "IPC_CONSTEXPR_ has been defined."
|
# error "IPC_CONSTEXPR_ has been defined."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __cplusplus >= 201703L
|
#if __cplusplus >= 201703L
|
||||||
|
|
||||||
#define IPC_UNUSED_ [[maybe_unused]]
|
#define IPC_UNUSED_ [[maybe_unused]]
|
||||||
#define IPC_FALLTHROUGH_ [[fallthrough]]
|
#define IPC_FALLTHROUGH_ [[fallthrough]]
|
||||||
#define IPC_STBIND_(A, B, ...) auto [A, B] = __VA_ARGS__
|
#define IPC_STBIND_(A, B, ...) auto [A, B] = __VA_ARGS__
|
||||||
#define IPC_CONSTEXPR_ constexpr
|
#define IPC_CONSTEXPR_ constexpr
|
||||||
|
|
||||||
#else /*__cplusplus < 201703L*/
|
#else /*__cplusplus < 201703L*/
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
# define IPC_UNUSED_ __pragma(warning(suppress: 4100 4101 4189))
|
# define IPC_UNUSED_ __pragma(warning(suppress: 4100 4101 4189))
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
# define IPC_UNUSED_ __attribute__((__unused__))
|
# define IPC_UNUSED_ __attribute__((__unused__))
|
||||||
#else
|
#else
|
||||||
# define IPC_UNUSED_
|
# define IPC_UNUSED_
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define IPC_FALLTHROUGH_
|
#define IPC_FALLTHROUGH_
|
||||||
|
|
||||||
#define IPC_STBIND_(A, B, ...) \
|
#define IPC_STBIND_(A, B, ...) \
|
||||||
auto tp___ = __VA_ARGS__ \
|
auto tp___ = __VA_ARGS__ \
|
||||||
auto A = std::get<0>(tp___); \
|
auto A = std::get<0>(tp___); \
|
||||||
auto B = std::get<1>(tp___)
|
auto B = std::get<1>(tp___)
|
||||||
|
|
||||||
#define IPC_CONSTEXPR_ inline
|
#define IPC_CONSTEXPR_ inline
|
||||||
|
|
||||||
#endif/*__cplusplus < 201703L*/
|
#endif/*__cplusplus < 201703L*/
|
||||||
|
|
||||||
#if __cplusplus >= 201703L
|
#if __cplusplus >= 201703L
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
|
|
||||||
// deduction guides for std::unique_ptr
|
// deduction guides for std::unique_ptr
|
||||||
template <typename T, typename D>
|
template <typename T, typename D>
|
||||||
unique_ptr(T* p, D&& d) -> unique_ptr<T, std::decay_t<D>>;
|
unique_ptr(T* p, D&& d) -> unique_ptr<T, std::decay_t<D>>;
|
||||||
|
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
using std::unique_ptr;
|
using std::unique_ptr;
|
||||||
using std::unique_lock;
|
using std::unique_lock;
|
||||||
using std::shared_lock;
|
using std::shared_lock;
|
||||||
using std::max;
|
using std::max;
|
||||||
using std::min;
|
using std::min;
|
||||||
|
|
||||||
#else /*__cplusplus < 201703L*/
|
#else /*__cplusplus < 201703L*/
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// deduction guides for std::unique_ptr
|
// deduction guides for std::unique_ptr
|
||||||
template <typename T, typename D>
|
template <typename T, typename D>
|
||||||
constexpr auto unique_ptr(T* p, D&& d) {
|
constexpr auto unique_ptr(T* p, D&& d) {
|
||||||
return std::unique_ptr<T, std::decay_t<D>> { p, std::forward<D>(d) };
|
return std::unique_ptr<T, std::decay_t<D>> { p, std::forward<D>(d) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// deduction guides for std::unique_lock
|
// deduction guides for std::unique_lock
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto unique_lock(T&& lc) {
|
constexpr auto unique_lock(T&& lc) {
|
||||||
return std::unique_lock<std::decay_t<T>> { std::forward<T>(lc) };
|
return std::unique_lock<std::decay_t<T>> { std::forward<T>(lc) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// deduction guides for std::shared_lock
|
// deduction guides for std::shared_lock
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto shared_lock(T&& lc) {
|
constexpr auto shared_lock(T&& lc) {
|
||||||
return std::shared_lock<std::decay_t<T>> { std::forward<T>(lc) };
|
return std::shared_lock<std::decay_t<T>> { std::forward<T>(lc) };
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr const T& (max)(const T& a, const T& b) {
|
constexpr const T& (max)(const T& a, const T& b) {
|
||||||
return (a < b) ? b : a;
|
return (a < b) ? b : a;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr const T& (min)(const T& a, const T& b) {
|
constexpr const T& (min)(const T& a, const T& b) {
|
||||||
return (b < a) ? b : a;
|
return (b < a) ? b : a;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif/*__cplusplus < 201703L*/
|
#endif/*__cplusplus < 201703L*/
|
||||||
|
|
||||||
template <typename F, typename D>
|
template <typename F, typename D>
|
||||||
constexpr decltype(auto) static_switch(std::size_t /*i*/, std::index_sequence<>, F&& /*f*/, D&& def) {
|
constexpr decltype(auto) static_switch(std::size_t /*i*/, std::index_sequence<>, F&& /*f*/, D&& def) {
|
||||||
return std::forward<D>(def)();
|
return std::forward<D>(def)();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F, typename D, std::size_t N, std::size_t...I>
|
template <typename F, typename D, std::size_t N, std::size_t...I>
|
||||||
constexpr decltype(auto) static_switch(std::size_t i, std::index_sequence<N, I...>, F&& f, D&& def) {
|
constexpr decltype(auto) static_switch(std::size_t i, std::index_sequence<N, I...>, F&& f, D&& def) {
|
||||||
return (i == N) ? std::forward<F>(f)(std::integral_constant<size_t, N>{}) :
|
return (i == N) ? std::forward<F>(f)(std::integral_constant<size_t, N>{}) :
|
||||||
static_switch(i, std::index_sequence<I...>{}, std::forward<F>(f), std::forward<D>(def));
|
static_switch(i, std::index_sequence<I...>{}, std::forward<F>(f), std::forward<D>(def));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t N, typename F, typename D>
|
template <std::size_t N, typename F, typename D>
|
||||||
constexpr decltype(auto) static_switch(std::size_t i, F&& f, D&& def) {
|
constexpr decltype(auto) static_switch(std::size_t i, F&& f, D&& def) {
|
||||||
return static_switch(i, std::make_index_sequence<N>{}, std::forward<F>(f), std::forward<D>(def));
|
return static_switch(i, std::make_index_sequence<N>{}, std::forward<F>(f), std::forward<D>(def));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F, std::size_t...I>
|
template <typename F, std::size_t...I>
|
||||||
IPC_CONSTEXPR_ void static_for(std::index_sequence<I...>, F&& f) {
|
IPC_CONSTEXPR_ void static_for(std::index_sequence<I...>, F&& f) {
|
||||||
IPC_UNUSED_ auto expand = { (std::forward<F>(f)(std::integral_constant<size_t, I>{}), 0)... };
|
IPC_UNUSED_ auto expand = { (std::forward<F>(f)(std::integral_constant<size_t, I>{}), 0)... };
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t N, typename F>
|
template <std::size_t N, typename F>
|
||||||
IPC_CONSTEXPR_ void static_for(F&& f) {
|
IPC_CONSTEXPR_ void static_for(F&& f) {
|
||||||
static_for(std::make_index_sequence<N>{}, std::forward<F>(f));
|
static_for(std::make_index_sequence<N>{}, std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimum offset between two objects to avoid false sharing.
|
// Minimum offset between two objects to avoid false sharing.
|
||||||
enum {
|
enum {
|
||||||
// #if __cplusplus >= 201703L
|
// #if __cplusplus >= 201703L
|
||||||
// cache_line_size = std::hardware_destructive_interference_size
|
// cache_line_size = std::hardware_destructive_interference_size
|
||||||
// #else /*__cplusplus < 201703L*/
|
// #else /*__cplusplus < 201703L*/
|
||||||
cache_line_size = 64
|
cache_line_size = 64
|
||||||
// #endif/*__cplusplus < 201703L*/
|
// #endif/*__cplusplus < 201703L*/
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,172 +1,172 @@
|
|||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
|
|
||||||
#include <sys/shm.h>
|
#include <sys/shm.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "pool_alloc.h"
|
#include "pool_alloc.h"
|
||||||
|
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct info_t {
|
struct info_t {
|
||||||
std::atomic_size_t acc_;
|
std::atomic_size_t acc_;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct id_info_t {
|
struct id_info_t {
|
||||||
int fd_ = -1;
|
int fd_ = -1;
|
||||||
void* mem_ = nullptr;
|
void* mem_ = nullptr;
|
||||||
std::size_t size_ = 0;
|
std::size_t size_ = 0;
|
||||||
ipc::string name_;
|
ipc::string name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::size_t calc_size(std::size_t size) {
|
constexpr std::size_t calc_size(std::size_t size) {
|
||||||
return ((((size - 1) / alignof(info_t)) + 1) * alignof(info_t)) + sizeof(info_t);
|
return ((((size - 1) / alignof(info_t)) + 1) * alignof(info_t)) + sizeof(info_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto& acc_of(void* mem, std::size_t size) {
|
inline auto& acc_of(void* mem, std::size_t size) {
|
||||||
return reinterpret_cast<info_t*>(static_cast<ipc::byte_t*>(mem) + size - sizeof(info_t))->acc_;
|
return reinterpret_cast<info_t*>(static_cast<ipc::byte_t*>(mem) + size - sizeof(info_t))->acc_;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace shm {
|
namespace shm {
|
||||||
|
|
||||||
id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
||||||
if (name == nullptr || name[0] == '\0') {
|
if (name == nullptr || name[0] == '\0') {
|
||||||
ipc::error("fail acquire: name is empty\n");
|
ipc::error("fail acquire: name is empty\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ipc::string op_name = ipc::string{"__IPC_SHM__"} + name;
|
ipc::string op_name = ipc::string{"__IPC_SHM__"} + name;
|
||||||
// Open the object for read-write access.
|
// Open the object for read-write access.
|
||||||
int flag = O_RDWR;
|
int flag = O_RDWR;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case open:
|
case open:
|
||||||
size = 0;
|
size = 0;
|
||||||
break;
|
break;
|
||||||
// The check for the existence of the object,
|
// The check for the existence of the object,
|
||||||
// and its creation if it does not exist, are performed atomically.
|
// and its creation if it does not exist, are performed atomically.
|
||||||
case create:
|
case create:
|
||||||
flag |= O_CREAT | O_EXCL;
|
flag |= O_CREAT | O_EXCL;
|
||||||
break;
|
break;
|
||||||
// Create the shared memory object if it does not exist.
|
// Create the shared memory object if it does not exist.
|
||||||
default:
|
default:
|
||||||
flag |= O_CREAT;
|
flag |= O_CREAT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int fd = ::shm_open(op_name.c_str(), flag, S_IRUSR | S_IWUSR |
|
int fd = ::shm_open(op_name.c_str(), flag, S_IRUSR | S_IWUSR |
|
||||||
S_IRGRP | S_IWGRP |
|
S_IRGRP | S_IWGRP |
|
||||||
S_IROTH | S_IWOTH);
|
S_IROTH | S_IWOTH);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
ipc::error("fail shm_open[%d]: %s\n", errno, name);
|
ipc::error("fail shm_open[%d]: %s\n", errno, name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto ii = mem::alloc<id_info_t>();
|
auto ii = mem::alloc<id_info_t>();
|
||||||
ii->fd_ = fd;
|
ii->fd_ = fd;
|
||||||
ii->size_ = size;
|
ii->size_ = size;
|
||||||
ii->name_ = std::move(op_name);
|
ii->name_ = std::move(op_name);
|
||||||
return ii;
|
return ii;
|
||||||
}
|
}
|
||||||
|
|
||||||
void * get_mem(id_t id, std::size_t * size) {
|
void * get_mem(id_t id, std::size_t * size) {
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
ipc::error("fail get_mem: invalid id (null)\n");
|
ipc::error("fail get_mem: invalid id (null)\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
if (ii->mem_ != nullptr) {
|
if (ii->mem_ != nullptr) {
|
||||||
if (size != nullptr) *size = ii->size_;
|
if (size != nullptr) *size = ii->size_;
|
||||||
return ii->mem_;
|
return ii->mem_;
|
||||||
}
|
}
|
||||||
int fd = ii->fd_;
|
int fd = ii->fd_;
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
ipc::error("fail to_mem: invalid id (fd = -1)\n");
|
ipc::error("fail to_mem: invalid id (fd = -1)\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (ii->size_ == 0) {
|
if (ii->size_ == 0) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (::fstat(fd, &st) != 0) {
|
if (::fstat(fd, &st) != 0) {
|
||||||
ipc::error("fail fstat[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
|
ipc::error("fail fstat[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ii->size_ = static_cast<std::size_t>(st.st_size);
|
ii->size_ = static_cast<std::size_t>(st.st_size);
|
||||||
if ((ii->size_ <= sizeof(info_t)) || (ii->size_ % sizeof(info_t))) {
|
if ((ii->size_ <= sizeof(info_t)) || (ii->size_ % sizeof(info_t))) {
|
||||||
ipc::error("fail to_mem: %s, invalid size = %zd\n", ii->name_.c_str(), ii->size_);
|
ipc::error("fail to_mem: %s, invalid size = %zd\n", ii->name_.c_str(), ii->size_);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ii->size_ = calc_size(ii->size_);
|
ii->size_ = calc_size(ii->size_);
|
||||||
if (::ftruncate(fd, static_cast<off_t>(ii->size_)) != 0) {
|
if (::ftruncate(fd, static_cast<off_t>(ii->size_)) != 0) {
|
||||||
ipc::error("fail ftruncate[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
|
ipc::error("fail ftruncate[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void* mem = ::mmap(nullptr, ii->size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
void* mem = ::mmap(nullptr, ii->size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
if (mem == MAP_FAILED) {
|
if (mem == MAP_FAILED) {
|
||||||
ipc::error("fail mmap[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
|
ipc::error("fail mmap[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
::close(fd);
|
::close(fd);
|
||||||
ii->fd_ = -1;
|
ii->fd_ = -1;
|
||||||
ii->mem_ = mem;
|
ii->mem_ = mem;
|
||||||
if (size != nullptr) *size = ii->size_;
|
if (size != nullptr) *size = ii->size_;
|
||||||
acc_of(mem, ii->size_).fetch_add(1, std::memory_order_release);
|
acc_of(mem, ii->size_).fetch_add(1, std::memory_order_release);
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
void release(id_t id) {
|
void release(id_t id) {
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
ipc::error("fail release: invalid id (null)\n");
|
ipc::error("fail release: invalid id (null)\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
||||||
ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_);
|
ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_);
|
||||||
}
|
}
|
||||||
else if (acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acquire) == 1) {
|
else if (acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acquire) == 1) {
|
||||||
::munmap(ii->mem_, ii->size_);
|
::munmap(ii->mem_, ii->size_);
|
||||||
if (!ii->name_.empty()) {
|
if (!ii->name_.empty()) {
|
||||||
::shm_unlink(ii->name_.c_str());
|
::shm_unlink(ii->name_.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else ::munmap(ii->mem_, ii->size_);
|
else ::munmap(ii->mem_, ii->size_);
|
||||||
mem::free(ii);
|
mem::free(ii);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(id_t id) {
|
void remove(id_t id) {
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
ipc::error("fail remove: invalid id (null)\n");
|
ipc::error("fail remove: invalid id (null)\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
auto name = std::move(ii->name_);
|
auto name = std::move(ii->name_);
|
||||||
release(id);
|
release(id);
|
||||||
if (!name.empty()) {
|
if (!name.empty()) {
|
||||||
::shm_unlink(name.c_str());
|
::shm_unlink(name.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(char const * name) {
|
void remove(char const * name) {
|
||||||
if (name == nullptr || name[0] == '\0') {
|
if (name == nullptr || name[0] == '\0') {
|
||||||
ipc::error("fail remove: name is empty\n");
|
ipc::error("fail remove: name is empty\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
::shm_unlink((ipc::string{"__IPC_SHM__"} + name).c_str());
|
::shm_unlink((ipc::string{"__IPC_SHM__"} + name).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace shm
|
} // namespace shm
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,124 +1,124 @@
|
|||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "pool_alloc.h"
|
#include "pool_alloc.h"
|
||||||
|
|
||||||
#include "platform/to_tchar.h"
|
#include "platform/to_tchar.h"
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct id_info_t {
|
struct id_info_t {
|
||||||
HANDLE h_ = NULL;
|
HANDLE h_ = NULL;
|
||||||
void* mem_ = nullptr;
|
void* mem_ = nullptr;
|
||||||
std::size_t size_ = 0;
|
std::size_t size_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace shm {
|
namespace shm {
|
||||||
|
|
||||||
id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
||||||
if (name == nullptr || name[0] == '\0') {
|
if (name == nullptr || name[0] == '\0') {
|
||||||
ipc::error("fail acquire: name is empty\n");
|
ipc::error("fail acquire: name is empty\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
HANDLE h;
|
HANDLE h;
|
||||||
auto fmt_name = ipc::detail::to_tchar(ipc::string{"__IPC_SHM__"} + name);
|
auto fmt_name = ipc::detail::to_tchar(ipc::string{"__IPC_SHM__"} + name);
|
||||||
// Opens a named file mapping object.
|
// Opens a named file mapping object.
|
||||||
if (mode == open) {
|
if (mode == open) {
|
||||||
h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, fmt_name.c_str());
|
h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, fmt_name.c_str());
|
||||||
}
|
}
|
||||||
// Creates or opens a named file mapping object for a specified file.
|
// Creates or opens a named file mapping object for a specified file.
|
||||||
else {
|
else {
|
||||||
h = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT,
|
h = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT,
|
||||||
0, static_cast<DWORD>(size), fmt_name.c_str());
|
0, static_cast<DWORD>(size), fmt_name.c_str());
|
||||||
// If the object exists before the function call, the function returns a handle to the existing object
|
// If the object exists before the function call, the function returns a handle to the existing object
|
||||||
// (with its current size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS.
|
// (with its current size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS.
|
||||||
if ((mode == create) && (::GetLastError() == ERROR_ALREADY_EXISTS)) {
|
if ((mode == create) && (::GetLastError() == ERROR_ALREADY_EXISTS)) {
|
||||||
::CloseHandle(h);
|
::CloseHandle(h);
|
||||||
h = NULL;
|
h = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (h == NULL) {
|
if (h == NULL) {
|
||||||
ipc::error("fail CreateFileMapping/OpenFileMapping[%d]: %s\n", static_cast<int>(::GetLastError()), name);
|
ipc::error("fail CreateFileMapping/OpenFileMapping[%d]: %s\n", static_cast<int>(::GetLastError()), name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto ii = mem::alloc<id_info_t>();
|
auto ii = mem::alloc<id_info_t>();
|
||||||
ii->h_ = h;
|
ii->h_ = h;
|
||||||
ii->size_ = size;
|
ii->size_ = size;
|
||||||
return ii;
|
return ii;
|
||||||
}
|
}
|
||||||
|
|
||||||
void * get_mem(id_t id, std::size_t * size) {
|
void * get_mem(id_t id, std::size_t * size) {
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
ipc::error("fail get_mem: invalid id (null)\n");
|
ipc::error("fail get_mem: invalid id (null)\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
if (ii->mem_ != nullptr) {
|
if (ii->mem_ != nullptr) {
|
||||||
if (size != nullptr) *size = ii->size_;
|
if (size != nullptr) *size = ii->size_;
|
||||||
return ii->mem_;
|
return ii->mem_;
|
||||||
}
|
}
|
||||||
if (ii->h_ == NULL) {
|
if (ii->h_ == NULL) {
|
||||||
ipc::error("fail to_mem: invalid id (h = null)\n");
|
ipc::error("fail to_mem: invalid id (h = null)\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
LPVOID mem = ::MapViewOfFile(ii->h_, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
LPVOID mem = ::MapViewOfFile(ii->h_, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||||
if (mem == NULL) {
|
if (mem == NULL) {
|
||||||
ipc::error("fail MapViewOfFile[%d]\n", static_cast<int>(::GetLastError()));
|
ipc::error("fail MapViewOfFile[%d]\n", static_cast<int>(::GetLastError()));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
MEMORY_BASIC_INFORMATION mem_info;
|
MEMORY_BASIC_INFORMATION mem_info;
|
||||||
if (::VirtualQuery(mem, &mem_info, sizeof(mem_info)) == 0) {
|
if (::VirtualQuery(mem, &mem_info, sizeof(mem_info)) == 0) {
|
||||||
ipc::error("fail VirtualQuery[%d]\n", static_cast<int>(::GetLastError()));
|
ipc::error("fail VirtualQuery[%d]\n", static_cast<int>(::GetLastError()));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ii->mem_ = mem;
|
ii->mem_ = mem;
|
||||||
ii->size_ = static_cast<std::size_t>(mem_info.RegionSize);
|
ii->size_ = static_cast<std::size_t>(mem_info.RegionSize);
|
||||||
if (size != nullptr) *size = ii->size_;
|
if (size != nullptr) *size = ii->size_;
|
||||||
return static_cast<void *>(mem);
|
return static_cast<void *>(mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void release(id_t id) {
|
void release(id_t id) {
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
ipc::error("fail release: invalid id (null)\n");
|
ipc::error("fail release: invalid id (null)\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
||||||
ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_);
|
ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_);
|
||||||
}
|
}
|
||||||
else ::UnmapViewOfFile(static_cast<LPCVOID>(ii->mem_));
|
else ::UnmapViewOfFile(static_cast<LPCVOID>(ii->mem_));
|
||||||
if (ii->h_ == NULL) {
|
if (ii->h_ == NULL) {
|
||||||
ipc::error("fail release: invalid id (h = null)\n");
|
ipc::error("fail release: invalid id (h = null)\n");
|
||||||
}
|
}
|
||||||
else ::CloseHandle(ii->h_);
|
else ::CloseHandle(ii->h_);
|
||||||
mem::free(ii);
|
mem::free(ii);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(id_t id) {
|
void remove(id_t id) {
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
ipc::error("fail release: invalid id (null)\n");
|
ipc::error("fail release: invalid id (null)\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
release(id);
|
release(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(char const * name) {
|
void remove(char const * name) {
|
||||||
if (name == nullptr || name[0] == '\0') {
|
if (name == nullptr || name[0] == '\0') {
|
||||||
ipc::error("fail remove: name is empty\n");
|
ipc::error("fail remove: name is empty\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Do Nothing.
|
// Do Nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace shm
|
} // namespace shm
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,29 +1,29 @@
|
|||||||
#include "tls_pointer.h"
|
#include "tls_pointer.h"
|
||||||
|
|
||||||
#include <pthread.h> // pthread_...
|
#include <pthread.h> // pthread_...
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace tls {
|
namespace tls {
|
||||||
|
|
||||||
key_t create(destructor_t destructor) {
|
key_t create(destructor_t destructor) {
|
||||||
pthread_key_t k;
|
pthread_key_t k;
|
||||||
if (pthread_key_create(&k, destructor) == 0) {
|
if (pthread_key_create(&k, destructor) == 0) {
|
||||||
return static_cast<key_t>(k);
|
return static_cast<key_t>(k);
|
||||||
}
|
}
|
||||||
return invalid_value;
|
return invalid_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void release(key_t key) {
|
void release(key_t key) {
|
||||||
pthread_key_delete(static_cast<pthread_key_t>(key));
|
pthread_key_delete(static_cast<pthread_key_t>(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool set(key_t key, void* ptr) {
|
bool set(key_t key, void* ptr) {
|
||||||
return pthread_setspecific(static_cast<pthread_key_t>(key), ptr) == 0;
|
return pthread_setspecific(static_cast<pthread_key_t>(key), ptr) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* get(key_t key) {
|
void* get(key_t key) {
|
||||||
return pthread_getspecific(static_cast<pthread_key_t>(key));
|
return pthread_getspecific(static_cast<pthread_key_t>(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tls
|
} // namespace tls
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,183 +1,230 @@
|
|||||||
#include "tls_pointer.h"
|
#include "tls_pointer.h"
|
||||||
|
#include "log.h"
|
||||||
#include <Windows.h> // ::Tls...
|
|
||||||
#include <unordered_map> // std::unordered_map
|
#include <Windows.h> // ::Tls...
|
||||||
|
#include <atomic>
|
||||||
namespace ipc {
|
#include <unordered_set> // std::unordered_set
|
||||||
|
|
||||||
/*
|
namespace ipc {
|
||||||
* <Remarks>
|
|
||||||
*
|
/*
|
||||||
* Windows doesn't support a per-thread destructor with its TLS primitives.
|
* <Remarks>
|
||||||
* So, here will build it manually by inserting a function to be called on each thread's exit.
|
*
|
||||||
*
|
* Windows doesn't support a per-thread destructor with its TLS primitives.
|
||||||
* <Reference>
|
* So, here will build it manually by inserting a function to be called on each thread's exit.
|
||||||
* - https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
|
*
|
||||||
* - https://src.chromium.org/viewvc/chrome/trunk/src/base/threading/thread_local_storage_win.cc
|
* <Reference>
|
||||||
* - https://github.com/mirror/mingw-org-wsl/blob/master/src/libcrt/crt/tlssup.c
|
* - https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
|
||||||
* - https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-crt/crt/tlssup.c
|
* - https://src.chromium.org/viewvc/chrome/trunk/src/base/threading/thread_local_storage_win.cc
|
||||||
* - http://svn.boost.org/svn/boost/trunk/libs/thread/src/win32/tss_pe.cpp
|
* - https://github.com/mirror/mingw-org-wsl/blob/master/src/libcrt/crt/tlssup.c
|
||||||
*/
|
* - https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-crt/crt/tlssup.c
|
||||||
|
* - http://svn.boost.org/svn/boost/trunk/libs/thread/src/win32/tss_pe.cpp
|
||||||
namespace {
|
*/
|
||||||
|
|
||||||
struct tls_data {
|
namespace {
|
||||||
using destructor_t = void(*)(void*);
|
|
||||||
using map_t = std::unordered_map<tls::key_t, tls_data>;
|
struct tls_data {
|
||||||
|
using destructor_t = void(*)(void*);
|
||||||
static DWORD& key() {
|
|
||||||
static DWORD rec_key = ::TlsAlloc();
|
DWORD win_key_;
|
||||||
return rec_key;
|
destructor_t destructor_;
|
||||||
}
|
|
||||||
|
void destruct(void* data) {
|
||||||
static map_t* records(map_t* rec) {
|
if ((destructor_ != nullptr) && (data != nullptr)) {
|
||||||
::TlsSetValue(key(), static_cast<LPVOID>(rec));
|
destructor_(data);
|
||||||
return rec;
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
static map_t* records() {
|
|
||||||
return static_cast<map_t*>(::TlsGetValue(key()));
|
using rec_t = std::unordered_set<tls_data*>;
|
||||||
}
|
|
||||||
|
DWORD& record_key() {
|
||||||
tls::key_t key_ = tls::invalid_value;
|
|
||||||
destructor_t destructor_ = nullptr;
|
struct key_gen {
|
||||||
|
DWORD rec_key_;
|
||||||
tls_data() = default;
|
key_gen() : rec_key_(::TlsAlloc()) {
|
||||||
|
if (rec_key_ == TLS_OUT_OF_INDEXES) {
|
||||||
tls_data(tls::key_t key, destructor_t destructor)
|
ipc::error("[record_key] TlsAlloc failed[%lu].\n", ::GetLastError());
|
||||||
: key_ (key)
|
}
|
||||||
, destructor_(destructor)
|
}
|
||||||
{}
|
~key_gen() { ::TlsFree(rec_key_); }
|
||||||
|
};
|
||||||
tls_data(tls_data&& rhs) : tls_data() {
|
|
||||||
(*this) = std::move(rhs);
|
static key_gen gen;
|
||||||
}
|
return gen.rec_key_;
|
||||||
|
}
|
||||||
tls_data& operator=(tls_data&& rhs) {
|
|
||||||
key_ = rhs.key_;
|
bool record(tls_data* tls) {
|
||||||
destructor_ = rhs.destructor_;
|
auto rec = static_cast<rec_t*>(::TlsGetValue(record_key()));
|
||||||
rhs.key_ = 0;
|
if (rec == nullptr) {
|
||||||
rhs.destructor_ = nullptr;
|
if (FALSE == ::TlsSetValue(record_key(), static_cast<LPVOID>(rec = new rec_t))) {
|
||||||
return *this;
|
ipc::error("[record] TlsSetValue failed[%lu].\n", ::GetLastError());
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
~tls_data() {
|
}
|
||||||
if (destructor_) destructor_(tls::get(key_));
|
rec->insert(tls);
|
||||||
}
|
return true;
|
||||||
};
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
static void erase_record(tls_data* tls) {
|
||||||
|
auto rec = static_cast<rec_t*>(::TlsGetValue(record_key()));
|
||||||
namespace tls {
|
if (rec == nullptr) return;
|
||||||
|
rec->erase(tls);
|
||||||
key_t create(destructor_t destructor) {
|
}
|
||||||
key_t key = static_cast<key_t>(::TlsAlloc());
|
|
||||||
if (key == TLS_OUT_OF_INDEXES) return invalid_value;
|
static void clear_all_records() {
|
||||||
auto rec = tls_data::records();
|
auto rec = static_cast<rec_t*>(::TlsGetValue(record_key()));
|
||||||
if (rec == nullptr) rec = tls_data::records(new tls_data::map_t);
|
if (rec == nullptr) return;
|
||||||
if (rec == nullptr) return key;
|
for (auto tls : *rec) {
|
||||||
rec->emplace(key, tls_data{ key, destructor });
|
if (tls != nullptr) {
|
||||||
return key;
|
tls->destruct(::TlsGetValue(tls->win_key_));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
void release(key_t key) {
|
delete rec;
|
||||||
auto rec = tls_data::records();
|
::TlsSetValue(record_key(), static_cast<LPVOID>(nullptr));
|
||||||
if (rec == nullptr) return;
|
}
|
||||||
rec->erase(key);
|
|
||||||
::TlsFree(static_cast<DWORD>(key));
|
} // internal-linkage
|
||||||
}
|
|
||||||
|
namespace tls {
|
||||||
bool set(key_t key, void* ptr) {
|
|
||||||
return ::TlsSetValue(static_cast<DWORD>(key),
|
key_t create(destructor_t destructor) {
|
||||||
static_cast<LPVOID>(ptr)) == TRUE;
|
record_key(); // gen record-key
|
||||||
}
|
auto tls_dat = new tls_data { ::TlsAlloc(), destructor };
|
||||||
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
void* get(key_t key) {
|
if (tls_dat->win_key_ == TLS_OUT_OF_INDEXES) {
|
||||||
return static_cast<void*>(::TlsGetValue(static_cast<DWORD>(key)));
|
ipc::error("[tls::create] TlsAlloc failed[%lu].\n", ::GetLastError());
|
||||||
}
|
delete tls_dat;
|
||||||
|
return invalid_value;
|
||||||
} // namespace tls
|
}
|
||||||
|
return reinterpret_cast<key_t>(tls_dat);
|
||||||
namespace {
|
}
|
||||||
|
|
||||||
void OnThreadExit() {
|
void release(key_t tls_key) {
|
||||||
auto rec = tls_data::records();
|
if (tls_key == invalid_value) {
|
||||||
if (rec == nullptr) return;
|
ipc::error("[tls::release] tls_key is invalid_value.\n");
|
||||||
delete rec;
|
return;
|
||||||
tls_data::records(nullptr);
|
}
|
||||||
}
|
auto tls_dat = reinterpret_cast<tls_data*>(tls_key);
|
||||||
|
if (tls_dat == nullptr) {
|
||||||
void NTAPI OnTlsCallback(PVOID, DWORD dwReason, PVOID) {
|
ipc::error("[tls::release] tls_dat is nullptr.\n");
|
||||||
if (dwReason == DLL_THREAD_DETACH) OnThreadExit();
|
return;
|
||||||
}
|
}
|
||||||
|
erase_record(tls_dat);
|
||||||
} // internal-linkage
|
::TlsFree(tls_dat->win_key_);
|
||||||
|
delete tls_dat;
|
||||||
////////////////////////////////////////////////////////////////
|
}
|
||||||
/// Call destructors on thread exit
|
|
||||||
////////////////////////////////////////////////////////////////
|
bool set(key_t tls_key, void* ptr) {
|
||||||
|
if (tls_key == invalid_value) {
|
||||||
#if defined(_MSC_VER)
|
ipc::error("[tls::set] tls_key is invalid_value.\n");
|
||||||
|
return false;
|
||||||
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
|
}
|
||||||
|
auto tls_dat = reinterpret_cast<tls_data*>(tls_key);
|
||||||
#pragma comment(linker, "/INCLUDE:_tls_used")
|
if (tls_dat == nullptr) {
|
||||||
#pragma comment(linker, "/INCLUDE:_tls_xl_b__")
|
ipc::error("[tls::set] tls_dat is nullptr.\n");
|
||||||
|
return false;
|
||||||
extern "C" {
|
}
|
||||||
# pragma const_seg(".CRT$XLB")
|
if (FALSE == ::TlsSetValue(tls_dat->win_key_, static_cast<LPVOID>(ptr))) {
|
||||||
extern const PIMAGE_TLS_CALLBACK _tls_xl_b__;
|
ipc::error("[tls::set] TlsSetValue failed[%lu].\n", ::GetLastError());
|
||||||
const PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
|
return false;
|
||||||
# pragma const_seg()
|
}
|
||||||
}
|
record(tls_dat);
|
||||||
|
return true;
|
||||||
#else /*!WIN64*/
|
}
|
||||||
|
|
||||||
#pragma comment(linker, "/INCLUDE:__tls_used")
|
void* get(key_t tls_key) {
|
||||||
#pragma comment(linker, "/INCLUDE:__tls_xl_b__")
|
if (tls_key == invalid_value) {
|
||||||
|
ipc::error("[tls::get] tls_key is invalid_value.\n");
|
||||||
extern "C" {
|
return nullptr;
|
||||||
# pragma data_seg(".CRT$XLB")
|
}
|
||||||
PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
|
auto tls_dat = reinterpret_cast<tls_data*>(tls_key);
|
||||||
# pragma data_seg()
|
if (tls_dat == nullptr) {
|
||||||
}
|
ipc::error("[tls::get] tls_dat is nullptr.\n");
|
||||||
|
return nullptr;
|
||||||
#endif/*!WIN64*/
|
}
|
||||||
|
return ::TlsGetValue(tls_dat->win_key_);
|
||||||
#elif defined(__GNUC__)
|
}
|
||||||
|
|
||||||
#define IPC_CRTALLOC__(x) __attribute__ ((section (x) ))
|
} // namespace tls
|
||||||
|
|
||||||
#if defined(__MINGW64__) || (__MINGW64_VERSION_MAJOR) || \
|
namespace {
|
||||||
(__MINGW32_MAJOR_VERSION > 3) || ((__MINGW32_MAJOR_VERSION == 3) && (__MINGW32_MINOR_VERSION >= 18))
|
|
||||||
|
void OnThreadExit() {
|
||||||
extern "C" {
|
clear_all_records();
|
||||||
IPC_CRTALLOC__(".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
|
}
|
||||||
}
|
|
||||||
|
void NTAPI OnTlsCallback(PVOID, DWORD dwReason, PVOID) {
|
||||||
#else /*!MINGW*/
|
if (dwReason == DLL_THREAD_DETACH) OnThreadExit();
|
||||||
|
}
|
||||||
extern "C" {
|
|
||||||
ULONG _tls_index__ = 0;
|
} // internal-linkage
|
||||||
|
|
||||||
IPC_CRTALLOC__(".tls$AAA") char _tls_start__ = 0;
|
////////////////////////////////////////////////////////////////
|
||||||
IPC_CRTALLOC__(".tls$ZZZ") char _tls_end__ = 0;
|
/// Call destructors on thread exit
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
IPC_CRTALLOC__(".CRT$XLA") PIMAGE_TLS_CALLBACK _tls_xl_a__ = 0;
|
|
||||||
IPC_CRTALLOC__(".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
|
#if defined(_MSC_VER)
|
||||||
IPC_CRTALLOC__(".CRT$XLZ") PIMAGE_TLS_CALLBACK _tls_xl_z__ = 0;
|
|
||||||
}
|
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
|
||||||
|
|
||||||
extern "C" NX_CRTALLOC_(".tls") const IMAGE_TLS_DIRECTORY _tls_used = {
|
#pragma comment(linker, "/INCLUDE:_tls_used")
|
||||||
(ULONG_PTR)(&_tls_start__ + 1),
|
#pragma comment(linker, "/INCLUDE:_tls_xl_b__")
|
||||||
(ULONG_PTR) &_tls_end__,
|
|
||||||
(ULONG_PTR) &_tls_index__,
|
extern "C" {
|
||||||
(ULONG_PTR) &_tls_xl_b__,
|
# pragma const_seg(".CRT$XLB")
|
||||||
(ULONG)0, (ULONG)0
|
extern const PIMAGE_TLS_CALLBACK _tls_xl_b__;
|
||||||
}
|
const PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
|
||||||
|
# pragma const_seg()
|
||||||
#endif/*!MINGW*/
|
}
|
||||||
|
|
||||||
#endif/*_MSC_VER, __GNUC__*/
|
#else /*!WIN64*/
|
||||||
|
|
||||||
} // namespace ipc
|
#pragma comment(linker, "/INCLUDE:__tls_used")
|
||||||
|
#pragma comment(linker, "/INCLUDE:__tls_xl_b__")
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
# pragma data_seg(".CRT$XLB")
|
||||||
|
PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
|
||||||
|
# pragma data_seg()
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif/*!WIN64*/
|
||||||
|
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
|
||||||
|
#define IPC_CRTALLOC__(x) __attribute__ ((section (x) ))
|
||||||
|
|
||||||
|
#if defined(__MINGW64__) || (__MINGW64_VERSION_MAJOR) || \
|
||||||
|
(__MINGW32_MAJOR_VERSION > 3) || ((__MINGW32_MAJOR_VERSION == 3) && (__MINGW32_MINOR_VERSION >= 18))
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
IPC_CRTALLOC__(".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /*!MINGW*/
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
ULONG _tls_index__ = 0;
|
||||||
|
|
||||||
|
IPC_CRTALLOC__(".tls$AAA") char _tls_start__ = 0;
|
||||||
|
IPC_CRTALLOC__(".tls$ZZZ") char _tls_end__ = 0;
|
||||||
|
|
||||||
|
IPC_CRTALLOC__(".CRT$XLA") PIMAGE_TLS_CALLBACK _tls_xl_a__ = 0;
|
||||||
|
IPC_CRTALLOC__(".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_xl_b__ = OnTlsCallback;
|
||||||
|
IPC_CRTALLOC__(".CRT$XLZ") PIMAGE_TLS_CALLBACK _tls_xl_z__ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" NX_CRTALLOC_(".tls") const IMAGE_TLS_DIRECTORY _tls_used = {
|
||||||
|
(ULONG_PTR)(&_tls_start__ + 1),
|
||||||
|
(ULONG_PTR) &_tls_end__,
|
||||||
|
(ULONG_PTR) &_tls_index__,
|
||||||
|
(ULONG_PTR) &_tls_xl_b__,
|
||||||
|
(ULONG)0, (ULONG)0
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif/*!MINGW*/
|
||||||
|
|
||||||
|
#endif/*_MSC_VER, __GNUC__*/
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,67 +1,67 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "concept.h"
|
#include "concept.h"
|
||||||
|
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
|
|
||||||
namespace ipc::detail {
|
namespace ipc::detail {
|
||||||
|
|
||||||
struct has_value_type_ {
|
struct has_value_type_ {
|
||||||
template <typename T> static std::true_type check(typename T::value_type *);
|
template <typename T> static std::true_type check(typename T::value_type *);
|
||||||
template <typename T> static std::false_type check(...);
|
template <typename T> static std::false_type check(...);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename U, typename = decltype(has_value_type_::check<U>(nullptr))>
|
template <typename T, typename U, typename = decltype(has_value_type_::check<U>(nullptr))>
|
||||||
struct is_same_char : std::is_same<T, U> {};
|
struct is_same_char : std::is_same<T, U> {};
|
||||||
|
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
struct is_same_char<T, U, std::true_type> : std::is_same<T, typename U::value_type> {};
|
struct is_same_char<T, U, std::true_type> : std::is_same<T, typename U::value_type> {};
|
||||||
|
|
||||||
template <typename T, typename S, typename R = S>
|
template <typename T, typename S, typename R = S>
|
||||||
using IsSameChar = ipc::require<is_same_char<T, S>::value, R>;
|
using IsSameChar = ipc::require<is_same_char<T, S>::value, R>;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// to_tchar implementation
|
/// to_tchar implementation
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template <typename T = TCHAR>
|
template <typename T = TCHAR>
|
||||||
constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::string, ipc::string &&> {
|
constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::string, ipc::string &&> {
|
||||||
return std::move(str);
|
return std::move(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T = TCHAR>
|
template <typename T = TCHAR>
|
||||||
constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::wstring> {
|
constexpr auto to_tchar(ipc::string && str) -> IsSameChar<T, ipc::wstring> {
|
||||||
return std::wstring_convert<
|
return std::wstring_convert<
|
||||||
std::codecvt_utf8_utf16<wchar_t>,
|
std::codecvt_utf8_utf16<wchar_t>,
|
||||||
wchar_t,
|
wchar_t,
|
||||||
ipc::mem::allocator<wchar_t>,
|
ipc::mem::allocator<wchar_t>,
|
||||||
ipc::mem::allocator<char>
|
ipc::mem::allocator<char>
|
||||||
>{}.from_bytes(std::move(str));
|
>{}.from_bytes(std::move(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, char, void> {
|
inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, char, void> {
|
||||||
std::memcpy(dst, src, size);
|
std::memcpy(dst, src, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, wchar_t, void> {
|
inline auto to_tchar(T* dst, char const * src, std::size_t size) -> IsSameChar<T, wchar_t, void> {
|
||||||
auto wstr = std::wstring_convert<
|
auto wstr = std::wstring_convert<
|
||||||
std::codecvt_utf8_utf16<wchar_t>,
|
std::codecvt_utf8_utf16<wchar_t>,
|
||||||
wchar_t,
|
wchar_t,
|
||||||
ipc::mem::allocator<wchar_t>,
|
ipc::mem::allocator<wchar_t>,
|
||||||
ipc::mem::allocator<char>
|
ipc::mem::allocator<char>
|
||||||
>{}.from_bytes(src, src + size);
|
>{}.from_bytes(src, src + size);
|
||||||
std::memcpy(dst, wstr.data(), (ipc::detail::min)(wstr.size(), size));
|
std::memcpy(dst, wstr.data(), (ipc::detail::min)(wstr.size(), size));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ipc::detail
|
} // namespace ipc::detail
|
||||||
|
|||||||
@ -1,347 +1,347 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline static void calc_wait_time(timespec& ts, std::size_t tm) {
|
inline static void calc_wait_time(timespec& ts, std::size_t tm) {
|
||||||
::clock_gettime(CLOCK_REALTIME, &ts);
|
::clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
ts.tv_nsec += tm * 1000000; // nanoseconds
|
ts.tv_nsec += tm * 1000000; // nanoseconds
|
||||||
ts.tv_sec += ts.tv_nsec / 1000000000;
|
ts.tv_sec += ts.tv_nsec / 1000000000;
|
||||||
ts.tv_nsec %= 1000000000;
|
ts.tv_nsec %= 1000000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma push_macro("IPC_PTHREAD_FUNC_")
|
#pragma push_macro("IPC_PTHREAD_FUNC_")
|
||||||
#undef IPC_PTHREAD_FUNC_
|
#undef IPC_PTHREAD_FUNC_
|
||||||
#define IPC_PTHREAD_FUNC_(CALL, ...) \
|
#define IPC_PTHREAD_FUNC_(CALL, ...) \
|
||||||
int eno; \
|
int eno; \
|
||||||
if ((eno = ::CALL(__VA_ARGS__)) != 0) { \
|
if ((eno = ::CALL(__VA_ARGS__)) != 0) { \
|
||||||
ipc::error("fail " #CALL "[%d]\n", eno); \
|
ipc::error("fail " #CALL "[%d]\n", eno); \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
return true
|
return true
|
||||||
|
|
||||||
class mutex {
|
class mutex {
|
||||||
pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
pthread_mutex_t& native() {
|
pthread_mutex_t& native() {
|
||||||
return mutex_;
|
return mutex_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open() {
|
bool open() {
|
||||||
int eno;
|
int eno;
|
||||||
// init mutex
|
// init mutex
|
||||||
pthread_mutexattr_t mutex_attr;
|
pthread_mutexattr_t mutex_attr;
|
||||||
if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) {
|
if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) {
|
||||||
ipc::error("fail pthread_mutexattr_init[%d]\n", eno);
|
ipc::error("fail pthread_mutexattr_init[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
IPC_UNUSED_ auto guard_mutex_attr = unique_ptr(&mutex_attr, ::pthread_mutexattr_destroy);
|
IPC_UNUSED_ auto guard_mutex_attr = unique_ptr(&mutex_attr, ::pthread_mutexattr_destroy);
|
||||||
if ((eno = ::pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
if ((eno = ::pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
||||||
ipc::error("fail pthread_mutexattr_setpshared[%d]\n", eno);
|
ipc::error("fail pthread_mutexattr_setpshared[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((eno = ::pthread_mutex_init(&mutex_, &mutex_attr)) != 0) {
|
if ((eno = ::pthread_mutex_init(&mutex_, &mutex_attr)) != 0) {
|
||||||
ipc::error("fail pthread_mutex_init[%d]\n", eno);
|
ipc::error("fail pthread_mutex_init[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool close() {
|
bool close() {
|
||||||
IPC_PTHREAD_FUNC_(pthread_mutex_destroy, &mutex_);
|
IPC_PTHREAD_FUNC_(pthread_mutex_destroy, &mutex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lock() {
|
bool lock() {
|
||||||
IPC_PTHREAD_FUNC_(pthread_mutex_lock, &mutex_);
|
IPC_PTHREAD_FUNC_(pthread_mutex_lock, &mutex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unlock() {
|
bool unlock() {
|
||||||
IPC_PTHREAD_FUNC_(pthread_mutex_unlock, &mutex_);
|
IPC_PTHREAD_FUNC_(pthread_mutex_unlock, &mutex_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class condition {
|
class condition {
|
||||||
pthread_cond_t cond_ = PTHREAD_COND_INITIALIZER;
|
pthread_cond_t cond_ = PTHREAD_COND_INITIALIZER;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool open() {
|
bool open() {
|
||||||
int eno;
|
int eno;
|
||||||
// init condition
|
// init condition
|
||||||
pthread_condattr_t cond_attr;
|
pthread_condattr_t cond_attr;
|
||||||
if ((eno = ::pthread_condattr_init(&cond_attr)) != 0) {
|
if ((eno = ::pthread_condattr_init(&cond_attr)) != 0) {
|
||||||
ipc::error("fail pthread_condattr_init[%d]\n", eno);
|
ipc::error("fail pthread_condattr_init[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
IPC_UNUSED_ auto guard_cond_attr = unique_ptr(&cond_attr, ::pthread_condattr_destroy);
|
IPC_UNUSED_ auto guard_cond_attr = unique_ptr(&cond_attr, ::pthread_condattr_destroy);
|
||||||
if ((eno = ::pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
if ((eno = ::pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
||||||
ipc::error("fail pthread_condattr_setpshared[%d]\n", eno);
|
ipc::error("fail pthread_condattr_setpshared[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((eno = ::pthread_cond_init(&cond_, &cond_attr)) != 0) {
|
if ((eno = ::pthread_cond_init(&cond_, &cond_attr)) != 0) {
|
||||||
ipc::error("fail pthread_cond_init[%d]\n", eno);
|
ipc::error("fail pthread_cond_init[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool close() {
|
bool close() {
|
||||||
IPC_PTHREAD_FUNC_(pthread_cond_destroy, &cond_);
|
IPC_PTHREAD_FUNC_(pthread_cond_destroy, &cond_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wait(mutex& mtx, std::size_t tm = invalid_value) {
|
bool wait(mutex& mtx, std::size_t tm = invalid_value) {
|
||||||
switch (tm) {
|
switch (tm) {
|
||||||
case 0:
|
case 0:
|
||||||
return true;
|
return true;
|
||||||
case invalid_value:
|
case invalid_value:
|
||||||
IPC_PTHREAD_FUNC_(pthread_cond_wait, &cond_, &mtx.native());
|
IPC_PTHREAD_FUNC_(pthread_cond_wait, &cond_, &mtx.native());
|
||||||
default: {
|
default: {
|
||||||
timespec ts;
|
timespec ts;
|
||||||
calc_wait_time(ts, tm);
|
calc_wait_time(ts, tm);
|
||||||
int eno;
|
int eno;
|
||||||
if ((eno = ::pthread_cond_timedwait(&cond_, &mtx.native(), &ts)) != 0) {
|
if ((eno = ::pthread_cond_timedwait(&cond_, &mtx.native(), &ts)) != 0) {
|
||||||
if (eno != ETIMEDOUT) {
|
if (eno != ETIMEDOUT) {
|
||||||
ipc::error("fail pthread_cond_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
|
ipc::error("fail pthread_cond_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
|
||||||
eno, tm, ts.tv_sec, ts.tv_nsec);
|
eno, tm, ts.tv_sec, ts.tv_nsec);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notify() {
|
bool notify() {
|
||||||
IPC_PTHREAD_FUNC_(pthread_cond_signal, &cond_);
|
IPC_PTHREAD_FUNC_(pthread_cond_signal, &cond_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool broadcast() {
|
bool broadcast() {
|
||||||
IPC_PTHREAD_FUNC_(pthread_cond_broadcast, &cond_);
|
IPC_PTHREAD_FUNC_(pthread_cond_broadcast, &cond_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pop_macro("IPC_PTHREAD_FUNC_")
|
#pragma pop_macro("IPC_PTHREAD_FUNC_")
|
||||||
|
|
||||||
class sem_helper {
|
class sem_helper {
|
||||||
public:
|
public:
|
||||||
using handle_t = sem_t*;
|
using handle_t = sem_t*;
|
||||||
|
|
||||||
constexpr static handle_t invalid() noexcept {
|
constexpr static handle_t invalid() noexcept {
|
||||||
return SEM_FAILED;
|
return SEM_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static handle_t open(char const* name, long count) {
|
static handle_t open(char const* name, long count) {
|
||||||
handle_t sem = ::sem_open(name, O_CREAT, 0666, count);
|
handle_t sem = ::sem_open(name, O_CREAT, 0666, count);
|
||||||
if (sem == SEM_FAILED) {
|
if (sem == SEM_FAILED) {
|
||||||
ipc::error("fail sem_open[%d]: %s\n", errno, name);
|
ipc::error("fail sem_open[%d]: %s\n", errno, name);
|
||||||
return invalid();
|
return invalid();
|
||||||
}
|
}
|
||||||
return sem;
|
return sem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma push_macro("IPC_SEMAPHORE_FUNC_")
|
#pragma push_macro("IPC_SEMAPHORE_FUNC_")
|
||||||
#undef IPC_SEMAPHORE_FUNC_
|
#undef IPC_SEMAPHORE_FUNC_
|
||||||
#define IPC_SEMAPHORE_FUNC_(CALL, ...) \
|
#define IPC_SEMAPHORE_FUNC_(CALL, ...) \
|
||||||
if (::CALL(__VA_ARGS__) != 0) { \
|
if (::CALL(__VA_ARGS__) != 0) { \
|
||||||
ipc::error("fail " #CALL "[%d]\n", errno); \
|
ipc::error("fail " #CALL "[%d]\n", errno); \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
return true
|
return true
|
||||||
|
|
||||||
static bool close(handle_t h) {
|
static bool close(handle_t h) {
|
||||||
if (h == invalid()) return false;
|
if (h == invalid()) return false;
|
||||||
IPC_SEMAPHORE_FUNC_(sem_close, h);
|
IPC_SEMAPHORE_FUNC_(sem_close, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool destroy(char const* name) {
|
static bool destroy(char const* name) {
|
||||||
IPC_SEMAPHORE_FUNC_(sem_unlink, name);
|
IPC_SEMAPHORE_FUNC_(sem_unlink, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool post(handle_t h) {
|
static bool post(handle_t h) {
|
||||||
if (h == invalid()) return false;
|
if (h == invalid()) return false;
|
||||||
IPC_SEMAPHORE_FUNC_(sem_post, h);
|
IPC_SEMAPHORE_FUNC_(sem_post, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool wait(handle_t h, std::size_t tm = invalid_value) {
|
static bool wait(handle_t h, std::size_t tm = invalid_value) {
|
||||||
if (h == invalid()) return false;
|
if (h == invalid()) return false;
|
||||||
switch (tm) {
|
switch (tm) {
|
||||||
case 0:
|
case 0:
|
||||||
return true;
|
return true;
|
||||||
case invalid_value:
|
case invalid_value:
|
||||||
IPC_SEMAPHORE_FUNC_(sem_wait, h);
|
IPC_SEMAPHORE_FUNC_(sem_wait, h);
|
||||||
default: {
|
default: {
|
||||||
timespec ts;
|
timespec ts;
|
||||||
calc_wait_time(ts, tm);
|
calc_wait_time(ts, tm);
|
||||||
if (::sem_timedwait(h, &ts) != 0) {
|
if (::sem_timedwait(h, &ts) != 0) {
|
||||||
if (errno != ETIMEDOUT) {
|
if (errno != ETIMEDOUT) {
|
||||||
ipc::error("fail sem_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
|
ipc::error("fail sem_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
|
||||||
errno, tm, ts.tv_sec, ts.tv_nsec);
|
errno, tm, ts.tv_sec, ts.tv_nsec);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma pop_macro("IPC_SEMAPHORE_FUNC_")
|
#pragma pop_macro("IPC_SEMAPHORE_FUNC_")
|
||||||
};
|
};
|
||||||
|
|
||||||
class waiter_helper {
|
class waiter_helper {
|
||||||
mutex lock_;
|
mutex lock_;
|
||||||
|
|
||||||
std::atomic<unsigned> waiting_ { 0 };
|
std::atomic<unsigned> waiting_ { 0 };
|
||||||
long counter_ = 0;
|
long counter_ = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using handle_t = std::tuple<ipc::string, sem_helper::handle_t, sem_helper::handle_t>;
|
using handle_t = std::tuple<ipc::string, sem_helper::handle_t, sem_helper::handle_t>;
|
||||||
|
|
||||||
static handle_t invalid() noexcept {
|
static handle_t invalid() noexcept {
|
||||||
return std::make_tuple(ipc::string{}, sem_helper::invalid(), sem_helper::invalid());
|
return std::make_tuple(ipc::string{}, sem_helper::invalid(), sem_helper::invalid());
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_t open_h(ipc::string && name) {
|
handle_t open_h(ipc::string && name) {
|
||||||
auto sem = sem_helper::open(("__WAITER_HELPER_SEM__" + name).c_str(), 0);
|
auto sem = sem_helper::open(("__WAITER_HELPER_SEM__" + name).c_str(), 0);
|
||||||
if (sem == sem_helper::invalid()) {
|
if (sem == sem_helper::invalid()) {
|
||||||
return invalid();
|
return invalid();
|
||||||
}
|
}
|
||||||
auto han = sem_helper::open(("__WAITER_HELPER_HAN__" + name).c_str(), 0);
|
auto han = sem_helper::open(("__WAITER_HELPER_HAN__" + name).c_str(), 0);
|
||||||
if (han == sem_helper::invalid()) {
|
if (han == sem_helper::invalid()) {
|
||||||
return invalid();
|
return invalid();
|
||||||
}
|
}
|
||||||
return std::make_tuple(std::move(name), sem, han);
|
return std::make_tuple(std::move(name), sem, han);
|
||||||
}
|
}
|
||||||
|
|
||||||
void release_h(handle_t const & h) {
|
void release_h(handle_t const & h) {
|
||||||
sem_helper::close(std::get<2>(h));
|
sem_helper::close(std::get<2>(h));
|
||||||
sem_helper::close(std::get<1>(h));
|
sem_helper::close(std::get<1>(h));
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_h(handle_t const & h) {
|
void close_h(handle_t const & h) {
|
||||||
auto const & name = std::get<0>(h);
|
auto const & name = std::get<0>(h);
|
||||||
sem_helper::destroy(("__WAITER_HELPER_HAN__" + name).c_str());
|
sem_helper::destroy(("__WAITER_HELPER_HAN__" + name).c_str());
|
||||||
sem_helper::destroy(("__WAITER_HELPER_SEM__" + name).c_str());
|
sem_helper::destroy(("__WAITER_HELPER_SEM__" + name).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open() {
|
bool open() {
|
||||||
return lock_.open();
|
return lock_.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
lock_.close();
|
lock_.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool wait_if(handle_t const & h, F&& pred, std::size_t tm = invalid_value) {
|
bool wait_if(handle_t const & h, F&& pred, std::size_t tm = invalid_value) {
|
||||||
waiting_.fetch_add(1, std::memory_order_release);
|
waiting_.fetch_add(1, std::memory_order_release);
|
||||||
{
|
{
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
if (!std::forward<F>(pred)()) return true;
|
if (!std::forward<F>(pred)()) return true;
|
||||||
++ counter_;
|
++ counter_;
|
||||||
}
|
}
|
||||||
bool ret = sem_helper::wait(std::get<1>(h), tm);
|
bool ret = sem_helper::wait(std::get<1>(h), tm);
|
||||||
waiting_.fetch_sub(1, std::memory_order_release);
|
waiting_.fetch_sub(1, std::memory_order_release);
|
||||||
ret = sem_helper::post(std::get<2>(h)) && ret;
|
ret = sem_helper::post(std::get<2>(h)) && ret;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notify(handle_t const & h) {
|
bool notify(handle_t const & h) {
|
||||||
std::atomic_thread_fence(std::memory_order_acq_rel);
|
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||||
if (waiting_.load(std::memory_order_relaxed) == 0) {
|
if (waiting_.load(std::memory_order_relaxed) == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
if (counter_ > 0) {
|
if (counter_ > 0) {
|
||||||
ret = sem_helper::post(std::get<1>(h));
|
ret = sem_helper::post(std::get<1>(h));
|
||||||
-- counter_;
|
-- counter_;
|
||||||
ret = ret && sem_helper::wait(std::get<2>(h), default_timeut);
|
ret = ret && sem_helper::wait(std::get<2>(h), default_timeut);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool broadcast(handle_t const & h) {
|
bool broadcast(handle_t const & h) {
|
||||||
std::atomic_thread_fence(std::memory_order_acq_rel);
|
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||||
if (waiting_.load(std::memory_order_relaxed) == 0) {
|
if (waiting_.load(std::memory_order_relaxed) == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
if (counter_ > 0) {
|
if (counter_ > 0) {
|
||||||
for (long i = 0; i < counter_; ++i) {
|
for (long i = 0; i < counter_; ++i) {
|
||||||
ret = ret && sem_helper::post(std::get<1>(h));
|
ret = ret && sem_helper::post(std::get<1>(h));
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
-- counter_;
|
-- counter_;
|
||||||
ret = ret && sem_helper::wait(std::get<2>(h), default_timeut);
|
ret = ret && sem_helper::wait(std::get<2>(h), default_timeut);
|
||||||
} while (counter_ > 0);
|
} while (counter_ > 0);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class waiter {
|
class waiter {
|
||||||
waiter_helper helper_;
|
waiter_helper helper_;
|
||||||
std::atomic<unsigned> opened_ { 0 };
|
std::atomic<unsigned> opened_ { 0 };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using handle_t = waiter_helper::handle_t;
|
using handle_t = waiter_helper::handle_t;
|
||||||
|
|
||||||
static handle_t invalid() noexcept {
|
static handle_t invalid() noexcept {
|
||||||
return waiter_helper::invalid();
|
return waiter_helper::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_t open(char const * name) {
|
handle_t open(char const * name) {
|
||||||
if (name == nullptr || name[0] == '\0') {
|
if (name == nullptr || name[0] == '\0') {
|
||||||
return invalid();
|
return invalid();
|
||||||
}
|
}
|
||||||
if ((opened_.fetch_add(1, std::memory_order_acq_rel) == 0) && !helper_.open()) {
|
if ((opened_.fetch_add(1, std::memory_order_acq_rel) == 0) && !helper_.open()) {
|
||||||
return invalid();
|
return invalid();
|
||||||
}
|
}
|
||||||
return helper_.open_h(name);
|
return helper_.open_h(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void close(handle_t h) {
|
void close(handle_t h) {
|
||||||
if (h == invalid()) return;
|
if (h == invalid()) return;
|
||||||
helper_.release_h(h);
|
helper_.release_h(h);
|
||||||
if (opened_.fetch_sub(1, std::memory_order_release) == 1) {
|
if (opened_.fetch_sub(1, std::memory_order_release) == 1) {
|
||||||
helper_.close_h(h);
|
helper_.close_h(h);
|
||||||
helper_.close();
|
helper_.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool wait_if(handle_t h, F&& pred, std::size_t tm = invalid_value) {
|
bool wait_if(handle_t h, F&& pred, std::size_t tm = invalid_value) {
|
||||||
if (h == invalid()) return false;
|
if (h == invalid()) return false;
|
||||||
return helper_.wait_if(h, std::forward<F>(pred), tm);
|
return helper_.wait_if(h, std::forward<F>(pred), tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify(handle_t h) {
|
void notify(handle_t h) {
|
||||||
if (h == invalid()) return;
|
if (h == invalid()) return;
|
||||||
helper_.notify(h);
|
helper_.notify(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
void broadcast(handle_t h) {
|
void broadcast(handle_t h) {
|
||||||
if (h == invalid()) return;
|
if (h == invalid()) return;
|
||||||
helper_.broadcast(h);
|
helper_.broadcast(h);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,215 +1,215 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include "rw_lock.h"
|
#include "rw_lock.h"
|
||||||
#include "pool_alloc.h"
|
#include "pool_alloc.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
|
|
||||||
#include "platform/to_tchar.h"
|
#include "platform/to_tchar.h"
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class semaphore {
|
class semaphore {
|
||||||
HANDLE h_ = NULL;
|
HANDLE h_ = NULL;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void remove(char const * /*name*/) {}
|
static void remove(char const * /*name*/) {}
|
||||||
|
|
||||||
bool open(ipc::string && name, long count = 0, long limit = LONG_MAX) {
|
bool open(ipc::string && name, long count = 0, long limit = LONG_MAX) {
|
||||||
h_ = ::CreateSemaphore(NULL, count, limit, ipc::detail::to_tchar(std::move(name)).c_str());
|
h_ = ::CreateSemaphore(NULL, count, limit, ipc::detail::to_tchar(std::move(name)).c_str());
|
||||||
if (h_ == NULL) {
|
if (h_ == NULL) {
|
||||||
ipc::error("fail CreateSemaphore[%lu]: %s\n", ::GetLastError(), name.c_str());
|
ipc::error("fail CreateSemaphore[%lu]: %s\n", ::GetLastError(), name.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
::CloseHandle(h_);
|
::CloseHandle(h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wait(std::size_t tm = invalid_value) {
|
bool wait(std::size_t tm = invalid_value) {
|
||||||
DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
||||||
switch ((ret = ::WaitForSingleObject(h_, ms))) {
|
switch ((ret = ::WaitForSingleObject(h_, ms))) {
|
||||||
case WAIT_OBJECT_0:
|
case WAIT_OBJECT_0:
|
||||||
return true;
|
return true;
|
||||||
case WAIT_ABANDONED:
|
case WAIT_ABANDONED:
|
||||||
case WAIT_TIMEOUT:
|
case WAIT_TIMEOUT:
|
||||||
default:
|
default:
|
||||||
ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret);
|
ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool post(long count = 1) {
|
bool post(long count = 1) {
|
||||||
if (::ReleaseSemaphore(h_, count, NULL)) {
|
if (::ReleaseSemaphore(h_, count, NULL)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ipc::error("fail ReleaseSemaphore[%lu]\n", ::GetLastError());
|
ipc::error("fail ReleaseSemaphore[%lu]\n", ::GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class mutex : public semaphore {
|
class mutex : public semaphore {
|
||||||
using semaphore::wait;
|
using semaphore::wait;
|
||||||
using semaphore::post;
|
using semaphore::post;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool open(ipc::string && name) {
|
bool open(ipc::string && name) {
|
||||||
return semaphore::open(std::move(name), 1, 1);
|
return semaphore::open(std::move(name), 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lock () { return semaphore::wait(); }
|
bool lock () { return semaphore::wait(); }
|
||||||
bool unlock() { return semaphore::post(); }
|
bool unlock() { return semaphore::post(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class condition {
|
class condition {
|
||||||
mutex lock_;
|
mutex lock_;
|
||||||
semaphore sema_, handshake_;
|
semaphore sema_, handshake_;
|
||||||
|
|
||||||
std::atomic<unsigned> * waiting_ = nullptr;
|
std::atomic<unsigned> * waiting_ = nullptr;
|
||||||
long * counter_ = nullptr;
|
long * counter_ = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
friend bool operator==(condition const & c1, condition const & c2) {
|
friend bool operator==(condition const & c1, condition const & c2) {
|
||||||
return (c1.waiting_ == c2.waiting_) && (c1.counter_ == c2.counter_);
|
return (c1.waiting_ == c2.waiting_) && (c1.counter_ == c2.counter_);
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator!=(condition const & c1, condition const & c2) {
|
friend bool operator!=(condition const & c1, condition const & c2) {
|
||||||
return !(c1 == c2);
|
return !(c1 == c2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove(char const * name) {
|
static void remove(char const * name) {
|
||||||
semaphore::remove((ipc::string{ "__COND_HAN__" } + name).c_str());
|
semaphore::remove((ipc::string{ "__COND_HAN__" } + name).c_str());
|
||||||
semaphore::remove((ipc::string{ "__COND_SEM__" } + name).c_str());
|
semaphore::remove((ipc::string{ "__COND_SEM__" } + name).c_str());
|
||||||
mutex ::remove((ipc::string{ "__COND_MTX__" } + name).c_str());
|
mutex ::remove((ipc::string{ "__COND_MTX__" } + name).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open(ipc::string const & name, std::atomic<unsigned> * waiting, long * counter) {
|
bool open(ipc::string const & name, std::atomic<unsigned> * waiting, long * counter) {
|
||||||
if (lock_ .open("__COND_MTX__" + name) &&
|
if (lock_ .open("__COND_MTX__" + name) &&
|
||||||
sema_ .open("__COND_SEM__" + name) &&
|
sema_ .open("__COND_SEM__" + name) &&
|
||||||
handshake_.open("__COND_HAN__" + name)) {
|
handshake_.open("__COND_HAN__" + name)) {
|
||||||
waiting_ = waiting;
|
waiting_ = waiting;
|
||||||
counter_ = counter;
|
counter_ = counter;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
handshake_.close();
|
handshake_.close();
|
||||||
sema_ .close();
|
sema_ .close();
|
||||||
lock_ .close();
|
lock_ .close();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Mutex, typename F>
|
template <typename Mutex, typename F>
|
||||||
bool wait_if(Mutex& mtx, F&& pred, std::size_t tm = invalid_value) {
|
bool wait_if(Mutex& mtx, F&& pred, std::size_t tm = invalid_value) {
|
||||||
waiting_->fetch_add(1, std::memory_order_release);
|
waiting_->fetch_add(1, std::memory_order_release);
|
||||||
{
|
{
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
if (!std::forward<F>(pred)()) return true;
|
if (!std::forward<F>(pred)()) return true;
|
||||||
++ *counter_;
|
++ *counter_;
|
||||||
}
|
}
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
bool ret = sema_.wait(tm);
|
bool ret = sema_.wait(tm);
|
||||||
waiting_->fetch_sub(1, std::memory_order_release);
|
waiting_->fetch_sub(1, std::memory_order_release);
|
||||||
ret = handshake_.post() && ret;
|
ret = handshake_.post() && ret;
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notify() {
|
bool notify() {
|
||||||
std::atomic_thread_fence(std::memory_order_acq_rel);
|
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||||
if (waiting_->load(std::memory_order_relaxed) == 0) {
|
if (waiting_->load(std::memory_order_relaxed) == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
if (*counter_ > 0) {
|
if (*counter_ > 0) {
|
||||||
ret = sema_.post();
|
ret = sema_.post();
|
||||||
-- *counter_;
|
-- *counter_;
|
||||||
ret = ret && handshake_.wait(default_timeut);
|
ret = ret && handshake_.wait(default_timeut);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool broadcast() {
|
bool broadcast() {
|
||||||
std::atomic_thread_fence(std::memory_order_acq_rel);
|
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||||
if (waiting_->load(std::memory_order_relaxed) == 0) {
|
if (waiting_->load(std::memory_order_relaxed) == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
if (*counter_ > 0) {
|
if (*counter_ > 0) {
|
||||||
ret = sema_.post(*counter_);
|
ret = sema_.post(*counter_);
|
||||||
do {
|
do {
|
||||||
-- *counter_;
|
-- *counter_;
|
||||||
ret = ret && handshake_.wait(default_timeut);
|
ret = ret && handshake_.wait(default_timeut);
|
||||||
} while (*counter_ > 0);
|
} while (*counter_ > 0);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class waiter {
|
class waiter {
|
||||||
|
|
||||||
std::atomic<unsigned> waiting_ { 0 };
|
std::atomic<unsigned> waiting_ { 0 };
|
||||||
long counter_ = 0;
|
long counter_ = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using handle_t = condition;
|
using handle_t = condition;
|
||||||
|
|
||||||
static handle_t invalid() {
|
static handle_t invalid() {
|
||||||
return condition {};
|
return condition {};
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_t open(char const * name) {
|
handle_t open(char const * name) {
|
||||||
if (name == nullptr || name[0] == '\0') {
|
if (name == nullptr || name[0] == '\0') {
|
||||||
return invalid();
|
return invalid();
|
||||||
}
|
}
|
||||||
condition cond;
|
condition cond;
|
||||||
if (cond.open(name, &waiting_, &counter_)) {
|
if (cond.open(name, &waiting_, &counter_)) {
|
||||||
return cond;
|
return cond;
|
||||||
}
|
}
|
||||||
return invalid();
|
return invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void close(handle_t& h) {
|
void close(handle_t& h) {
|
||||||
if (h == invalid()) return;
|
if (h == invalid()) return;
|
||||||
h.close();
|
h.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool wait_if(handle_t& h, F&& pred, std::size_t tm = invalid_value) {
|
bool wait_if(handle_t& h, F&& pred, std::size_t tm = invalid_value) {
|
||||||
if (h == invalid()) return false;
|
if (h == invalid()) return false;
|
||||||
|
|
||||||
class non_mutex {
|
class non_mutex {
|
||||||
public:
|
public:
|
||||||
void lock () noexcept {}
|
void lock () noexcept {}
|
||||||
void unlock() noexcept {}
|
void unlock() noexcept {}
|
||||||
} nm;
|
} nm;
|
||||||
|
|
||||||
return h.wait_if(nm, std::forward<F>(pred), tm);
|
return h.wait_if(nm, std::forward<F>(pred), tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify(handle_t& h) {
|
void notify(handle_t& h) {
|
||||||
if (h == invalid()) return;
|
if (h == invalid()) return;
|
||||||
h.notify();
|
h.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void broadcast(handle_t& h) {
|
void broadcast(handle_t& h) {
|
||||||
if (h == invalid()) return;
|
if (h == invalid()) return;
|
||||||
h.broadcast();
|
h.broadcast();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,286 +1,286 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
|
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \
|
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \
|
||||||
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
|
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
|
||||||
defined(WINCE) || defined(_WIN32_WCE)
|
defined(WINCE) || defined(_WIN32_WCE)
|
||||||
|
|
||||||
#include "platform/waiter_win.h"
|
#include "platform/waiter_win.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
using mutex_impl = ipc::detail::mutex;
|
using mutex_impl = ipc::detail::mutex;
|
||||||
using semaphore_impl = ipc::detail::semaphore;
|
using semaphore_impl = ipc::detail::semaphore;
|
||||||
|
|
||||||
class condition_impl : public ipc::detail::condition {
|
class condition_impl : public ipc::detail::condition {
|
||||||
|
|
||||||
ipc::shm::handle wait_h_, cnt_h_;
|
ipc::shm::handle wait_h_, cnt_h_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void remove(char const * name) {
|
static void remove(char const * name) {
|
||||||
ipc::detail::condition::remove(name);
|
ipc::detail::condition::remove(name);
|
||||||
ipc::string n = name;
|
ipc::string n = name;
|
||||||
ipc::shm::remove((n + "__COND_CNT__" ).c_str());
|
ipc::shm::remove((n + "__COND_CNT__" ).c_str());
|
||||||
ipc::shm::remove((n + "__COND_WAIT__").c_str());
|
ipc::shm::remove((n + "__COND_WAIT__").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open(ipc::string const & name) {
|
bool open(ipc::string const & name) {
|
||||||
if (wait_h_.acquire((name + "__COND_WAIT__").c_str(), sizeof(std::atomic<unsigned>)) &&
|
if (wait_h_.acquire((name + "__COND_WAIT__").c_str(), sizeof(std::atomic<unsigned>)) &&
|
||||||
cnt_h_ .acquire((name + "__COND_CNT__" ).c_str(), sizeof(long))) {
|
cnt_h_ .acquire((name + "__COND_CNT__" ).c_str(), sizeof(long))) {
|
||||||
return ipc::detail::condition::open(name,
|
return ipc::detail::condition::open(name,
|
||||||
static_cast<std::atomic<unsigned> *>(wait_h_.get()),
|
static_cast<std::atomic<unsigned> *>(wait_h_.get()),
|
||||||
static_cast<long *>(cnt_h_.get()));
|
static_cast<long *>(cnt_h_.get()));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
ipc::detail::condition::close();
|
ipc::detail::condition::close();
|
||||||
cnt_h_ .release();
|
cnt_h_ .release();
|
||||||
wait_h_.release();
|
wait_h_.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) {
|
bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) {
|
||||||
return ipc::detail::condition::wait_if(mtx, [] { return true; }, tm);
|
return ipc::detail::condition::wait_if(mtx, [] { return true; }, tm);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|
||||||
#else /*!WIN*/
|
#else /*!WIN*/
|
||||||
|
|
||||||
#include "platform/waiter_linux.h"
|
#include "platform/waiter_linux.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class object_impl {
|
class object_impl {
|
||||||
ipc::shm::handle h_;
|
ipc::shm::handle h_;
|
||||||
|
|
||||||
struct info_t {
|
struct info_t {
|
||||||
T object_;
|
T object_;
|
||||||
std::atomic<unsigned> opened_;
|
std::atomic<unsigned> opened_;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void remove(char const * name) {
|
static void remove(char const * name) {
|
||||||
{
|
{
|
||||||
ipc::shm::handle h { name, sizeof(info_t) };
|
ipc::shm::handle h { name, sizeof(info_t) };
|
||||||
if (h.valid()) {
|
if (h.valid()) {
|
||||||
auto info = static_cast<info_t*>(h.get());
|
auto info = static_cast<info_t*>(h.get());
|
||||||
info->object_.close();
|
info->object_.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ipc::shm::remove(name);
|
ipc::shm::remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
T& object() {
|
T& object() {
|
||||||
return static_cast<info_t*>(h_.get())->object_;
|
return static_cast<info_t*>(h_.get())->object_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... P>
|
template <typename... P>
|
||||||
bool open(char const * name, P&&... params) {
|
bool open(char const * name, P&&... params) {
|
||||||
if (!h_.acquire(name, sizeof(info_t))) {
|
if (!h_.acquire(name, sizeof(info_t))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto info = static_cast<info_t*>(h_.get());
|
auto info = static_cast<info_t*>(h_.get());
|
||||||
if ((info->opened_.fetch_add(1, std::memory_order_acq_rel) == 0) &&
|
if ((info->opened_.fetch_add(1, std::memory_order_acq_rel) == 0) &&
|
||||||
!info->object_.open(std::forward<P>(params)...)) {
|
!info->object_.open(std::forward<P>(params)...)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
if (!h_.valid()) return;
|
if (!h_.valid()) return;
|
||||||
auto info = static_cast<info_t*>(h_.get());
|
auto info = static_cast<info_t*>(h_.get());
|
||||||
if (info->opened_.fetch_sub(1, std::memory_order_release) == 1) {
|
if (info->opened_.fetch_sub(1, std::memory_order_release) == 1) {
|
||||||
info->object_.close();
|
info->object_.close();
|
||||||
}
|
}
|
||||||
h_.release();
|
h_.release();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class mutex_impl : public object_impl<ipc::detail::mutex> {
|
class mutex_impl : public object_impl<ipc::detail::mutex> {
|
||||||
public:
|
public:
|
||||||
bool lock () { return object().lock (); }
|
bool lock () { return object().lock (); }
|
||||||
bool unlock() { return object().unlock(); }
|
bool unlock() { return object().unlock(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class condition_impl : public object_impl<ipc::detail::condition> {
|
class condition_impl : public object_impl<ipc::detail::condition> {
|
||||||
public:
|
public:
|
||||||
bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) {
|
bool wait(mutex_impl& mtx, std::size_t tm = invalid_value) {
|
||||||
return object().wait(mtx.object(), tm);
|
return object().wait(mtx.object(), tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notify () { return object().notify (); }
|
bool notify () { return object().notify (); }
|
||||||
bool broadcast() { return object().broadcast(); }
|
bool broadcast() { return object().broadcast(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class semaphore_impl {
|
class semaphore_impl {
|
||||||
sem_helper::handle_t h_;
|
sem_helper::handle_t h_;
|
||||||
ipc::shm::handle opened_; // std::atomic<unsigned>
|
ipc::shm::handle opened_; // std::atomic<unsigned>
|
||||||
ipc::string name_;
|
ipc::string name_;
|
||||||
|
|
||||||
auto cnt() {
|
auto cnt() {
|
||||||
return static_cast<std::atomic<unsigned>*>(opened_.get());
|
return static_cast<std::atomic<unsigned>*>(opened_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void remove(char const * name) {
|
static void remove(char const * name) {
|
||||||
sem_helper::destroy((ipc::string{ "__SEMAPHORE_IMPL_SEM__" } + name).c_str());
|
sem_helper::destroy((ipc::string{ "__SEMAPHORE_IMPL_SEM__" } + name).c_str());
|
||||||
ipc::shm::remove ((ipc::string{ "__SEMAPHORE_IMPL_CNT__" } + name).c_str());
|
ipc::shm::remove ((ipc::string{ "__SEMAPHORE_IMPL_CNT__" } + name).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open(char const * name, long count) {
|
bool open(char const * name, long count) {
|
||||||
name_ = name;
|
name_ = name;
|
||||||
if (!opened_.acquire(("__SEMAPHORE_IMPL_CNT__" + name_).c_str(), sizeof(std::atomic<unsigned>))) {
|
if (!opened_.acquire(("__SEMAPHORE_IMPL_CNT__" + name_).c_str(), sizeof(std::atomic<unsigned>))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((h_ = sem_helper::open(("__SEMAPHORE_IMPL_SEM__" + name_).c_str(), count)) == sem_helper::invalid()) {
|
if ((h_ = sem_helper::open(("__SEMAPHORE_IMPL_SEM__" + name_).c_str(), count)) == sem_helper::invalid()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
cnt()->fetch_add(1, std::memory_order_acq_rel);
|
cnt()->fetch_add(1, std::memory_order_acq_rel);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
if (h_ == sem_helper::invalid()) return;
|
if (h_ == sem_helper::invalid()) return;
|
||||||
sem_helper::close(h_);
|
sem_helper::close(h_);
|
||||||
if (cnt() == nullptr) return;
|
if (cnt() == nullptr) return;
|
||||||
if (cnt()->fetch_sub(1, std::memory_order_release) == 1) {
|
if (cnt()->fetch_sub(1, std::memory_order_release) == 1) {
|
||||||
sem_helper::destroy(("__SEMAPHORE_IMPL_SEM__" + name_).c_str());
|
sem_helper::destroy(("__SEMAPHORE_IMPL_SEM__" + name_).c_str());
|
||||||
}
|
}
|
||||||
opened_.release();
|
opened_.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wait(std::size_t tm = invalid_value) {
|
bool wait(std::size_t tm = invalid_value) {
|
||||||
if (h_ == sem_helper::invalid()) return false;
|
if (h_ == sem_helper::invalid()) return false;
|
||||||
return sem_helper::wait(h_, tm);
|
return sem_helper::wait(h_, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool post(long count) {
|
bool post(long count) {
|
||||||
if (h_ == sem_helper::invalid()) return false;
|
if (h_ == sem_helper::invalid()) return false;
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
for (long i = 0; i < count; ++i) {
|
for (long i = 0; i < count; ++i) {
|
||||||
ret = ret && sem_helper::post(h_);
|
ret = ret && sem_helper::post(h_);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|
||||||
#endif/*!WIN*/
|
#endif/*!WIN*/
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class waiter_wrapper {
|
class waiter_wrapper {
|
||||||
public:
|
public:
|
||||||
using waiter_t = detail::waiter;
|
using waiter_t = detail::waiter;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
waiter_t* w_ = nullptr;
|
waiter_t* w_ = nullptr;
|
||||||
waiter_t::handle_t h_ = waiter_t::invalid();
|
waiter_t::handle_t h_ = waiter_t::invalid();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
waiter_wrapper() = default;
|
waiter_wrapper() = default;
|
||||||
explicit waiter_wrapper(waiter_t* w) {
|
explicit waiter_wrapper(waiter_t* w) {
|
||||||
attach(w);
|
attach(w);
|
||||||
}
|
}
|
||||||
waiter_wrapper(const waiter_wrapper&) = delete;
|
waiter_wrapper(const waiter_wrapper&) = delete;
|
||||||
waiter_wrapper& operator=(const waiter_wrapper&) = delete;
|
waiter_wrapper& operator=(const waiter_wrapper&) = delete;
|
||||||
|
|
||||||
waiter_t * waiter() { return w_; }
|
waiter_t * waiter() { return w_; }
|
||||||
waiter_t const * waiter() const { return w_; }
|
waiter_t const * waiter() const { return w_; }
|
||||||
|
|
||||||
void attach(waiter_t* w) {
|
void attach(waiter_t* w) {
|
||||||
close();
|
close();
|
||||||
w_ = w;
|
w_ = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool valid() const {
|
bool valid() const {
|
||||||
return (w_ != nullptr) && (h_ != waiter_t::invalid());
|
return (w_ != nullptr) && (h_ != waiter_t::invalid());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open(char const * name) {
|
bool open(char const * name) {
|
||||||
if (w_ == nullptr) return false;
|
if (w_ == nullptr) return false;
|
||||||
close();
|
close();
|
||||||
h_ = w_->open(name);
|
h_ = w_->open(name);
|
||||||
return valid();
|
return valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
if (!valid()) return;
|
if (!valid()) return;
|
||||||
w_->close(h_);
|
w_->close(h_);
|
||||||
h_ = waiter_t::invalid();
|
h_ = waiter_t::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool wait_if(F&& pred, std::size_t tm = invalid_value) {
|
bool wait_if(F&& pred, std::size_t tm = invalid_value) {
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
return w_->wait_if(h_, std::forward<F>(pred), tm);
|
return w_->wait_if(h_, std::forward<F>(pred), tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notify() {
|
bool notify() {
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
w_->notify(h_);
|
w_->notify(h_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool broadcast() {
|
bool broadcast() {
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
w_->broadcast(h_);
|
w_->broadcast(h_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
class waiter : public detail::waiter_wrapper {
|
class waiter : public detail::waiter_wrapper {
|
||||||
|
|
||||||
shm::handle shm_;
|
shm::handle shm_;
|
||||||
|
|
||||||
using detail::waiter_wrapper::attach;
|
using detail::waiter_wrapper::attach;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
waiter() = default;
|
waiter() = default;
|
||||||
waiter(char const * name) {
|
waiter(char const * name) {
|
||||||
open(name);
|
open(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
~waiter() {
|
~waiter() {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open(char const * name) {
|
bool open(char const * name) {
|
||||||
if (name == nullptr || name[0] == '\0') {
|
if (name == nullptr || name[0] == '\0') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
if (!shm_.acquire((ipc::string{ "__SHM_WAITER__" } + name).c_str(), sizeof(waiter_t))) {
|
if (!shm_.acquire((ipc::string{ "__SHM_WAITER__" } + name).c_str(), sizeof(waiter_t))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
attach(static_cast<waiter_t*>(shm_.get()));
|
attach(static_cast<waiter_t*>(shm_.get()));
|
||||||
return detail::waiter_wrapper::open((ipc::string{ "__IMP_WAITER__" } + name).c_str());
|
return detail::waiter_wrapper::open((ipc::string{ "__IMP_WAITER__" } + name).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
detail::waiter_wrapper::close();
|
detail::waiter_wrapper::close();
|
||||||
shm_.release();
|
shm_.release();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
46
src/policy.h
46
src/policy.h
@ -1,23 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "prod_cons.h"
|
#include "prod_cons.h"
|
||||||
|
|
||||||
#include "circ/elem_array.h"
|
#include "circ/elem_array.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace policy {
|
namespace policy {
|
||||||
|
|
||||||
template <template <typename, std::size_t...> class Elems, typename Flag>
|
template <template <typename, std::size_t...> class Elems, typename Flag>
|
||||||
struct choose;
|
struct choose;
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
struct choose<circ::elem_array, Flag> {
|
struct choose<circ::elem_array, Flag> {
|
||||||
template <std::size_t DataSize, std::size_t AlignSize>
|
template <std::size_t DataSize, std::size_t AlignSize>
|
||||||
using elems_t = circ::elem_array<ipc::prod_cons_impl<Flag>, DataSize, AlignSize>;
|
using elems_t = circ::elem_array<ipc::prod_cons_impl<Flag>, DataSize, AlignSize>;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace policy
|
} // namespace policy
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
#include "pool_alloc.h"
|
#include "pool_alloc.h"
|
||||||
|
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace mem {
|
namespace mem {
|
||||||
|
|
||||||
void pool_alloc::clear() {
|
void pool_alloc::clear() {
|
||||||
async_pool_alloc::clear();
|
async_pool_alloc::clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void* pool_alloc::alloc(std::size_t size) {
|
void* pool_alloc::alloc(std::size_t size) {
|
||||||
return async_pool_alloc::alloc(size);
|
return async_pool_alloc::alloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pool_alloc::free(void* p, std::size_t size) {
|
void pool_alloc::free(void* p, std::size_t size) {
|
||||||
async_pool_alloc::free(p, size);
|
async_pool_alloc::free(p, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mem
|
} // namespace mem
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
758
src/prod_cons.h
758
src/prod_cons.h
@ -1,379 +1,379 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
#include "circ/elem_def.h"
|
#include "circ/elem_def.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
/// producer-consumer implementation
|
/// producer-consumer implementation
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
struct prod_cons_impl;
|
struct prod_cons_impl;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
|
struct prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
|
||||||
|
|
||||||
template <std::size_t DataSize, std::size_t AlignSize>
|
template <std::size_t DataSize, std::size_t AlignSize>
|
||||||
struct elem_t {
|
struct elem_t {
|
||||||
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
||||||
};
|
};
|
||||||
|
|
||||||
alignas(detail::cache_line_size) std::atomic<circ::u2_t> rd_; // read index
|
alignas(detail::cache_line_size) std::atomic<circ::u2_t> rd_; // read index
|
||||||
alignas(detail::cache_line_size) std::atomic<circ::u2_t> wt_; // write index
|
alignas(detail::cache_line_size) std::atomic<circ::u2_t> wt_; // write index
|
||||||
|
|
||||||
constexpr circ::u2_t cursor() const noexcept {
|
constexpr circ::u2_t cursor() const noexcept {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool push(W* /*wrapper*/, F&& f, E* elems) {
|
bool push(W* /*wrapper*/, F&& f, E* elems) {
|
||||||
auto cur_wt = circ::index_of(wt_.load(std::memory_order_relaxed));
|
auto cur_wt = circ::index_of(wt_.load(std::memory_order_relaxed));
|
||||||
if (cur_wt == circ::index_of(rd_.load(std::memory_order_acquire) - 1)) {
|
if (cur_wt == circ::index_of(rd_.load(std::memory_order_acquire) - 1)) {
|
||||||
return false; // full
|
return false; // full
|
||||||
}
|
}
|
||||||
std::forward<F>(f)(&(elems[cur_wt].data_));
|
std::forward<F>(f)(&(elems[cur_wt].data_));
|
||||||
wt_.fetch_add(1, std::memory_order_release);
|
wt_.fetch_add(1, std::memory_order_release);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool force_push(W* wrapper, F&& f, E* elems) {
|
bool force_push(W* wrapper, F&& f, E* elems) {
|
||||||
return push(wrapper, std::forward<F>(f), elems);
|
return push(wrapper, std::forward<F>(f), elems);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E* elems) {
|
bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E* elems) {
|
||||||
auto cur_rd = circ::index_of(rd_.load(std::memory_order_relaxed));
|
auto cur_rd = circ::index_of(rd_.load(std::memory_order_relaxed));
|
||||||
if (cur_rd == circ::index_of(wt_.load(std::memory_order_acquire))) {
|
if (cur_rd == circ::index_of(wt_.load(std::memory_order_acquire))) {
|
||||||
return false; // empty
|
return false; // empty
|
||||||
}
|
}
|
||||||
std::forward<F>(f)(&(elems[cur_rd].data_));
|
std::forward<F>(f)(&(elems[cur_rd].data_));
|
||||||
rd_.fetch_add(1, std::memory_order_release);
|
rd_.fetch_add(1, std::memory_order_release);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct prod_cons_impl<wr<relat::single, relat::multi , trans::unicast>>
|
struct prod_cons_impl<wr<relat::single, relat::multi , trans::unicast>>
|
||||||
: prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
|
: prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
|
||||||
|
|
||||||
template <typename W, typename F, template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS>
|
template <typename W, typename F, template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS>
|
||||||
bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E<DS, AS>* elems) {
|
bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E<DS, AS>* elems) {
|
||||||
byte_t buff[DS];
|
byte_t buff[DS];
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
auto cur_rd = rd_.load(std::memory_order_relaxed);
|
auto cur_rd = rd_.load(std::memory_order_relaxed);
|
||||||
if (circ::index_of(cur_rd) ==
|
if (circ::index_of(cur_rd) ==
|
||||||
circ::index_of(wt_.load(std::memory_order_acquire))) {
|
circ::index_of(wt_.load(std::memory_order_acquire))) {
|
||||||
return false; // empty
|
return false; // empty
|
||||||
}
|
}
|
||||||
std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
|
std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
|
||||||
if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
|
if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
|
||||||
std::forward<F>(f)(buff);
|
std::forward<F>(f)(buff);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ipc::yield(k);
|
ipc::yield(k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct prod_cons_impl<wr<relat::multi , relat::multi, trans::unicast>>
|
struct prod_cons_impl<wr<relat::multi , relat::multi, trans::unicast>>
|
||||||
: prod_cons_impl<wr<relat::single, relat::multi, trans::unicast>> {
|
: prod_cons_impl<wr<relat::single, relat::multi, trans::unicast>> {
|
||||||
|
|
||||||
using flag_t = std::uint64_t;
|
using flag_t = std::uint64_t;
|
||||||
|
|
||||||
template <std::size_t DataSize, std::size_t AlignSize>
|
template <std::size_t DataSize, std::size_t AlignSize>
|
||||||
struct elem_t {
|
struct elem_t {
|
||||||
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
||||||
std::atomic<flag_t> f_ct_ { 0 }; // commit flag
|
std::atomic<flag_t> f_ct_ { 0 }; // commit flag
|
||||||
};
|
};
|
||||||
|
|
||||||
alignas(detail::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
|
alignas(detail::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool push(W* /*wrapper*/, F&& f, E* elems) {
|
bool push(W* /*wrapper*/, F&& f, E* elems) {
|
||||||
circ::u2_t cur_ct, nxt_ct;
|
circ::u2_t cur_ct, nxt_ct;
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
cur_ct = ct_.load(std::memory_order_relaxed);
|
cur_ct = ct_.load(std::memory_order_relaxed);
|
||||||
if (circ::index_of(nxt_ct = cur_ct + 1) ==
|
if (circ::index_of(nxt_ct = cur_ct + 1) ==
|
||||||
circ::index_of(rd_.load(std::memory_order_acquire))) {
|
circ::index_of(rd_.load(std::memory_order_acquire))) {
|
||||||
return false; // full
|
return false; // full
|
||||||
}
|
}
|
||||||
if (ct_.compare_exchange_weak(cur_ct, nxt_ct, std::memory_order_release)) {
|
if (ct_.compare_exchange_weak(cur_ct, nxt_ct, std::memory_order_release)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ipc::yield(k);
|
ipc::yield(k);
|
||||||
}
|
}
|
||||||
auto* el = elems + circ::index_of(cur_ct);
|
auto* el = elems + circ::index_of(cur_ct);
|
||||||
std::forward<F>(f)(&(el->data_));
|
std::forward<F>(f)(&(el->data_));
|
||||||
// set flag & try update wt
|
// set flag & try update wt
|
||||||
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
|
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
|
||||||
while (1) {
|
while (1) {
|
||||||
auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
|
auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
|
||||||
if (cur_ct != wt_.load(std::memory_order_acquire)) {
|
if (cur_ct != wt_.load(std::memory_order_acquire)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ((~cac_ct) != cur_ct) {
|
if ((~cac_ct) != cur_ct) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!el->f_ct_.compare_exchange_strong(cac_ct, 0, std::memory_order_relaxed)) {
|
if (!el->f_ct_.compare_exchange_strong(cac_ct, 0, std::memory_order_relaxed)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
wt_.store(nxt_ct, std::memory_order_release);
|
wt_.store(nxt_ct, std::memory_order_release);
|
||||||
cur_ct = nxt_ct;
|
cur_ct = nxt_ct;
|
||||||
nxt_ct = cur_ct + 1;
|
nxt_ct = cur_ct + 1;
|
||||||
el = elems + circ::index_of(cur_ct);
|
el = elems + circ::index_of(cur_ct);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool force_push(W* wrapper, F&& f, E* elems) {
|
bool force_push(W* wrapper, F&& f, E* elems) {
|
||||||
return push(wrapper, std::forward<F>(f), elems); /* TBD */
|
return push(wrapper, std::forward<F>(f), elems); /* TBD */
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS>
|
template <typename W, typename F, template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS>
|
||||||
bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E<DS, AS>* elems) {
|
bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, E<DS, AS>* elems) {
|
||||||
byte_t buff[DS];
|
byte_t buff[DS];
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
auto cur_rd = rd_.load(std::memory_order_relaxed);
|
auto cur_rd = rd_.load(std::memory_order_relaxed);
|
||||||
auto cur_wt = wt_.load(std::memory_order_acquire);
|
auto cur_wt = wt_.load(std::memory_order_acquire);
|
||||||
auto id_rd = circ::index_of(cur_rd);
|
auto id_rd = circ::index_of(cur_rd);
|
||||||
auto id_wt = circ::index_of(cur_wt);
|
auto id_wt = circ::index_of(cur_wt);
|
||||||
if (id_rd == id_wt) {
|
if (id_rd == id_wt) {
|
||||||
auto* el = elems + id_wt;
|
auto* el = elems + id_wt;
|
||||||
auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
|
auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
|
||||||
if ((~cac_ct) != cur_wt) {
|
if ((~cac_ct) != cur_wt) {
|
||||||
return false; // empty
|
return false; // empty
|
||||||
}
|
}
|
||||||
if (el->f_ct_.compare_exchange_weak(cac_ct, 0, std::memory_order_relaxed)) {
|
if (el->f_ct_.compare_exchange_weak(cac_ct, 0, std::memory_order_relaxed)) {
|
||||||
wt_.store(cur_wt + 1, std::memory_order_release);
|
wt_.store(cur_wt + 1, std::memory_order_release);
|
||||||
}
|
}
|
||||||
k = 0;
|
k = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
|
std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
|
||||||
if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
|
if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
|
||||||
std::forward<F>(f)(buff);
|
std::forward<F>(f)(buff);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ipc::yield(k);
|
ipc::yield(k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct prod_cons_impl<wr<relat::single, relat::multi, trans::broadcast>> {
|
struct prod_cons_impl<wr<relat::single, relat::multi, trans::broadcast>> {
|
||||||
|
|
||||||
using rc_t = std::size_t;
|
using rc_t = std::uint32_t;
|
||||||
|
|
||||||
template <std::size_t DataSize, std::size_t AlignSize>
|
template <std::size_t DataSize, std::size_t AlignSize>
|
||||||
struct elem_t {
|
struct elem_t {
|
||||||
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
||||||
std::atomic<rc_t> rc_ { 0 }; // read-counter
|
std::atomic<rc_t> rc_ { 0 }; // read-counter
|
||||||
};
|
};
|
||||||
|
|
||||||
alignas(detail::cache_line_size) std::atomic<circ::u2_t> wt_; // write index
|
alignas(detail::cache_line_size) std::atomic<circ::u2_t> wt_; // write index
|
||||||
|
|
||||||
circ::u2_t cursor() const noexcept {
|
circ::u2_t cursor() const noexcept {
|
||||||
return wt_.load(std::memory_order_acquire);
|
return wt_.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool push(W* wrapper, F&& f, E* elems) {
|
bool push(W* wrapper, F&& f, E* elems) {
|
||||||
E* el;
|
E* el;
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
auto cc = wrapper->conn_count(std::memory_order_relaxed);
|
auto cc = wrapper->conn_count(std::memory_order_relaxed);
|
||||||
if (cc == 0) return false; // no reader
|
if (cc == 0) return false; // no reader
|
||||||
el = elems + circ::index_of(wt_.load(std::memory_order_acquire));
|
el = elems + circ::index_of(wt_.load(std::memory_order_acquire));
|
||||||
// check all consumers have finished reading this element
|
// check all consumers have finished reading this element
|
||||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||||
if (cur_rc) {
|
if (cur_rc) {
|
||||||
return false; // full
|
return false; // full
|
||||||
}
|
}
|
||||||
// cur_rc should be 0 here
|
// cur_rc should be 0 here
|
||||||
if (el->rc_.compare_exchange_weak(
|
if (el->rc_.compare_exchange_weak(
|
||||||
cur_rc, static_cast<rc_t>(cc), std::memory_order_release)) {
|
cur_rc, static_cast<rc_t>(cc), std::memory_order_release)) {
|
||||||
wrapper->clear_dis_flag(std::memory_order_relaxed);
|
wrapper->clear_dis_flag(std::memory_order_relaxed);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ipc::yield(k);
|
ipc::yield(k);
|
||||||
}
|
}
|
||||||
std::forward<F>(f)(&(el->data_));
|
std::forward<F>(f)(&(el->data_));
|
||||||
wt_.fetch_add(1, std::memory_order_release);
|
wt_.fetch_add(1, std::memory_order_release);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool force_push(W* wrapper, F&& f, E* elems) {
|
bool force_push(W* wrapper, F&& f, E* elems) {
|
||||||
E* el;
|
E* el;
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
auto cc = wrapper->conn_count(std::memory_order_relaxed);
|
auto cc = wrapper->conn_count(std::memory_order_relaxed);
|
||||||
if (cc == 0) return false; // no reader
|
if (cc == 0) return false; // no reader
|
||||||
el = elems + circ::index_of(wt_.load(std::memory_order_acquire));
|
el = elems + circ::index_of(wt_.load(std::memory_order_acquire));
|
||||||
// check all consumers have finished reading this element
|
// check all consumers have finished reading this element
|
||||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||||
if (cur_rc) {
|
if (cur_rc) {
|
||||||
wrapper->try_disconnect(); // try disconnect a reader
|
wrapper->try_disconnect(); // try disconnect a reader
|
||||||
cc = wrapper->conn_count(std::memory_order_relaxed);
|
cc = wrapper->conn_count(std::memory_order_relaxed);
|
||||||
if (cc == 0) return false; // no reader
|
if (cc == 0) return false; // no reader
|
||||||
}
|
}
|
||||||
// just compare & exchange
|
// just compare & exchange
|
||||||
if (el->rc_.compare_exchange_weak(
|
if (el->rc_.compare_exchange_weak(
|
||||||
cur_rc, static_cast<rc_t>(cc), std::memory_order_release)) {
|
cur_rc, static_cast<rc_t>(cc), std::memory_order_release)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ipc::yield(k);
|
ipc::yield(k);
|
||||||
}
|
}
|
||||||
std::forward<F>(f)(&(el->data_));
|
std::forward<F>(f)(&(el->data_));
|
||||||
wt_.fetch_add(1, std::memory_order_release);
|
wt_.fetch_add(1, std::memory_order_release);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool pop(W* /*wrapper*/, circ::u2_t& cur, F&& f, E* elems) {
|
bool pop(W* /*wrapper*/, circ::u2_t& cur, F&& f, E* elems) {
|
||||||
if (cur == cursor()) return false; // acquire
|
if (cur == cursor()) return false; // acquire
|
||||||
auto* el = elems + circ::index_of(cur++);
|
auto* el = elems + circ::index_of(cur++);
|
||||||
std::forward<F>(f)(&(el->data_));
|
std::forward<F>(f)(&(el->data_));
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
rc_t cur_rc = el->rc_.load(std::memory_order_acquire);
|
rc_t cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||||
if (cur_rc == 0) {
|
if (cur_rc == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (el->rc_.compare_exchange_weak(
|
if (el->rc_.compare_exchange_weak(
|
||||||
cur_rc, cur_rc - 1, std::memory_order_release)) {
|
cur_rc, cur_rc - 1, std::memory_order_release)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ipc::yield(k);
|
ipc::yield(k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct prod_cons_impl<wr<relat::multi , relat::multi, trans::broadcast>> {
|
struct prod_cons_impl<wr<relat::multi , relat::multi, trans::broadcast>> {
|
||||||
|
|
||||||
using rc_t = std::uint64_t;
|
using rc_t = std::uint64_t;
|
||||||
using flag_t = std::uint64_t;
|
using flag_t = std::uint64_t;
|
||||||
|
|
||||||
enum : rc_t {
|
enum : rc_t {
|
||||||
rc_mask = 0x00000000ffffffffull,
|
rc_mask = 0x00000000ffffffffull,
|
||||||
rc_incr = 0x0000000100000000ull
|
rc_incr = 0x0000000100000000ull
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t DataSize, std::size_t AlignSize>
|
template <std::size_t DataSize, std::size_t AlignSize>
|
||||||
struct elem_t {
|
struct elem_t {
|
||||||
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
||||||
std::atomic<rc_t > rc_ { 0 }; // read-counter
|
std::atomic<rc_t > rc_ { 0 }; // read-counter
|
||||||
std::atomic<flag_t> f_ct_ { 0 }; // commit flag
|
std::atomic<flag_t> f_ct_ { 0 }; // commit flag
|
||||||
};
|
};
|
||||||
|
|
||||||
alignas(detail::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
|
alignas(detail::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
|
||||||
|
|
||||||
circ::u2_t cursor() const noexcept {
|
circ::u2_t cursor() const noexcept {
|
||||||
return ct_.load(std::memory_order_acquire);
|
return ct_.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool push(W* wrapper, F&& f, E* elems) {
|
bool push(W* wrapper, F&& f, E* elems) {
|
||||||
E* el;
|
E* el;
|
||||||
circ::u2_t cur_ct;
|
circ::u2_t cur_ct;
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
auto cc = wrapper->conn_count(std::memory_order_relaxed);
|
auto cc = wrapper->conn_count(std::memory_order_relaxed);
|
||||||
if (cc == 0) return false; // no reader
|
if (cc == 0) return false; // no reader
|
||||||
el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
|
el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
|
||||||
// check all consumers have finished reading this element
|
// check all consumers have finished reading this element
|
||||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||||
if (cur_rc & rc_mask) {
|
if (cur_rc & rc_mask) {
|
||||||
return false; // full
|
return false; // full
|
||||||
}
|
}
|
||||||
auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
|
auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
|
||||||
if ((cur_fl != cur_ct) && cur_fl) {
|
if ((cur_fl != cur_ct) && cur_fl) {
|
||||||
return false; // full
|
return false; // full
|
||||||
}
|
}
|
||||||
// (cur_rc & rc_mask) should be 0 here
|
// (cur_rc & rc_mask) should be 0 here
|
||||||
if (el->rc_.compare_exchange_weak(
|
if (el->rc_.compare_exchange_weak(
|
||||||
cur_rc, static_cast<rc_t>(cc) | ((cur_rc & ~rc_mask) + rc_incr), std::memory_order_release)) {
|
cur_rc, static_cast<rc_t>(cc) | ((cur_rc & ~rc_mask) + rc_incr), std::memory_order_release)) {
|
||||||
wrapper->clear_dis_flag(std::memory_order_relaxed);
|
wrapper->clear_dis_flag(std::memory_order_relaxed);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ipc::yield(k);
|
ipc::yield(k);
|
||||||
}
|
}
|
||||||
// only one thread/process would touch here at one time
|
// only one thread/process would touch here at one time
|
||||||
ct_.store(cur_ct + 1, std::memory_order_release);
|
ct_.store(cur_ct + 1, std::memory_order_release);
|
||||||
std::forward<F>(f)(&(el->data_));
|
std::forward<F>(f)(&(el->data_));
|
||||||
// set flag & try update wt
|
// set flag & try update wt
|
||||||
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
|
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool force_push(W* wrapper, F&& f, E* elems) {
|
bool force_push(W* wrapper, F&& f, E* elems) {
|
||||||
E* el;
|
E* el;
|
||||||
circ::u2_t cur_ct;
|
circ::u2_t cur_ct;
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
auto cc = wrapper->conn_count(std::memory_order_relaxed);
|
auto cc = wrapper->conn_count(std::memory_order_relaxed);
|
||||||
if (cc == 0) return false; // no reader
|
if (cc == 0) return false; // no reader
|
||||||
el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
|
el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
|
||||||
// check all consumers have finished reading this element
|
// check all consumers have finished reading this element
|
||||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||||
ipc::log("force_push: k = %d, cc = %zd, rc = %zd\n", k, cc, (cur_rc & rc_mask));
|
ipc::log("force_push: k = %d, cc = %zd, rc = %zd\n", k, cc, (cur_rc & rc_mask));
|
||||||
if (cur_rc & rc_mask) {
|
if (cur_rc & rc_mask) {
|
||||||
wrapper->try_disconnect(); // try disconnect a reader
|
wrapper->try_disconnect(); // try disconnect a reader
|
||||||
cc = wrapper->conn_count(std::memory_order_relaxed);
|
cc = wrapper->conn_count(std::memory_order_relaxed);
|
||||||
if (cc == 0) return false; // no reader
|
if (cc == 0) return false; // no reader
|
||||||
}
|
}
|
||||||
// just compare & exchange
|
// just compare & exchange
|
||||||
if (el->rc_.compare_exchange_weak(
|
if (el->rc_.compare_exchange_weak(
|
||||||
cur_rc, static_cast<rc_t>(cc) | ((cur_rc & ~rc_mask) + rc_incr), std::memory_order_release)) {
|
cur_rc, static_cast<rc_t>(cc) | ((cur_rc & ~rc_mask) + rc_incr), std::memory_order_release)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ipc::yield(k);
|
ipc::yield(k);
|
||||||
}
|
}
|
||||||
// only one thread/process would touch here at one time
|
// only one thread/process would touch here at one time
|
||||||
ct_.store(cur_ct + 1, std::memory_order_release);
|
ct_.store(cur_ct + 1, std::memory_order_release);
|
||||||
std::forward<F>(f)(&(el->data_));
|
std::forward<F>(f)(&(el->data_));
|
||||||
// set flag & try update wt
|
// set flag & try update wt
|
||||||
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
|
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename W, typename F, typename E, std::size_t N>
|
template <typename W, typename F, typename E, std::size_t N>
|
||||||
bool pop(W* /*wrapper*/, circ::u2_t& cur, F&& f, E(& elems)[N]) {
|
bool pop(W* /*wrapper*/, circ::u2_t& cur, F&& f, E(& elems)[N]) {
|
||||||
auto* el = elems + circ::index_of(cur);
|
auto* el = elems + circ::index_of(cur);
|
||||||
auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
|
auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
|
||||||
if (cur_fl != ~static_cast<flag_t>(cur)) {
|
if (cur_fl != ~static_cast<flag_t>(cur)) {
|
||||||
return false; // empty
|
return false; // empty
|
||||||
}
|
}
|
||||||
++cur;
|
++cur;
|
||||||
std::forward<F>(f)(&(el->data_));
|
std::forward<F>(f)(&(el->data_));
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||||
switch (cur_rc & rc_mask) {
|
switch (cur_rc & rc_mask) {
|
||||||
case 0:
|
case 0:
|
||||||
el->f_ct_.store(cur + N - 1, std::memory_order_release);
|
el->f_ct_.store(cur + N - 1, std::memory_order_release);
|
||||||
return true;
|
return true;
|
||||||
case 1:
|
case 1:
|
||||||
el->f_ct_.store(cur + N - 1, std::memory_order_release);
|
el->f_ct_.store(cur + N - 1, std::memory_order_release);
|
||||||
IPC_FALLTHROUGH_;
|
IPC_FALLTHROUGH_;
|
||||||
default:
|
default:
|
||||||
if (el->rc_.compare_exchange_weak(
|
if (el->rc_.compare_exchange_weak(
|
||||||
cur_rc, cur_rc + rc_incr - 1, std::memory_order_release)) {
|
cur_rc, cur_rc + rc_incr - 1, std::memory_order_release)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ipc::yield(k);
|
ipc::yield(k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
394
src/queue.h
394
src/queue.h
@ -1,197 +1,197 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "rw_lock.h"
|
#include "rw_lock.h"
|
||||||
|
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class queue_conn {
|
class queue_conn {
|
||||||
protected:
|
protected:
|
||||||
bool connected_ = false;
|
bool connected_ = false;
|
||||||
shm::handle elems_h_;
|
shm::handle elems_h_;
|
||||||
|
|
||||||
template <typename Elems>
|
template <typename Elems>
|
||||||
Elems* open(char const * name) {
|
Elems* open(char const * name) {
|
||||||
if (name == nullptr || name[0] == '\0') {
|
if (name == nullptr || name[0] == '\0') {
|
||||||
ipc::error("fail open waiter: name is empty!\n");
|
ipc::error("fail open waiter: name is empty!\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (!elems_h_.acquire(name, sizeof(Elems))) {
|
if (!elems_h_.acquire(name, sizeof(Elems))) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto elems = static_cast<Elems*>(elems_h_.get());
|
auto elems = static_cast<Elems*>(elems_h_.get());
|
||||||
if (elems == nullptr) {
|
if (elems == nullptr) {
|
||||||
ipc::error("fail acquire elems: %s\n", name);
|
ipc::error("fail acquire elems: %s\n", name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
elems->init();
|
elems->init();
|
||||||
return elems;
|
return elems;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
elems_h_.release();
|
elems_h_.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
queue_conn() = default;
|
queue_conn() = default;
|
||||||
queue_conn(const queue_conn&) = delete;
|
queue_conn(const queue_conn&) = delete;
|
||||||
queue_conn& operator=(const queue_conn&) = delete;
|
queue_conn& operator=(const queue_conn&) = delete;
|
||||||
|
|
||||||
bool connected() const noexcept {
|
bool connected() const noexcept {
|
||||||
return connected_;
|
return connected_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Elems>
|
template <typename Elems>
|
||||||
auto connect(Elems* elems)
|
auto connect(Elems* elems)
|
||||||
-> std::tuple<bool, decltype(std::declval<Elems>().cursor())> {
|
-> std::tuple<bool, decltype(std::declval<Elems>().cursor())> {
|
||||||
if (elems == nullptr) return {};
|
if (elems == nullptr) return {};
|
||||||
if (connected_) {
|
if (connected_) {
|
||||||
// if it's already connected, just return false
|
// if it's already connected, just return false
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
connected_ = true;
|
connected_ = true;
|
||||||
elems->connect();
|
elems->connect();
|
||||||
return std::make_tuple(true, elems->cursor());
|
return std::make_tuple(true, elems->cursor());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Elems>
|
template <typename Elems>
|
||||||
bool disconnect(Elems* elems) {
|
bool disconnect(Elems* elems) {
|
||||||
if (elems == nullptr) return false;
|
if (elems == nullptr) return false;
|
||||||
if (!connected_) {
|
if (!connected_) {
|
||||||
// if it's already disconnected, just return false
|
// if it's already disconnected, just return false
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
connected_ = false;
|
connected_ = false;
|
||||||
elems->disconnect();
|
elems->disconnect();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Elems>
|
template <typename Elems>
|
||||||
class queue_base : public queue_conn {
|
class queue_base : public queue_conn {
|
||||||
using base_t = queue_conn;
|
using base_t = queue_conn;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using elems_t = Elems;
|
using elems_t = Elems;
|
||||||
using policy_t = typename elems_t::policy_t;
|
using policy_t = typename elems_t::policy_t;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
elems_t * elems_ = nullptr;
|
elems_t * elems_ = nullptr;
|
||||||
decltype(std::declval<elems_t>().cursor()) cursor_ = 0;
|
decltype(std::declval<elems_t>().cursor()) cursor_ = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using base_t::base_t;
|
using base_t::base_t;
|
||||||
|
|
||||||
queue_base() = default;
|
queue_base() = default;
|
||||||
|
|
||||||
explicit queue_base(char const * name)
|
explicit queue_base(char const * name)
|
||||||
: queue_base() {
|
: queue_base() {
|
||||||
elems_ = open<elems_t>(name);
|
elems_ = open<elems_t>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* not virtual */ ~queue_base() {
|
/* not virtual */ ~queue_base() {
|
||||||
base_t::close();
|
base_t::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr elems_t * elems() const noexcept {
|
constexpr elems_t * elems() const noexcept {
|
||||||
return elems_;
|
return elems_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool connect() {
|
bool connect() {
|
||||||
auto tp = base_t::connect(elems_);
|
auto tp = base_t::connect(elems_);
|
||||||
if (std::get<0>(tp)) {
|
if (std::get<0>(tp)) {
|
||||||
cursor_ = std::get<1>(tp);
|
cursor_ = std::get<1>(tp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool disconnect() {
|
bool disconnect() {
|
||||||
return base_t::disconnect(elems_);
|
return base_t::disconnect(elems_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dis_flag() {
|
bool dis_flag() {
|
||||||
return elems_->dis_flag();
|
return elems_->dis_flag();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t conn_count() const noexcept {
|
std::size_t conn_count() const noexcept {
|
||||||
return (elems_ == nullptr) ? invalid_value : elems_->conn_count();
|
return (elems_ == nullptr) ? invalid_value : elems_->conn_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool valid() const noexcept {
|
bool valid() const noexcept {
|
||||||
return elems_ != nullptr;
|
return elems_ != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const noexcept {
|
bool empty() const noexcept {
|
||||||
return (elems_ == nullptr) ? true : (cursor_ == elems_->cursor());
|
return (elems_ == nullptr) ? true : (cursor_ == elems_->cursor());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... P>
|
template <typename T, typename... P>
|
||||||
auto push(P&&... params) {
|
auto push(P&&... params) {
|
||||||
if (elems_ == nullptr) return false;
|
if (elems_ == nullptr) return false;
|
||||||
return elems_->push([&](void* p) {
|
return elems_->push([&](void* p) {
|
||||||
::new (p) T(std::forward<P>(params)...);
|
::new (p) T(std::forward<P>(params)...);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... P>
|
template <typename T, typename... P>
|
||||||
auto force_push(P&&... params) {
|
auto force_push(P&&... params) {
|
||||||
if (elems_ == nullptr) return false;
|
if (elems_ == nullptr) return false;
|
||||||
return elems_->force_push([&](void* p) {
|
return elems_->force_push([&](void* p) {
|
||||||
::new (p) T(std::forward<P>(params)...);
|
::new (p) T(std::forward<P>(params)...);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool pop(T& item) {
|
bool pop(T& item) {
|
||||||
if (elems_ == nullptr) {
|
if (elems_ == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return elems_->pop(&(this->cursor_), [&item](void* p) {
|
return elems_->pop(&(this->cursor_), [&item](void* p) {
|
||||||
::new (&item) T(std::move(*static_cast<T*>(p)));
|
::new (&item) T(std::move(*static_cast<T*>(p)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename T, typename Policy>
|
template <typename T, typename Policy>
|
||||||
class queue : public detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>> {
|
class queue : public detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>> {
|
||||||
using base_t = detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>>;
|
using base_t = detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using value_t = T;
|
using value_t = T;
|
||||||
|
|
||||||
using base_t::base_t;
|
using base_t::base_t;
|
||||||
|
|
||||||
template <typename... P>
|
template <typename... P>
|
||||||
auto push(P&&... params) {
|
auto push(P&&... params) {
|
||||||
return base_t::template push<T>(std::forward<P>(params)...);
|
return base_t::template push<T>(std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... P>
|
template <typename... P>
|
||||||
auto force_push(P&&... params) {
|
auto force_push(P&&... params) {
|
||||||
return base_t::template force_push<T>(std::forward<P>(params)...);
|
return base_t::template force_push<T>(std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pop(T& item) {
|
bool pop(T& item) {
|
||||||
return base_t::pop(item);
|
return base_t::pop(item);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
188
src/shm.cpp
188
src/shm.cpp
@ -1,94 +1,94 @@
|
|||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "pimpl.h"
|
#include "pimpl.h"
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace shm {
|
namespace shm {
|
||||||
|
|
||||||
class handle::handle_ : public pimpl<handle_> {
|
class handle::handle_ : public pimpl<handle_> {
|
||||||
public:
|
public:
|
||||||
shm::id_t id_ = nullptr;
|
shm::id_t id_ = nullptr;
|
||||||
void* m_ = nullptr;
|
void* m_ = nullptr;
|
||||||
|
|
||||||
ipc::string n_;
|
ipc::string n_;
|
||||||
std::size_t s_ = 0;
|
std::size_t s_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
handle::handle()
|
handle::handle()
|
||||||
: p_(p_->make()) {
|
: p_(p_->make()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
handle::handle(char const * name, std::size_t size, unsigned mode)
|
handle::handle(char const * name, std::size_t size, unsigned mode)
|
||||||
: handle() {
|
: handle() {
|
||||||
acquire(name, size, mode);
|
acquire(name, size, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle::handle(handle&& rhs)
|
handle::handle(handle&& rhs)
|
||||||
: handle() {
|
: handle() {
|
||||||
swap(rhs);
|
swap(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle::~handle() {
|
handle::~handle() {
|
||||||
release();
|
release();
|
||||||
p_->clear();
|
p_->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle::swap(handle& rhs) {
|
void handle::swap(handle& rhs) {
|
||||||
std::swap(p_, rhs.p_);
|
std::swap(p_, rhs.p_);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle& handle::operator=(handle rhs) {
|
handle& handle::operator=(handle rhs) {
|
||||||
swap(rhs);
|
swap(rhs);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handle::valid() const {
|
bool handle::valid() const {
|
||||||
return impl(p_)->m_ != nullptr;
|
return impl(p_)->m_ != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t handle::size() const {
|
std::size_t handle::size() const {
|
||||||
return impl(p_)->s_;
|
return impl(p_)->s_;
|
||||||
}
|
}
|
||||||
|
|
||||||
char const * handle::name() const {
|
char const * handle::name() const {
|
||||||
return impl(p_)->n_.c_str();
|
return impl(p_)->n_.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handle::acquire(char const * name, std::size_t size, unsigned mode) {
|
bool handle::acquire(char const * name, std::size_t size, unsigned mode) {
|
||||||
release();
|
release();
|
||||||
impl(p_)->id_ = shm::acquire((impl(p_)->n_ = name).c_str(), size, mode);
|
impl(p_)->id_ = shm::acquire((impl(p_)->n_ = name).c_str(), size, mode);
|
||||||
impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_));
|
impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_));
|
||||||
return valid();
|
return valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle::release() {
|
void handle::release() {
|
||||||
if (impl(p_)->id_ == nullptr) return;
|
if (impl(p_)->id_ == nullptr) return;
|
||||||
shm::release(detach());
|
shm::release(detach());
|
||||||
}
|
}
|
||||||
|
|
||||||
void* handle::get() const {
|
void* handle::get() const {
|
||||||
return impl(p_)->m_;
|
return impl(p_)->m_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle::attach(id_t id) {
|
void handle::attach(id_t id) {
|
||||||
if (id == nullptr) return;
|
if (id == nullptr) return;
|
||||||
release();
|
release();
|
||||||
impl(p_)->id_ = id;
|
impl(p_)->id_ = id;
|
||||||
impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_));
|
impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_));
|
||||||
}
|
}
|
||||||
|
|
||||||
id_t handle::detach() {
|
id_t handle::detach() {
|
||||||
auto old = impl(p_)->id_;
|
auto old = impl(p_)->id_;
|
||||||
impl(p_)->id_ = nullptr;
|
impl(p_)->id_ = nullptr;
|
||||||
impl(p_)->m_ = nullptr;
|
impl(p_)->m_ = nullptr;
|
||||||
impl(p_)->s_ = 0;
|
impl(p_)->s_ = 0;
|
||||||
impl(p_)->n_.clear();
|
impl(p_)->n_.clear();
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace shm
|
} // namespace shm
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
152
src/waiter.cpp
152
src/waiter.cpp
@ -1,76 +1,76 @@
|
|||||||
#include "waiter.h"
|
#include "waiter.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "pimpl.h"
|
#include "pimpl.h"
|
||||||
#include "platform/waiter_wrapper.h"
|
#include "platform/waiter_wrapper.h"
|
||||||
|
|
||||||
#undef IPC_PP_CAT_
|
#undef IPC_PP_CAT_
|
||||||
#undef IPC_PP_JOIN_T__
|
#undef IPC_PP_JOIN_T__
|
||||||
#undef IPC_PP_JOIN_
|
#undef IPC_PP_JOIN_
|
||||||
|
|
||||||
#define IPC_PP_CAT_(X, ...) X##__VA_ARGS__
|
#define IPC_PP_CAT_(X, ...) X##__VA_ARGS__
|
||||||
#define IPC_PP_JOIN_T__(X, ...) IPC_PP_CAT_(X, __VA_ARGS__)
|
#define IPC_PP_JOIN_T__(X, ...) IPC_PP_CAT_(X, __VA_ARGS__)
|
||||||
#define IPC_PP_JOIN_(X, ...) IPC_PP_JOIN_T__(X, __VA_ARGS__)
|
#define IPC_PP_JOIN_(X, ...) IPC_PP_JOIN_T__(X, __VA_ARGS__)
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
#undef IPC_OBJECT_TYPE_
|
#undef IPC_OBJECT_TYPE_
|
||||||
#undef IPC_OBJECT_TYPE_OPEN_PARS_
|
#undef IPC_OBJECT_TYPE_OPEN_PARS_
|
||||||
#undef IPC_OBJECT_TYPE_OPEN_ARGS_
|
#undef IPC_OBJECT_TYPE_OPEN_ARGS_
|
||||||
|
|
||||||
#define IPC_OBJECT_TYPE_ mutex
|
#define IPC_OBJECT_TYPE_ mutex
|
||||||
#define IPC_OBJECT_TYPE_OPEN_PARS_
|
#define IPC_OBJECT_TYPE_OPEN_PARS_
|
||||||
#define IPC_OBJECT_TYPE_OPEN_ARGS_
|
#define IPC_OBJECT_TYPE_OPEN_ARGS_
|
||||||
|
|
||||||
#include "waiter_template.inc"
|
#include "waiter_template.inc"
|
||||||
|
|
||||||
bool mutex::lock() {
|
bool mutex::lock() {
|
||||||
return impl(p_)->h_.lock();
|
return impl(p_)->h_.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mutex::unlock() {
|
bool mutex::unlock() {
|
||||||
return impl(p_)->h_.unlock();
|
return impl(p_)->h_.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef IPC_OBJECT_TYPE_
|
#undef IPC_OBJECT_TYPE_
|
||||||
#undef IPC_OBJECT_TYPE_OPEN_PARS_
|
#undef IPC_OBJECT_TYPE_OPEN_PARS_
|
||||||
#undef IPC_OBJECT_TYPE_OPEN_ARGS_
|
#undef IPC_OBJECT_TYPE_OPEN_ARGS_
|
||||||
|
|
||||||
#define IPC_OBJECT_TYPE_ semaphore
|
#define IPC_OBJECT_TYPE_ semaphore
|
||||||
#define IPC_OBJECT_TYPE_OPEN_PARS_ , long count
|
#define IPC_OBJECT_TYPE_OPEN_PARS_ , long count
|
||||||
#define IPC_OBJECT_TYPE_OPEN_ARGS_ , count
|
#define IPC_OBJECT_TYPE_OPEN_ARGS_ , count
|
||||||
|
|
||||||
#include "waiter_template.inc"
|
#include "waiter_template.inc"
|
||||||
|
|
||||||
bool semaphore::wait(std::size_t tm) {
|
bool semaphore::wait(std::size_t tm) {
|
||||||
return impl(p_)->h_.wait(tm);
|
return impl(p_)->h_.wait(tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool semaphore::post(long count) {
|
bool semaphore::post(long count) {
|
||||||
return impl(p_)->h_.post(count);
|
return impl(p_)->h_.post(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef IPC_OBJECT_TYPE_
|
#undef IPC_OBJECT_TYPE_
|
||||||
#undef IPC_OBJECT_TYPE_OPEN_PARS_
|
#undef IPC_OBJECT_TYPE_OPEN_PARS_
|
||||||
#undef IPC_OBJECT_TYPE_OPEN_ARGS_
|
#undef IPC_OBJECT_TYPE_OPEN_ARGS_
|
||||||
|
|
||||||
#define IPC_OBJECT_TYPE_ condition
|
#define IPC_OBJECT_TYPE_ condition
|
||||||
#define IPC_OBJECT_TYPE_OPEN_PARS_
|
#define IPC_OBJECT_TYPE_OPEN_PARS_
|
||||||
#define IPC_OBJECT_TYPE_OPEN_ARGS_
|
#define IPC_OBJECT_TYPE_OPEN_ARGS_
|
||||||
|
|
||||||
#include "waiter_template.inc"
|
#include "waiter_template.inc"
|
||||||
|
|
||||||
bool condition::wait(mutex& mtx, std::size_t tm) {
|
bool condition::wait(mutex& mtx, std::size_t tm) {
|
||||||
return impl(p_)->h_.wait(impl(mtx.p_)->h_, tm);
|
return impl(p_)->h_.wait(impl(mtx.p_)->h_, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool condition::notify() {
|
bool condition::notify() {
|
||||||
return impl(p_)->h_.notify();
|
return impl(p_)->h_.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool condition::broadcast() {
|
bool condition::broadcast() {
|
||||||
return impl(p_)->h_.broadcast();
|
return impl(p_)->h_.broadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|||||||
@ -1,43 +1,43 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
QVector<QObject*>* suites__ = nullptr;
|
QVector<QObject*>* suites__ = nullptr;
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|
||||||
TestSuite::TestSuite() {
|
TestSuite::TestSuite() {
|
||||||
static struct __ {
|
static struct __ {
|
||||||
QVector<QObject*> suites_;
|
QVector<QObject*> suites_;
|
||||||
__() { suites__ = &suites_; }
|
__() { suites__ = &suites_; }
|
||||||
} _;
|
} _;
|
||||||
_.suites_ << this;
|
_.suites_ << this;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* TestSuite::name() const {
|
const char* TestSuite::name() const {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSuite::initTestCase() {
|
void TestSuite::initTestCase() {
|
||||||
qDebug() << QString("#### Start: %1 ####").arg(name());
|
qDebug() << QString("#### Start: %1 ####").arg(name());
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
Q_UNUSED(app)
|
Q_UNUSED(app)
|
||||||
|
|
||||||
// QThread::sleep(5);
|
// QThread::sleep(5);
|
||||||
|
|
||||||
int failed_count = 0;
|
int failed_count = 0;
|
||||||
for (const auto& suite : (*suites__)) {
|
for (const auto& suite : (*suites__)) {
|
||||||
if (QTest::qExec(suite, argc, argv) != 0)
|
if (QTest::qExec(suite, argc, argv) != 0)
|
||||||
++failed_count;
|
++failed_count;
|
||||||
}
|
}
|
||||||
return failed_count;
|
return failed_count;
|
||||||
}
|
}
|
||||||
|
|||||||
310
test/test.h
310
test/test.h
@ -1,155 +1,155 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
# include <cxxabi.h> // abi::__cxa_demangle
|
# include <cxxabi.h> // abi::__cxa_demangle
|
||||||
#endif/*__GNUC__*/
|
#endif/*__GNUC__*/
|
||||||
|
|
||||||
#include "stopwatch.hpp"
|
#include "stopwatch.hpp"
|
||||||
#include "spin_lock.hpp"
|
#include "spin_lock.hpp"
|
||||||
|
|
||||||
class TestSuite : public QObject
|
class TestSuite : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TestSuite();
|
explicit TestSuite();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual const char* name() const;
|
virtual const char* name() const;
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
virtual void initTestCase();
|
virtual void initTestCase();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct test_stopwatch {
|
struct test_stopwatch {
|
||||||
capo::stopwatch<> sw_;
|
capo::stopwatch<> sw_;
|
||||||
std::atomic_flag started_ = ATOMIC_FLAG_INIT;
|
std::atomic_flag started_ = ATOMIC_FLAG_INIT;
|
||||||
|
|
||||||
void start() {
|
void start() {
|
||||||
if (!started_.test_and_set()) {
|
if (!started_.test_and_set()) {
|
||||||
sw_.start();
|
sw_.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int Factor>
|
template <int Factor>
|
||||||
void print_elapsed(int N, int M, int Loops) {
|
void print_elapsed(int N, int M, int Loops) {
|
||||||
auto ts = sw_.elapsed<std::chrono::microseconds>();
|
auto ts = sw_.elapsed<std::chrono::microseconds>();
|
||||||
std::cout << "[" << N << ":" << M << ", " << Loops << "] "
|
std::cout << "[" << N << ":" << M << ", " << Loops << "] "
|
||||||
<< "performance: " << (ts / 1000.0) << " ms, "
|
<< "performance: " << (ts / 1000.0) << " ms, "
|
||||||
<< (double(ts) / double(Factor ? (Loops * Factor) : (Loops * N))) << " us/d" << std::endl;
|
<< (double(ts) / double(Factor ? (Loops * Factor) : (Loops * N))) << " us/d" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_elapsed(int N, int M, int Loops) {
|
void print_elapsed(int N, int M, int Loops) {
|
||||||
print_elapsed<0>(N, M, Loops);
|
print_elapsed<0>(N, M, Loops);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
struct test_verify;
|
struct test_verify;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct test_verify<void> {
|
struct test_verify<void> {
|
||||||
test_verify (int) {}
|
test_verify (int) {}
|
||||||
void prepare (void*) {}
|
void prepare (void*) {}
|
||||||
void verify (int, int) {}
|
void verify (int, int) {}
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
void push_data(int, U&&) {}
|
void push_data(int, U&&) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct test_cq;
|
struct test_cq;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::string type_name() {
|
std::string type_name() {
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
const char* typeid_name = typeid(T).name();
|
const char* typeid_name = typeid(T).name();
|
||||||
const char* real_name = abi::__cxa_demangle(typeid_name, nullptr, nullptr, nullptr);
|
const char* real_name = abi::__cxa_demangle(typeid_name, nullptr, nullptr, nullptr);
|
||||||
std::unique_ptr<void, decltype(::free)*> guard { (void*)real_name, ::free };
|
std::unique_ptr<void, decltype(::free)*> guard { (void*)real_name, ::free };
|
||||||
if (real_name == nullptr) real_name = typeid_name;
|
if (real_name == nullptr) real_name = typeid_name;
|
||||||
return real_name;
|
return real_name;
|
||||||
#else
|
#else
|
||||||
return typeid(T).name();
|
return typeid(T).name();
|
||||||
#endif/*__GNUC__*/
|
#endif/*__GNUC__*/
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int N, int M, int Loops, typename V = void, typename T>
|
template <int N, int M, int Loops, typename V = void, typename T>
|
||||||
void benchmark_prod_cons(T* cq) {
|
void benchmark_prod_cons(T* cq) {
|
||||||
std::cout << "benchmark_prod_cons " << type_name<T>() << " [" << N << ":" << M << ", " << Loops << "]" << std::endl;
|
std::cout << "benchmark_prod_cons " << type_name<T>() << " [" << N << ":" << M << ", " << Loops << "]" << std::endl;
|
||||||
test_cq<T> tcq { cq };
|
test_cq<T> tcq { cq };
|
||||||
|
|
||||||
std::thread producers[N];
|
std::thread producers[N];
|
||||||
std::thread consumers[M];
|
std::thread consumers[M];
|
||||||
std::atomic_int fini_p { 0 }, fini_c { 0 };
|
std::atomic_int fini_p { 0 }, fini_c { 0 };
|
||||||
|
|
||||||
test_stopwatch sw;
|
test_stopwatch sw;
|
||||||
test_verify<V> vf { M };
|
test_verify<V> vf { M };
|
||||||
|
|
||||||
// capo::spin_lock lc;
|
// capo::spin_lock lc;
|
||||||
|
|
||||||
int cid = 0;
|
int cid = 0;
|
||||||
for (auto& t : consumers) {
|
for (auto& t : consumers) {
|
||||||
t = std::thread{[&, cid] {
|
t = std::thread{[&, cid] {
|
||||||
vf.prepare(&t);
|
vf.prepare(&t);
|
||||||
auto cn = tcq.connect();
|
auto cn = tcq.connect();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
tcq.recv(cn, [&](auto&& msg) {
|
tcq.recv(cn, [&](auto&& msg) {
|
||||||
// if (i % ((Loops * N) / 10) == 0) {
|
// if (i % ((Loops * N) / 10) == 0) {
|
||||||
// std::unique_lock<capo::spin_lock> guard { lc };
|
// std::unique_lock<capo::spin_lock> guard { lc };
|
||||||
// std::cout << cid << "-recving: " << (i * 100) / (Loops * N) << "%" << std::endl;
|
// std::cout << cid << "-recving: " << (i * 100) / (Loops * N) << "%" << std::endl;
|
||||||
// }
|
// }
|
||||||
vf.push_data(cid, msg);
|
vf.push_data(cid, msg);
|
||||||
++i;
|
++i;
|
||||||
});
|
});
|
||||||
// {
|
// {
|
||||||
// std::unique_lock<capo::spin_lock> guard { lc };
|
// std::unique_lock<capo::spin_lock> guard { lc };
|
||||||
// std::cout << cid << "-consumer-disconnect" << std::endl;
|
// std::cout << cid << "-consumer-disconnect" << std::endl;
|
||||||
// }
|
// }
|
||||||
tcq.disconnect(cn);
|
tcq.disconnect(cn);
|
||||||
if ((fini_c.fetch_add(1, std::memory_order_relaxed) + 1) != M) {
|
if ((fini_c.fetch_add(1, std::memory_order_relaxed) + 1) != M) {
|
||||||
// std::unique_lock<capo::spin_lock> guard { lc };
|
// std::unique_lock<capo::spin_lock> guard { lc };
|
||||||
// std::cout << cid << "-consumer-end" << std::endl;
|
// std::cout << cid << "-consumer-end" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sw.print_elapsed(N, M, Loops);
|
sw.print_elapsed(N, M, Loops);
|
||||||
vf.verify(N, Loops);
|
vf.verify(N, Loops);
|
||||||
}};
|
}};
|
||||||
++cid;
|
++cid;
|
||||||
}
|
}
|
||||||
|
|
||||||
tcq.wait_start(M);
|
tcq.wait_start(M);
|
||||||
|
|
||||||
std::cout << "start producers..." << std::endl;
|
std::cout << "start producers..." << std::endl;
|
||||||
int pid = 0;
|
int pid = 0;
|
||||||
for (auto& t : producers) {
|
for (auto& t : producers) {
|
||||||
t = std::thread{[&, pid] {
|
t = std::thread{[&, pid] {
|
||||||
auto cn = tcq.connect_send();
|
auto cn = tcq.connect_send();
|
||||||
sw.start();
|
sw.start();
|
||||||
for (int i = 0; i < Loops; ++i) {
|
for (int i = 0; i < Loops; ++i) {
|
||||||
// if (i % (Loops / 10) == 0) {
|
// if (i % (Loops / 10) == 0) {
|
||||||
// std::unique_lock<capo::spin_lock> guard { lc };
|
// std::unique_lock<capo::spin_lock> guard { lc };
|
||||||
// std::cout << pid << "-sending: " << (i * 100 / Loops) << "%" << std::endl;
|
// std::cout << pid << "-sending: " << (i * 100 / Loops) << "%" << std::endl;
|
||||||
// }
|
// }
|
||||||
tcq.send(cn, { pid, i });
|
tcq.send(cn, { pid, i });
|
||||||
}
|
}
|
||||||
if ((fini_p.fetch_add(1, std::memory_order_relaxed) + 1) != N) {
|
if ((fini_p.fetch_add(1, std::memory_order_relaxed) + 1) != N) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// quit
|
// quit
|
||||||
tcq.send(cn, { -1, -1 });
|
tcq.send(cn, { -1, -1 });
|
||||||
tcq.disconnect(cn);
|
tcq.disconnect(cn);
|
||||||
}};
|
}};
|
||||||
++pid;
|
++pid;
|
||||||
}
|
}
|
||||||
for (auto& t : producers) t.join();
|
for (auto& t : producers) t.join();
|
||||||
for (auto& t : consumers) t.join();
|
for (auto& t : consumers) t.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,400 +1,400 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "prod_cons.h"
|
#include "prod_cons.h"
|
||||||
#include "policy.h"
|
#include "policy.h"
|
||||||
#include "circ/elem_array.h"
|
#include "circ/elem_array.h"
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct msg_t {
|
struct msg_t {
|
||||||
int pid_;
|
int pid_;
|
||||||
int dat_;
|
int dat_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <ipc::relat Rp, ipc::relat Rc, ipc::trans Ts>
|
template <ipc::relat Rp, ipc::relat Rc, ipc::trans Ts>
|
||||||
using pc_t = ipc::prod_cons_impl<ipc::wr<Rp, Rc, Ts>>;
|
using pc_t = ipc::prod_cons_impl<ipc::wr<Rp, Rc, Ts>>;
|
||||||
|
|
||||||
template <std::size_t DataSize, typename Policy>
|
template <std::size_t DataSize, typename Policy>
|
||||||
struct ea_t : public ipc::circ::elem_array<Policy, DataSize, 1> {
|
struct ea_t : public ipc::circ::elem_array<Policy, DataSize, 1> {
|
||||||
ea_t() { std::memset(this, 0, sizeof(ipc::circ::elem_array<Policy, DataSize, 1>)); }
|
ea_t() { std::memset(this, 0, sizeof(ipc::circ::elem_array<Policy, DataSize, 1>)); }
|
||||||
};
|
};
|
||||||
|
|
||||||
using cq_t = ea_t<
|
using cq_t = ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::broadcast>
|
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::broadcast>
|
||||||
>;
|
>;
|
||||||
cq_t* cq__;
|
cq_t* cq__;
|
||||||
|
|
||||||
bool operator==(msg_t const & m1, msg_t const & m2) {
|
bool operator==(msg_t const & m1, msg_t const & m2) {
|
||||||
return (m1.pid_ == m2.pid_) && (m1.dat_ == m2.dat_);
|
return (m1.pid_ == m2.pid_) && (m1.dat_ == m2.dat_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|
||||||
template <std::size_t D, typename P>
|
template <std::size_t D, typename P>
|
||||||
struct test_verify<ea_t<D, P>> {
|
struct test_verify<ea_t<D, P>> {
|
||||||
std::vector<std::unordered_map<int, std::vector<int>>> list_;
|
std::vector<std::unordered_map<int, std::vector<int>>> list_;
|
||||||
|
|
||||||
test_verify(int M)
|
test_verify(int M)
|
||||||
: list_(static_cast<std::size_t>(M))
|
: list_(static_cast<std::size_t>(M))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void prepare(void* pt) {
|
void prepare(void* pt) {
|
||||||
std::cout << "start consumer: " << pt << std::endl;
|
std::cout << "start consumer: " << pt << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_data(int cid, msg_t const & msg) {
|
void push_data(int cid, msg_t const & msg) {
|
||||||
list_[cid][msg.pid_].push_back(msg.dat_);
|
list_[cid][msg.pid_].push_back(msg.dat_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void verify(int N, int Loops) {
|
void verify(int N, int Loops) {
|
||||||
std::cout << "verifying..." << std::endl;
|
std::cout << "verifying..." << std::endl;
|
||||||
for (auto& c_dats : list_) {
|
for (auto& c_dats : list_) {
|
||||||
for (int n = 0; n < N; ++n) {
|
for (int n = 0; n < N; ++n) {
|
||||||
auto& vec = c_dats[n];
|
auto& vec = c_dats[n];
|
||||||
//for (int d : vec) {
|
//for (int d : vec) {
|
||||||
// std::cout << d << " ";
|
// std::cout << d << " ";
|
||||||
//}
|
//}
|
||||||
//std::cout << std::endl;
|
//std::cout << std::endl;
|
||||||
QCOMPARE(vec.size(), static_cast<std::size_t>(Loops));
|
QCOMPARE(vec.size(), static_cast<std::size_t>(Loops));
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (int d : vec) {
|
for (int d : vec) {
|
||||||
QCOMPARE(i, d);
|
QCOMPARE(i, d);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <ipc::relat Rp>
|
template <ipc::relat Rp>
|
||||||
struct test_verify<pc_t<Rp, ipc::relat::multi, ipc::trans::unicast>> : test_verify<cq_t> {
|
struct test_verify<pc_t<Rp, ipc::relat::multi, ipc::trans::unicast>> : test_verify<cq_t> {
|
||||||
using test_verify<cq_t>::test_verify;
|
using test_verify<cq_t>::test_verify;
|
||||||
|
|
||||||
void verify(int N, int Loops) {
|
void verify(int N, int Loops) {
|
||||||
std::cout << "verifying..." << std::endl;
|
std::cout << "verifying..." << std::endl;
|
||||||
for (int n = 0; n < N; ++n) {
|
for (int n = 0; n < N; ++n) {
|
||||||
std::vector<int> datas;
|
std::vector<int> datas;
|
||||||
std::uint64_t sum = 0;
|
std::uint64_t sum = 0;
|
||||||
for (auto& c_dats : list_) {
|
for (auto& c_dats : list_) {
|
||||||
for (int d : c_dats[n]) {
|
for (int d : c_dats[n]) {
|
||||||
datas.push_back(d);
|
datas.push_back(d);
|
||||||
sum += d;
|
sum += d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QCOMPARE(datas.size(), static_cast<std::size_t>(Loops));
|
QCOMPARE(datas.size(), static_cast<std::size_t>(Loops));
|
||||||
QCOMPARE(sum, (Loops * std::uint64_t(Loops - 1)) / 2);
|
QCOMPARE(sum, (Loops * std::uint64_t(Loops - 1)) / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename P>
|
template <typename P>
|
||||||
struct quit_mode;
|
struct quit_mode;
|
||||||
|
|
||||||
template <ipc::relat Rp, ipc::relat Rc>
|
template <ipc::relat Rp, ipc::relat Rc>
|
||||||
struct quit_mode<pc_t<Rp, Rc, ipc::trans::unicast>> {
|
struct quit_mode<pc_t<Rp, Rc, ipc::trans::unicast>> {
|
||||||
using type = volatile bool;
|
using type = volatile bool;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <ipc::relat Rp, ipc::relat Rc>
|
template <ipc::relat Rp, ipc::relat Rc>
|
||||||
struct quit_mode<pc_t<Rp, Rc, ipc::trans::broadcast>> {
|
struct quit_mode<pc_t<Rp, Rc, ipc::trans::broadcast>> {
|
||||||
struct type {
|
struct type {
|
||||||
constexpr type(bool) {}
|
constexpr type(bool) {}
|
||||||
constexpr operator bool() const { return false; }
|
constexpr operator bool() const { return false; }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t D, typename P>
|
template <std::size_t D, typename P>
|
||||||
struct test_cq<ea_t<D, P>> {
|
struct test_cq<ea_t<D, P>> {
|
||||||
using ca_t = ea_t<D, P>;
|
using ca_t = ea_t<D, P>;
|
||||||
using cn_t = decltype(std::declval<ca_t>().cursor());
|
using cn_t = decltype(std::declval<ca_t>().cursor());
|
||||||
|
|
||||||
typename quit_mode<P>::type quit_ = false;
|
typename quit_mode<P>::type quit_ = false;
|
||||||
ca_t* ca_;
|
ca_t* ca_;
|
||||||
|
|
||||||
test_cq(ca_t* ca) : ca_(ca) {}
|
test_cq(ca_t* ca) : ca_(ca) {}
|
||||||
|
|
||||||
cn_t connect() {
|
cn_t connect() {
|
||||||
auto cur = ca_->cursor();
|
auto cur = ca_->cursor();
|
||||||
ca_->connect();
|
ca_->connect();
|
||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnect(cn_t) {
|
void disconnect(cn_t) {
|
||||||
ca_->disconnect();
|
ca_->disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnect(ca_t*) {
|
void disconnect(ca_t*) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_start(int M) {
|
void wait_start(int M) {
|
||||||
while (ca_->conn_count() != static_cast<std::size_t>(M)) {
|
while (ca_->conn_count() != static_cast<std::size_t>(M)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void recv(cn_t cur, F&& proc) {
|
void recv(cn_t cur, F&& proc) {
|
||||||
while (1) {
|
while (1) {
|
||||||
msg_t msg;
|
msg_t msg;
|
||||||
while (ca_->pop(&cur, [&msg](void* p) {
|
while (ca_->pop(&cur, [&msg](void* p) {
|
||||||
msg = *static_cast<msg_t*>(p);
|
msg = *static_cast<msg_t*>(p);
|
||||||
})) {
|
})) {
|
||||||
if (msg.pid_ < 0) {
|
if (msg.pid_ < 0) {
|
||||||
quit_ = true;
|
quit_ = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
proc(msg);
|
proc(msg);
|
||||||
}
|
}
|
||||||
if (quit_) return;
|
if (quit_) return;
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ca_t* connect_send() {
|
ca_t* connect_send() {
|
||||||
return ca_;
|
return ca_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void send(ca_t* ca, msg_t const & msg) {
|
void send(ca_t* ca, msg_t const & msg) {
|
||||||
while (!ca->push([&msg](void* p) {
|
while (!ca->push([&msg](void* p) {
|
||||||
(*static_cast<msg_t*>(p)) = msg;
|
(*static_cast<msg_t*>(p)) = msg;
|
||||||
})) {
|
})) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
struct test_cq<ipc::queue<T...>> {
|
struct test_cq<ipc::queue<T...>> {
|
||||||
using cn_t = ipc::queue<T...>;
|
using cn_t = ipc::queue<T...>;
|
||||||
|
|
||||||
test_cq(void*) {}
|
test_cq(void*) {}
|
||||||
|
|
||||||
cn_t* connect() {
|
cn_t* connect() {
|
||||||
cn_t* queue = new cn_t { "test-ipc-queue" };
|
cn_t* queue = new cn_t { "test-ipc-queue" };
|
||||||
[&] { QVERIFY(queue->connect()); } ();
|
[&] { QVERIFY(queue->connect()); } ();
|
||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnect(cn_t* queue) {
|
void disconnect(cn_t* queue) {
|
||||||
queue->disconnect();
|
queue->disconnect();
|
||||||
delete queue;
|
delete queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_start(int M) {
|
void wait_start(int M) {
|
||||||
cn_t que("test-ipc-queue");
|
cn_t que("test-ipc-queue");
|
||||||
while (que.conn_count() != static_cast<std::size_t>(M)) {
|
while (que.conn_count() != static_cast<std::size_t>(M)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void recv(cn_t* queue, F&& proc) {
|
void recv(cn_t* queue, F&& proc) {
|
||||||
while(1) {
|
while(1) {
|
||||||
typename cn_t::value_t msg;
|
typename cn_t::value_t msg;
|
||||||
while (!queue->pop(msg)) {
|
while (!queue->pop(msg)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
if (msg.pid_ < 0) return;
|
if (msg.pid_ < 0) return;
|
||||||
proc(msg);
|
proc(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cn_t* connect_send() {
|
cn_t* connect_send() {
|
||||||
return new cn_t { "test-ipc-queue" };
|
return new cn_t { "test-ipc-queue" };
|
||||||
}
|
}
|
||||||
|
|
||||||
void send(cn_t* cn, msg_t const & msg) {
|
void send(cn_t* cn, msg_t const & msg) {
|
||||||
while (!cn->push(msg)) {
|
while (!cn->push(msg)) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class Unit : public TestSuite {
|
class Unit : public TestSuite {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
const char* name() const {
|
const char* name() const {
|
||||||
return "test_circ";
|
return "test_circ";
|
||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
void cleanupTestCase();
|
void cleanupTestCase();
|
||||||
|
|
||||||
void test_inst();
|
void test_inst();
|
||||||
void test_prod_cons_1v1();
|
void test_prod_cons_1v1();
|
||||||
void test_prod_cons_1v3();
|
void test_prod_cons_1v3();
|
||||||
void test_prod_cons_performance();
|
void test_prod_cons_performance();
|
||||||
void test_queue();
|
void test_queue();
|
||||||
} /*unit__*/;
|
} unit__;
|
||||||
|
|
||||||
#include "test_circ.moc"
|
#include "test_circ.moc"
|
||||||
|
|
||||||
constexpr int LoopCount = 1000000;
|
constexpr int LoopCount = 1000000;
|
||||||
//constexpr int LoopCount = 1000/*0000*/;
|
//constexpr int LoopCount = 1000/*0000*/;
|
||||||
|
|
||||||
void Unit::initTestCase() {
|
void Unit::initTestCase() {
|
||||||
TestSuite::initTestCase();
|
TestSuite::initTestCase();
|
||||||
cq__ = new cq_t;
|
cq__ = new cq_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::cleanupTestCase() {
|
void Unit::cleanupTestCase() {
|
||||||
delete cq__;
|
delete cq__;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_inst() {
|
void Unit::test_inst() {
|
||||||
std::cout << "cq_t::head_size = " << cq_t::head_size << std::endl;
|
std::cout << "cq_t::head_size = " << cq_t::head_size << std::endl;
|
||||||
std::cout << "cq_t::data_size = " << cq_t::data_size << std::endl;
|
std::cout << "cq_t::data_size = " << cq_t::data_size << std::endl;
|
||||||
std::cout << "cq_t::elem_size = " << cq_t::elem_size << std::endl;
|
std::cout << "cq_t::elem_size = " << cq_t::elem_size << std::endl;
|
||||||
std::cout << "cq_t::block_size = " << cq_t::block_size << std::endl;
|
std::cout << "cq_t::block_size = " << cq_t::block_size << std::endl;
|
||||||
|
|
||||||
QCOMPARE(static_cast<std::size_t>(cq_t::data_size), sizeof(msg_t));
|
QCOMPARE(static_cast<std::size_t>(cq_t::data_size), sizeof(msg_t));
|
||||||
|
|
||||||
std::cout << "sizeof(ea_t<sizeof(msg_t)>) = " << sizeof(*cq__) << std::endl;
|
std::cout << "sizeof(ea_t<sizeof(msg_t)>) = " << sizeof(*cq__) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int N, int M, bool V = true, int Loops = LoopCount>
|
template <int N, int M, bool V = true, int Loops = LoopCount>
|
||||||
void test_prod_cons() {
|
void test_prod_cons() {
|
||||||
benchmark_prod_cons<N, M, Loops, std::conditional_t<V, cq_t, void>>(cq__);
|
benchmark_prod_cons<N, M, Loops, std::conditional_t<V, cq_t, void>>(cq__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_prod_cons_1v1() {
|
void Unit::test_prod_cons_1v1() {
|
||||||
// ea_t<
|
// ea_t<
|
||||||
// sizeof(msg_t),
|
// sizeof(msg_t),
|
||||||
// pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
|
// pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
|
||||||
// > el_arr_mmb;
|
// > el_arr_mmb;
|
||||||
// benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmb);
|
// benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmb);
|
||||||
// benchmark_prod_cons<2, 1, LoopCount, void>(&el_arr_mmb);
|
// benchmark_prod_cons<2, 1, LoopCount, void>(&el_arr_mmb);
|
||||||
|
|
||||||
ea_t<
|
ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::single, ipc::relat::single, ipc::trans::unicast>
|
pc_t<ipc::relat::single, ipc::relat::single, ipc::trans::unicast>
|
||||||
> el_arr_ssu;
|
> el_arr_ssu;
|
||||||
benchmark_prod_cons<1, 1, LoopCount, cq_t>(&el_arr_ssu);
|
benchmark_prod_cons<1, 1, LoopCount, cq_t>(&el_arr_ssu);
|
||||||
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_ssu);
|
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_ssu);
|
||||||
|
|
||||||
ea_t<
|
ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast>
|
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast>
|
||||||
> el_arr_smu;
|
> el_arr_smu;
|
||||||
benchmark_prod_cons<1, 1, LoopCount, decltype(el_arr_smu)::policy_t>(&el_arr_smu);
|
benchmark_prod_cons<1, 1, LoopCount, decltype(el_arr_smu)::policy_t>(&el_arr_smu);
|
||||||
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_smu);
|
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_smu);
|
||||||
|
|
||||||
ea_t<
|
ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast>
|
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast>
|
||||||
> el_arr_mmu;
|
> el_arr_mmu;
|
||||||
benchmark_prod_cons<1, 1, LoopCount, decltype(el_arr_mmu)::policy_t>(&el_arr_mmu);
|
benchmark_prod_cons<1, 1, LoopCount, decltype(el_arr_mmu)::policy_t>(&el_arr_mmu);
|
||||||
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmu);
|
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmu);
|
||||||
|
|
||||||
test_prod_cons<1, 1>();
|
test_prod_cons<1, 1>();
|
||||||
test_prod_cons<1, 1, false>();
|
test_prod_cons<1, 1, false>();
|
||||||
|
|
||||||
ea_t<
|
ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
|
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
|
||||||
> el_arr_mmb;
|
> el_arr_mmb;
|
||||||
benchmark_prod_cons<1, 1, LoopCount, cq_t>(&el_arr_mmb);
|
benchmark_prod_cons<1, 1, LoopCount, cq_t>(&el_arr_mmb);
|
||||||
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmb);
|
benchmark_prod_cons<1, 1, LoopCount, void>(&el_arr_mmb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_prod_cons_1v3() {
|
void Unit::test_prod_cons_1v3() {
|
||||||
ea_t<
|
ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast>
|
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast>
|
||||||
> el_arr_smu;
|
> el_arr_smu;
|
||||||
benchmark_prod_cons<1, 3, LoopCount, decltype(el_arr_smu)::policy_t>(&el_arr_smu);
|
benchmark_prod_cons<1, 3, LoopCount, decltype(el_arr_smu)::policy_t>(&el_arr_smu);
|
||||||
benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_smu);
|
benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_smu);
|
||||||
|
|
||||||
ea_t<
|
ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast>
|
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast>
|
||||||
> el_arr_mmu;
|
> el_arr_mmu;
|
||||||
benchmark_prod_cons<1, 3, LoopCount, decltype(el_arr_mmu)::policy_t>(&el_arr_mmu);
|
benchmark_prod_cons<1, 3, LoopCount, decltype(el_arr_mmu)::policy_t>(&el_arr_mmu);
|
||||||
benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_mmu);
|
benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_mmu);
|
||||||
|
|
||||||
test_prod_cons<1, 3>();
|
test_prod_cons<1, 3>();
|
||||||
test_prod_cons<1, 3, false>();
|
test_prod_cons<1, 3, false>();
|
||||||
|
|
||||||
ea_t<
|
ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
|
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
|
||||||
> el_arr_mmb;
|
> el_arr_mmb;
|
||||||
benchmark_prod_cons<1, 3, LoopCount, cq_t>(&el_arr_mmb);
|
benchmark_prod_cons<1, 3, LoopCount, cq_t>(&el_arr_mmb);
|
||||||
benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_mmb);
|
benchmark_prod_cons<1, 3, LoopCount, void>(&el_arr_mmb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_prod_cons_performance() {
|
void Unit::test_prod_cons_performance() {
|
||||||
ea_t<
|
ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast>
|
pc_t<ipc::relat::single, ipc::relat::multi, ipc::trans::unicast>
|
||||||
> el_arr_smu;
|
> el_arr_smu;
|
||||||
ipc::detail::static_for<8>([&el_arr_smu](auto index) {
|
ipc::detail::static_for<8>([&el_arr_smu](auto index) {
|
||||||
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_smu);
|
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_smu);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc::detail::static_for<8>([](auto index) {
|
ipc::detail::static_for<8>([](auto index) {
|
||||||
test_prod_cons<1, decltype(index)::value + 1, false>();
|
test_prod_cons<1, decltype(index)::value + 1, false>();
|
||||||
});
|
});
|
||||||
test_prod_cons<1, 8>(); // test & verify
|
test_prod_cons<1, 8>(); // test & verify
|
||||||
|
|
||||||
ea_t<
|
ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast>
|
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::unicast>
|
||||||
> el_arr_mmu;
|
> el_arr_mmu;
|
||||||
ipc::detail::static_for<8>([&el_arr_mmu](auto index) {
|
ipc::detail::static_for<8>([&el_arr_mmu](auto index) {
|
||||||
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmu);
|
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmu);
|
||||||
});
|
});
|
||||||
ipc::detail::static_for<8>([&el_arr_mmu](auto index) {
|
ipc::detail::static_for<8>([&el_arr_mmu](auto index) {
|
||||||
benchmark_prod_cons<decltype(index)::value + 1, 1, LoopCount, void>(&el_arr_mmu);
|
benchmark_prod_cons<decltype(index)::value + 1, 1, LoopCount, void>(&el_arr_mmu);
|
||||||
});
|
});
|
||||||
ipc::detail::static_for<8>([&el_arr_mmu](auto index) {
|
ipc::detail::static_for<8>([&el_arr_mmu](auto index) {
|
||||||
benchmark_prod_cons<decltype(index)::value + 1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmu);
|
benchmark_prod_cons<decltype(index)::value + 1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmu);
|
||||||
});
|
});
|
||||||
|
|
||||||
ea_t<
|
ea_t<
|
||||||
sizeof(msg_t),
|
sizeof(msg_t),
|
||||||
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
|
pc_t<ipc::relat::multi, ipc::relat::multi, ipc::trans::broadcast>
|
||||||
> el_arr_mmb;
|
> el_arr_mmb;
|
||||||
ipc::detail::static_for<8>([&el_arr_mmb](auto index) {
|
ipc::detail::static_for<8>([&el_arr_mmb](auto index) {
|
||||||
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmb);
|
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmb);
|
||||||
});
|
});
|
||||||
ipc::detail::static_for<8>([&el_arr_mmb](auto index) {
|
ipc::detail::static_for<8>([&el_arr_mmb](auto index) {
|
||||||
benchmark_prod_cons<decltype(index)::value + 1, 1, LoopCount, void>(&el_arr_mmb);
|
benchmark_prod_cons<decltype(index)::value + 1, 1, LoopCount, void>(&el_arr_mmb);
|
||||||
});
|
});
|
||||||
ipc::detail::static_for<8>([&el_arr_mmb](auto index) {
|
ipc::detail::static_for<8>([&el_arr_mmb](auto index) {
|
||||||
benchmark_prod_cons<decltype(index)::value + 1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmb);
|
benchmark_prod_cons<decltype(index)::value + 1, decltype(index)::value + 1, LoopCount, void>(&el_arr_mmb);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_queue() {
|
void Unit::test_queue() {
|
||||||
using queue_t = ipc::queue<msg_t, ipc::policy::choose<
|
using queue_t = ipc::queue<msg_t, ipc::policy::choose<
|
||||||
ipc::circ::elem_array,
|
ipc::circ::elem_array,
|
||||||
ipc::wr<ipc::relat::single, ipc::relat::multi, ipc::trans::broadcast>
|
ipc::wr<ipc::relat::single, ipc::relat::multi, ipc::trans::broadcast>
|
||||||
>>;
|
>>;
|
||||||
queue_t queue;
|
queue_t queue;
|
||||||
|
|
||||||
QVERIFY(!queue.push(msg_t { 1, 2 }));
|
QVERIFY(!queue.push(msg_t { 1, 2 }));
|
||||||
msg_t msg {};
|
msg_t msg {};
|
||||||
QVERIFY(!queue.pop(msg));
|
QVERIFY(!queue.pop(msg));
|
||||||
QCOMPARE(msg, (msg_t {}));
|
QCOMPARE(msg, (msg_t {}));
|
||||||
QVERIFY(sizeof(decltype(queue)::elems_t) <= sizeof(*cq__));
|
QVERIFY(sizeof(decltype(queue)::elems_t) <= sizeof(*cq__));
|
||||||
|
|
||||||
ipc::detail::static_for<16>([](auto index) {
|
ipc::detail::static_for<16>([](auto index) {
|
||||||
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount>((queue_t*)nullptr);
|
benchmark_prod_cons<1, decltype(index)::value + 1, LoopCount>((queue_t*)nullptr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|||||||
964
test/test_ipc.cpp
Executable file → Normal file
964
test/test_ipc.cpp
Executable file → Normal file
@ -1,482 +1,482 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "stopwatch.hpp"
|
#include "stopwatch.hpp"
|
||||||
#include "spin_lock.hpp"
|
#include "spin_lock.hpp"
|
||||||
#include "random.hpp"
|
#include "random.hpp"
|
||||||
|
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
#include "rw_lock.h"
|
#include "rw_lock.h"
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::vector<ipc::buff_t> datas__;
|
std::vector<ipc::buff_t> datas__;
|
||||||
|
|
||||||
constexpr int DataMin = 2;
|
constexpr int DataMin = 2;
|
||||||
constexpr int DataMax = 256;
|
constexpr int DataMax = 256;
|
||||||
constexpr int LoopCount = 100000;
|
constexpr int LoopCount = 100000;
|
||||||
//constexpr int LoopCount = 1000;
|
//constexpr int LoopCount = 1000;
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct test_verify {
|
struct test_verify {
|
||||||
std::vector<std::vector<ipc::buff_t>> list_;
|
std::vector<std::vector<ipc::buff_t>> list_;
|
||||||
|
|
||||||
test_verify(int M)
|
test_verify(int M)
|
||||||
: list_(static_cast<std::size_t>(M))
|
: list_(static_cast<std::size_t>(M))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void prepare(void* /*pt*/) {}
|
void prepare(void* /*pt*/) {}
|
||||||
|
|
||||||
void push_data(int cid, ipc::buff_t & msg) {
|
void push_data(int cid, ipc::buff_t & msg) {
|
||||||
list_[cid].emplace_back(std::move(msg));
|
list_[cid].emplace_back(std::move(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
void verify(int /*N*/, int /*Loops*/) {
|
void verify(int /*N*/, int /*Loops*/) {
|
||||||
std::cout << "verifying..." << std::endl;
|
std::cout << "verifying..." << std::endl;
|
||||||
for (auto& c_dats : list_) {
|
for (auto& c_dats : list_) {
|
||||||
QCOMPARE(datas__.size(), c_dats.size());
|
QCOMPARE(datas__.size(), c_dats.size());
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
for (auto& d : c_dats) {
|
for (auto& d : c_dats) {
|
||||||
QCOMPARE(datas__[i++], d);
|
QCOMPARE(datas__[i++], d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct test_cq<ipc::route> {
|
struct test_cq<ipc::route> {
|
||||||
using cn_t = ipc::route;
|
using cn_t = ipc::route;
|
||||||
|
|
||||||
std::string conn_name_;
|
std::string conn_name_;
|
||||||
|
|
||||||
test_cq(void*)
|
test_cq(void*)
|
||||||
: conn_name_("test-ipc-route") {
|
: conn_name_("test-ipc-route") {
|
||||||
}
|
}
|
||||||
|
|
||||||
cn_t connect() {
|
cn_t connect() {
|
||||||
return cn_t { conn_name_.c_str() };
|
return cn_t { conn_name_.c_str() };
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnect(cn_t& cn) {
|
void disconnect(cn_t& cn) {
|
||||||
cn.disconnect();
|
cn.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_start(int M) {
|
void wait_start(int M) {
|
||||||
cn_t::wait_for_recv(conn_name_.c_str(), static_cast<std::size_t>(M));
|
cn_t::wait_for_recv(conn_name_.c_str(), static_cast<std::size_t>(M));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void recv(cn_t& cn, F&& proc) {
|
void recv(cn_t& cn, F&& proc) {
|
||||||
do {
|
do {
|
||||||
auto msg = cn.recv();
|
auto msg = cn.recv();
|
||||||
if (msg.size() < 2) {
|
if (msg.size() < 2) {
|
||||||
QCOMPARE(msg, ipc::buff_t('\0'));
|
QCOMPARE(msg, ipc::buff_t('\0'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
proc(msg);
|
proc(msg);
|
||||||
} while(1);
|
} while(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
cn_t connect_send() {
|
cn_t connect_send() {
|
||||||
return connect();
|
return connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void send(cn_t& cn, const std::array<int, 2>& info) {
|
void send(cn_t& cn, const std::array<int, 2>& info) {
|
||||||
int n = info[1];
|
int n = info[1];
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
/*QVERIFY*/(cn.send(ipc::buff_t('\0')));
|
/*QVERIFY*/(cn.send(ipc::buff_t('\0')));
|
||||||
}
|
}
|
||||||
else /*QVERIFY*/(cn.send(datas__[static_cast<decltype(datas__)::size_type>(n)]));
|
else /*QVERIFY*/(cn.send(datas__[static_cast<decltype(datas__)::size_type>(n)]));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct test_cq<ipc::channel> {
|
struct test_cq<ipc::channel> {
|
||||||
using cn_t = ipc::channel;
|
using cn_t = ipc::channel;
|
||||||
|
|
||||||
std::string conn_name_;
|
std::string conn_name_;
|
||||||
int m_ = 0;
|
int m_ = 0;
|
||||||
|
|
||||||
test_cq(void*)
|
test_cq(void*)
|
||||||
: conn_name_("test-ipc-channel") {
|
: conn_name_("test-ipc-channel") {
|
||||||
}
|
}
|
||||||
|
|
||||||
cn_t connect() {
|
cn_t connect() {
|
||||||
return cn_t { conn_name_.c_str() };
|
return cn_t { conn_name_.c_str() };
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnect(cn_t& cn) {
|
void disconnect(cn_t& cn) {
|
||||||
cn.disconnect();
|
cn.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_start(int M) { m_ = M; }
|
void wait_start(int M) { m_ = M; }
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void recv(cn_t& cn, F&& proc) {
|
void recv(cn_t& cn, F&& proc) {
|
||||||
do {
|
do {
|
||||||
auto msg = cn.recv();
|
auto msg = cn.recv();
|
||||||
if (msg.size() < 2) {
|
if (msg.size() < 2) {
|
||||||
QCOMPARE(msg, ipc::buff_t('\0'));
|
QCOMPARE(msg, ipc::buff_t('\0'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
proc(msg);
|
proc(msg);
|
||||||
} while(1);
|
} while(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
cn_t connect_send() {
|
cn_t connect_send() {
|
||||||
return connect();
|
return connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void send(cn_t& cn, const std::array<int, 2>& info) {
|
void send(cn_t& cn, const std::array<int, 2>& info) {
|
||||||
thread_local struct s_dummy {
|
thread_local struct s_dummy {
|
||||||
s_dummy(cn_t& cn, int m) {
|
s_dummy(cn_t& cn, int m) {
|
||||||
cn.wait_for_recv(static_cast<std::size_t>(m));
|
cn.wait_for_recv(static_cast<std::size_t>(m));
|
||||||
// std::printf("start to send: %d.\n", m);
|
// std::printf("start to send: %d.\n", m);
|
||||||
}
|
}
|
||||||
} _(cn, m_);
|
} _(cn, m_);
|
||||||
int n = info[1];
|
int n = info[1];
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
/*QVERIFY*/(cn.send(ipc::buff_t('\0')));
|
/*QVERIFY*/(cn.send(ipc::buff_t('\0')));
|
||||||
}
|
}
|
||||||
else /*QVERIFY*/(cn.send(datas__[static_cast<decltype(datas__)::size_type>(n)]));
|
else /*QVERIFY*/(cn.send(datas__[static_cast<decltype(datas__)::size_type>(n)]));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class Unit : public TestSuite {
|
class Unit : public TestSuite {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
const char* name() const {
|
const char* name() const {
|
||||||
return "test_ipc";
|
return "test_ipc";
|
||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
void cleanupTestCase();
|
void cleanupTestCase();
|
||||||
|
|
||||||
void test_rw_lock();
|
void test_rw_lock();
|
||||||
void test_route();
|
void test_route();
|
||||||
void test_route_rtt();
|
void test_route_rtt();
|
||||||
void test_route_performance();
|
void test_route_performance();
|
||||||
void test_channel();
|
void test_channel();
|
||||||
void test_channel_rtt();
|
void test_channel_rtt();
|
||||||
void test_channel_performance();
|
void test_channel_performance();
|
||||||
} unit__;
|
} unit__;
|
||||||
|
|
||||||
#include "test_ipc.moc"
|
#include "test_ipc.moc"
|
||||||
|
|
||||||
void Unit::initTestCase() {
|
void Unit::initTestCase() {
|
||||||
TestSuite::initTestCase();
|
TestSuite::initTestCase();
|
||||||
|
|
||||||
capo::random<> rdm { DataMin, DataMax };
|
capo::random<> rdm { DataMin, DataMax };
|
||||||
capo::random<> bit { 0, (std::numeric_limits<ipc::byte_t>::max)() };
|
capo::random<> bit { 0, (std::numeric_limits<ipc::byte_t>::max)() };
|
||||||
|
|
||||||
for (int i = 0; i < LoopCount; ++i) {
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
std::size_t n = static_cast<std::size_t>(rdm());
|
std::size_t n = static_cast<std::size_t>(rdm());
|
||||||
ipc::buff_t buff {
|
ipc::buff_t buff {
|
||||||
new ipc::byte_t[n], n,
|
new ipc::byte_t[n], n,
|
||||||
[](void* p, std::size_t) {
|
[](void* p, std::size_t) {
|
||||||
delete [] static_cast<ipc::byte_t*>(p);
|
delete [] static_cast<ipc::byte_t*>(p);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
for (std::size_t k = 0; k < buff.size(); ++k) {
|
for (std::size_t k = 0; k < buff.size(); ++k) {
|
||||||
static_cast<ipc::byte_t*>(buff.data())[k] = static_cast<ipc::byte_t>(bit());
|
static_cast<ipc::byte_t*>(buff.data())[k] = static_cast<ipc::byte_t>(bit());
|
||||||
}
|
}
|
||||||
datas__.emplace_back(std::move(buff));
|
datas__.emplace_back(std::move(buff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::cleanupTestCase() {
|
void Unit::cleanupTestCase() {
|
||||||
datas__.clear();
|
datas__.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr T acc(T b, T e) {
|
constexpr T acc(T b, T e) {
|
||||||
return (e + b) * (e - b + 1) / 2;
|
return (e + b) * (e - b + 1) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Mutex>
|
template <typename Mutex>
|
||||||
struct lc_wrapper : Mutex {
|
struct lc_wrapper : Mutex {
|
||||||
void lock_shared () { Mutex::lock (); }
|
void lock_shared () { Mutex::lock (); }
|
||||||
void unlock_shared() { Mutex::unlock(); }
|
void unlock_shared() { Mutex::unlock(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Lc, int W, int R, int Loops = LoopCount>
|
template <typename Lc, int W, int R, int Loops = LoopCount>
|
||||||
void benchmark_lc() {
|
void benchmark_lc() {
|
||||||
std::thread w_trd[W];
|
std::thread w_trd[W];
|
||||||
std::thread r_trd[R];
|
std::thread r_trd[R];
|
||||||
std::atomic_int fini { 0 };
|
std::atomic_int fini { 0 };
|
||||||
// std::atomic_bool wf { false };
|
// std::atomic_bool wf { false };
|
||||||
|
|
||||||
std::vector<int> datas;
|
std::vector<int> datas;
|
||||||
Lc lc;
|
Lc lc;
|
||||||
|
|
||||||
test_stopwatch sw;
|
test_stopwatch sw;
|
||||||
std::cout << std::endl << type_name<Lc>() << std::endl;
|
std::cout << std::endl << type_name<Lc>() << std::endl;
|
||||||
|
|
||||||
for (auto& t : r_trd) {
|
for (auto& t : r_trd) {
|
||||||
t = std::thread([&] {
|
t = std::thread([&] {
|
||||||
std::vector<int> seq;
|
std::vector<int> seq;
|
||||||
std::size_t cnt = 0;
|
std::size_t cnt = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
int x = -1;
|
int x = -1;
|
||||||
{
|
{
|
||||||
std::shared_lock<Lc> guard { lc };
|
std::shared_lock<Lc> guard { lc };
|
||||||
// QVERIFY(!wf);
|
// QVERIFY(!wf);
|
||||||
if (cnt < datas.size()) {
|
if (cnt < datas.size()) {
|
||||||
x = datas[cnt];
|
x = datas[cnt];
|
||||||
}
|
}
|
||||||
// std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
// std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
if (x == 0) break; // quit
|
if (x == 0) break; // quit
|
||||||
if (x != -1) {
|
if (x != -1) {
|
||||||
seq.push_back(x);
|
seq.push_back(x);
|
||||||
++cnt;
|
++cnt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == R) {
|
if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == R) {
|
||||||
sw.print_elapsed(W, R, Loops);
|
sw.print_elapsed(W, R, Loops);
|
||||||
}
|
}
|
||||||
std::uint64_t sum = 0;
|
std::uint64_t sum = 0;
|
||||||
for (int i : seq) sum += static_cast<std::uint64_t>(i);
|
for (int i : seq) sum += static_cast<std::uint64_t>(i);
|
||||||
QCOMPARE(sum, acc<std::uint64_t>(1, Loops) * W);
|
QCOMPARE(sum, acc<std::uint64_t>(1, Loops) * W);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& t : w_trd) {
|
for (auto& t : w_trd) {
|
||||||
t = std::thread([&] {
|
t = std::thread([&] {
|
||||||
sw.start();
|
sw.start();
|
||||||
for (int i = 1; i <= Loops; ++i) {
|
for (int i = 1; i <= Loops; ++i) {
|
||||||
{
|
{
|
||||||
std::unique_lock<Lc> guard { lc };
|
std::unique_lock<Lc> guard { lc };
|
||||||
// wf = true;
|
// wf = true;
|
||||||
datas.push_back(i);
|
datas.push_back(i);
|
||||||
// std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
// std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
// wf = false;
|
// wf = false;
|
||||||
}
|
}
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& t : w_trd) t.join();
|
for (auto& t : w_trd) t.join();
|
||||||
lc.lock();
|
lc.lock();
|
||||||
datas.push_back(0);
|
datas.push_back(0);
|
||||||
lc.unlock();
|
lc.unlock();
|
||||||
for (auto& t : r_trd) t.join();
|
for (auto& t : r_trd) t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int W, int R>
|
template <int W, int R>
|
||||||
void test_lock_performance() {
|
void test_lock_performance() {
|
||||||
|
|
||||||
std::cout << std::endl
|
std::cout << std::endl
|
||||||
<< "test_lock_performance: [" << W << ":" << R << "]"
|
<< "test_lock_performance: [" << W << ":" << R << "]"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
benchmark_lc<ipc::rw_lock , W, R>();
|
benchmark_lc<ipc::rw_lock , W, R>();
|
||||||
benchmark_lc<lc_wrapper< ipc::spin_lock>, W, R>();
|
benchmark_lc<lc_wrapper< ipc::spin_lock>, W, R>();
|
||||||
benchmark_lc<lc_wrapper<capo::spin_lock>, W, R>();
|
benchmark_lc<lc_wrapper<capo::spin_lock>, W, R>();
|
||||||
benchmark_lc<lc_wrapper<std::mutex> , W, R>();
|
benchmark_lc<lc_wrapper<std::mutex> , W, R>();
|
||||||
benchmark_lc<std::shared_timed_mutex , W, R>();
|
benchmark_lc<std::shared_timed_mutex , W, R>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_rw_lock() {
|
void Unit::test_rw_lock() {
|
||||||
// test_lock_performance<1, 1>();
|
// test_lock_performance<1, 1>();
|
||||||
// test_lock_performance<4, 4>();
|
// test_lock_performance<4, 4>();
|
||||||
// test_lock_performance<1, 8>();
|
// test_lock_performance<1, 8>();
|
||||||
// test_lock_performance<8, 1>();
|
// test_lock_performance<8, 1>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, int N, int M, bool V = true, int Loops = LoopCount>
|
template <typename T, int N, int M, bool V = true, int Loops = LoopCount>
|
||||||
void test_prod_cons() {
|
void test_prod_cons() {
|
||||||
benchmark_prod_cons<N, M, Loops, std::conditional_t<V, T, void>>((T*)nullptr);
|
benchmark_prod_cons<N, M, Loops, std::conditional_t<V, T, void>>((T*)nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_route() {
|
void Unit::test_route() {
|
||||||
//return;
|
//return;
|
||||||
std::vector<char const *> const datas = {
|
std::vector<char const *> const datas = {
|
||||||
"hello!",
|
"hello!",
|
||||||
"foo",
|
"foo",
|
||||||
"bar",
|
"bar",
|
||||||
"ISO/IEC",
|
"ISO/IEC",
|
||||||
"14882:2011",
|
"14882:2011",
|
||||||
"ISO/IEC 14882:2017 Information technology - Programming languages - C++",
|
"ISO/IEC 14882:2017 Information technology - Programming languages - C++",
|
||||||
"ISO/IEC 14882:2020",
|
"ISO/IEC 14882:2020",
|
||||||
"Modern C++ Design: Generic Programming and Design Patterns Applied"
|
"Modern C++ Design: Generic Programming and Design Patterns Applied"
|
||||||
};
|
};
|
||||||
|
|
||||||
std::thread t1 {[&] {
|
std::thread t1 {[&] {
|
||||||
ipc::route cc { "my-ipc-route" };
|
ipc::route cc { "my-ipc-route" };
|
||||||
for (std::size_t i = 0; i < datas.size(); ++i) {
|
for (std::size_t i = 0; i < datas.size(); ++i) {
|
||||||
ipc::buff_t dd = cc.recv();
|
ipc::buff_t dd = cc.recv();
|
||||||
std::cout << "recv: " << (char*)dd.data() << std::endl;
|
std::cout << "recv: " << (char*)dd.data() << std::endl;
|
||||||
QCOMPARE(dd.size(), std::strlen(datas[i]) + 1);
|
QCOMPARE(dd.size(), std::strlen(datas[i]) + 1);
|
||||||
QVERIFY(std::memcmp(dd.data(), datas[i], dd.size()) == 0);
|
QVERIFY(std::memcmp(dd.data(), datas[i], dd.size()) == 0);
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
std::thread t2 {[&] {
|
std::thread t2 {[&] {
|
||||||
ipc::route cc { "my-ipc-route" };
|
ipc::route cc { "my-ipc-route" };
|
||||||
while (cc.recv_count() == 0) {
|
while (cc.recv_count() == 0) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
for (std::size_t i = 0; i < datas.size(); ++i) {
|
for (std::size_t i = 0; i < datas.size(); ++i) {
|
||||||
std::cout << "sending: " << datas[i] << std::endl;
|
std::cout << "sending: " << datas[i] << std::endl;
|
||||||
QVERIFY(cc.send(datas[i]));
|
QVERIFY(cc.send(datas[i]));
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
|
|
||||||
test_prod_cons<ipc::route, 1, 1>(); // test & verify
|
test_prod_cons<ipc::route, 1, 1>(); // test & verify
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_route_rtt() {
|
void Unit::test_route_rtt() {
|
||||||
//return;
|
//return;
|
||||||
test_stopwatch sw;
|
test_stopwatch sw;
|
||||||
|
|
||||||
std::thread t1 {[&] {
|
std::thread t1 {[&] {
|
||||||
ipc::route cc { "my-ipc-route-1" };
|
ipc::route cc { "my-ipc-route-1" };
|
||||||
ipc::route cr { "my-ipc-route-2" };
|
ipc::route cr { "my-ipc-route-2" };
|
||||||
for (std::size_t i = 0;; ++i) {
|
for (std::size_t i = 0;; ++i) {
|
||||||
auto dd = cc.recv();
|
auto dd = cc.recv();
|
||||||
if (dd.size() < 2) return;
|
if (dd.size() < 2) return;
|
||||||
//std::cout << "recv: " << i << "-[" << dd.size() << "]" << std::endl;
|
//std::cout << "recv: " << i << "-[" << dd.size() << "]" << std::endl;
|
||||||
while (!cr.send(ipc::buff_t('a'))) {
|
while (!cr.send(ipc::buff_t('a'))) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
std::thread t2 {[&] {
|
std::thread t2 {[&] {
|
||||||
ipc::route cc { "my-ipc-route-1" };
|
ipc::route cc { "my-ipc-route-1" };
|
||||||
ipc::route cr { "my-ipc-route-2" };
|
ipc::route cr { "my-ipc-route-2" };
|
||||||
while (cc.recv_count() == 0) {
|
while (cc.recv_count() == 0) {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
sw.start();
|
sw.start();
|
||||||
for (std::size_t i = 0; i < LoopCount; ++i) {
|
for (std::size_t i = 0; i < LoopCount; ++i) {
|
||||||
cc.send(datas__[i]);
|
cc.send(datas__[i]);
|
||||||
//std::cout << "sent: " << i << "-[" << datas__[i].size() << "]" << std::endl;
|
//std::cout << "sent: " << i << "-[" << datas__[i].size() << "]" << std::endl;
|
||||||
/*auto dd = */cr.recv();
|
/*auto dd = */cr.recv();
|
||||||
// if (dd.size() != 1 || dd[0] != 'a') {
|
// if (dd.size() != 1 || dd[0] != 'a') {
|
||||||
// QVERIFY(false);
|
// QVERIFY(false);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
cc.send(ipc::buff_t('\0'));
|
cc.send(ipc::buff_t('\0'));
|
||||||
t1.join();
|
t1.join();
|
||||||
sw.print_elapsed(1, 1, LoopCount);
|
sw.print_elapsed(1, 1, LoopCount);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
t2.join();
|
t2.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_route_performance() {
|
void Unit::test_route_performance() {
|
||||||
//return;
|
//return;
|
||||||
ipc::detail::static_for<8>([](auto index) {
|
ipc::detail::static_for<8>([](auto index) {
|
||||||
test_prod_cons<ipc::route, 1, decltype(index)::value + 1, false>();
|
test_prod_cons<ipc::route, 1, decltype(index)::value + 1, false>();
|
||||||
});
|
});
|
||||||
test_prod_cons<ipc::route, 1, 8>(); // test & verify
|
test_prod_cons<ipc::route, 1, 8>(); // test & verify
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_channel() {
|
void Unit::test_channel() {
|
||||||
//return;
|
//return;
|
||||||
std::thread t1 {[&] {
|
std::thread t1 {[&] {
|
||||||
ipc::channel cc { "my-ipc-channel" };
|
ipc::channel cc { "my-ipc-channel" };
|
||||||
for (std::size_t i = 0;; ++i) {
|
for (std::size_t i = 0;; ++i) {
|
||||||
ipc::buff_t dd = cc.recv();
|
ipc::buff_t dd = cc.recv();
|
||||||
if (dd.size() < 2) return;
|
if (dd.size() < 2) return;
|
||||||
QCOMPARE(dd, datas__[i]);
|
QCOMPARE(dd, datas__[i]);
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
std::thread t2 {[&] {
|
std::thread t2 {[&] {
|
||||||
ipc::channel cc { "my-ipc-channel" };
|
ipc::channel cc { "my-ipc-channel" };
|
||||||
cc.wait_for_recv(1);
|
cc.wait_for_recv(1);
|
||||||
for (std::size_t i = 0; i < static_cast<std::size_t>((std::min)(100, LoopCount)); ++i) {
|
for (std::size_t i = 0; i < static_cast<std::size_t>((std::min)(100, LoopCount)); ++i) {
|
||||||
std::cout << "sending: " << i << "-[" << datas__[i].size() << "]" << std::endl;
|
std::cout << "sending: " << i << "-[" << datas__[i].size() << "]" << std::endl;
|
||||||
cc.send(datas__[i]);
|
cc.send(datas__[i]);
|
||||||
}
|
}
|
||||||
cc.send(ipc::buff_t('\0'));
|
cc.send(ipc::buff_t('\0'));
|
||||||
t1.join();
|
t1.join();
|
||||||
}};
|
}};
|
||||||
|
|
||||||
t2.join();
|
t2.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_channel_rtt() {
|
void Unit::test_channel_rtt() {
|
||||||
test_stopwatch sw;
|
test_stopwatch sw;
|
||||||
|
|
||||||
std::thread t1 {[&] {
|
std::thread t1 {[&] {
|
||||||
ipc::channel cc { "my-ipc-channel" };
|
ipc::channel cc { "my-ipc-channel" };
|
||||||
bool recv_2 = false;
|
bool recv_2 = false;
|
||||||
for (std::size_t i = 0;; ++i) {
|
for (std::size_t i = 0;; ++i) {
|
||||||
auto dd = cc.recv();
|
auto dd = cc.recv();
|
||||||
if (dd.size() < 2) return;
|
if (dd.size() < 2) return;
|
||||||
//if (i % 1000 == 0) {
|
//if (i % 1000 == 0) {
|
||||||
// std::cout << "recv: " << i << "-[" << dd.size() << "]" << std::endl;
|
// std::cout << "recv: " << i << "-[" << dd.size() << "]" << std::endl;
|
||||||
//}
|
//}
|
||||||
while (!recv_2) {
|
while (!recv_2) {
|
||||||
recv_2 = cc.wait_for_recv(2);
|
recv_2 = cc.wait_for_recv(2);
|
||||||
}
|
}
|
||||||
cc.send(ipc::buff_t('a'));
|
cc.send(ipc::buff_t('a'));
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
std::thread t2 {[&] {
|
std::thread t2 {[&] {
|
||||||
ipc::channel cc { "my-ipc-channel" };
|
ipc::channel cc { "my-ipc-channel" };
|
||||||
cc.wait_for_recv(1);
|
cc.wait_for_recv(1);
|
||||||
sw.start();
|
sw.start();
|
||||||
for (std::size_t i = 0; i < LoopCount; ++i) {
|
for (std::size_t i = 0; i < LoopCount; ++i) {
|
||||||
//if (i % 1000 == 0) {
|
//if (i % 1000 == 0) {
|
||||||
// std::cout << "send: " << i << "-[" << datas__[i].size() << "]" << std::endl;
|
// std::cout << "send: " << i << "-[" << datas__[i].size() << "]" << std::endl;
|
||||||
//}
|
//}
|
||||||
cc.send(datas__[i]);
|
cc.send(datas__[i]);
|
||||||
/*auto dd = */cc.recv();
|
/*auto dd = */cc.recv();
|
||||||
//if (dd.size() != 1 || dd.data<char>()[0] != 'a') {
|
//if (dd.size() != 1 || dd.data<char>()[0] != 'a') {
|
||||||
// std::cout << "recv ack fail: " << i << "-[" << dd.size() << "]" << std::endl;
|
// std::cout << "recv ack fail: " << i << "-[" << dd.size() << "]" << std::endl;
|
||||||
// QVERIFY(false);
|
// QVERIFY(false);
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
cc.send(ipc::buff_t('\0'));
|
cc.send(ipc::buff_t('\0'));
|
||||||
t1.join();
|
t1.join();
|
||||||
sw.print_elapsed(1, 1, LoopCount);
|
sw.print_elapsed(1, 1, LoopCount);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
t2.join();
|
t2.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_channel_performance() {
|
void Unit::test_channel_performance() {
|
||||||
ipc::detail::static_for<8>([](auto index) {
|
ipc::detail::static_for<8>([](auto index) {
|
||||||
test_prod_cons<ipc::channel, 1, decltype(index)::value + 1, false>();
|
test_prod_cons<ipc::channel, 1, decltype(index)::value + 1, false>();
|
||||||
});
|
});
|
||||||
ipc::detail::static_for<8>([](auto index) {
|
ipc::detail::static_for<8>([](auto index) {
|
||||||
test_prod_cons<ipc::channel, decltype(index)::value + 1, 1, false>();
|
test_prod_cons<ipc::channel, decltype(index)::value + 1, 1, false>();
|
||||||
});
|
});
|
||||||
ipc::detail::static_for<8>([](auto index) {
|
ipc::detail::static_for<8>([](auto index) {
|
||||||
test_prod_cons<ipc::channel, decltype(index)::value + 1,
|
test_prod_cons<ipc::channel, decltype(index)::value + 1,
|
||||||
decltype(index)::value + 1, false>();
|
decltype(index)::value + 1, false>();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|||||||
375
test/test_mem.cpp
Executable file → Normal file
375
test/test_mem.cpp
Executable file → Normal file
@ -1,186 +1,189 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include "random.hpp"
|
#include "random.hpp"
|
||||||
|
|
||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
#include "pool_alloc.h"
|
#include "pool_alloc.h"
|
||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class Unit : public TestSuite {
|
class Unit : public TestSuite {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
const char* name() const {
|
const char* name() const {
|
||||||
return "test_mem";
|
return "test_mem";
|
||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
|
|
||||||
void test_alloc_free();
|
void test_alloc_free();
|
||||||
void test_static();
|
void test_static();
|
||||||
void test_pool();
|
void test_pool();
|
||||||
} /*unit__*/;
|
} unit__;
|
||||||
|
|
||||||
#include "test_mem.moc"
|
#include "test_mem.moc"
|
||||||
|
|
||||||
constexpr int DataMin = sizeof(void*);
|
constexpr int DataMin = sizeof(void*);
|
||||||
constexpr int DataMax = sizeof(void*) * 16;
|
constexpr int DataMax = sizeof(void*) * 16;
|
||||||
constexpr int LoopCount = 1000000;
|
constexpr int LoopCount = 1000000;
|
||||||
|
|
||||||
std::vector<std::size_t> sizes__;
|
std::vector<std::size_t> sizes__;
|
||||||
|
|
||||||
template <typename M>
|
template <typename M>
|
||||||
struct alloc_ix_t {
|
struct alloc_ix_t {
|
||||||
static std::vector<int> ix_[2];
|
static std::vector<int> ix_[2];
|
||||||
static bool inited_;
|
static bool inited_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename M>
|
template <typename M>
|
||||||
std::vector<int> alloc_ix_t<M>::ix_[2] = { std::vector<int>(LoopCount), std::vector<int>(LoopCount) };
|
std::vector<int> alloc_ix_t<M>::ix_[2] = { std::vector<int>(LoopCount), std::vector<int>(LoopCount) };
|
||||||
template <typename M>
|
template <typename M>
|
||||||
bool alloc_ix_t<M>::inited_ = false;
|
bool alloc_ix_t<M>::inited_ = false;
|
||||||
|
|
||||||
struct alloc_random : alloc_ix_t<alloc_random> {
|
struct alloc_random : alloc_ix_t<alloc_random> {
|
||||||
alloc_random() {
|
alloc_random() {
|
||||||
if (inited_) return;
|
if (inited_) return;
|
||||||
inited_ = true;
|
inited_ = true;
|
||||||
capo::random<> rdm_index(0, LoopCount - 1);
|
capo::random<> rdm_index(0, LoopCount - 1);
|
||||||
for (int i = 0; i < LoopCount; ++i) {
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
ix_[0][static_cast<std::size_t>(i)] =
|
ix_[0][static_cast<std::size_t>(i)] =
|
||||||
ix_[1][static_cast<std::size_t>(i)] = rdm_index();
|
ix_[1][static_cast<std::size_t>(i)] = rdm_index();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct alloc_LIFO : alloc_ix_t<alloc_LIFO> {
|
struct alloc_LIFO : alloc_ix_t<alloc_LIFO> {
|
||||||
alloc_LIFO() {
|
alloc_LIFO() {
|
||||||
if (inited_) return;
|
if (inited_) return;
|
||||||
inited_ = true;
|
inited_ = true;
|
||||||
for (int i = 0, n = LoopCount - 1; i < LoopCount; ++i, --n) {
|
for (int i = 0, n = LoopCount - 1; i < LoopCount; ++i, --n) {
|
||||||
ix_[0][static_cast<std::size_t>(i)] =
|
ix_[0][static_cast<std::size_t>(i)] =
|
||||||
ix_[1][static_cast<std::size_t>(n)] = i;
|
ix_[1][static_cast<std::size_t>(n)] = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct alloc_FIFO : alloc_ix_t<alloc_FIFO> {
|
struct alloc_FIFO : alloc_ix_t<alloc_FIFO> {
|
||||||
alloc_FIFO() {
|
alloc_FIFO() {
|
||||||
if (inited_) return;
|
if (inited_) return;
|
||||||
inited_ = true;
|
inited_ = true;
|
||||||
for (int i = 0; i < LoopCount; ++i) {
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
ix_[0][static_cast<std::size_t>(i)] =
|
ix_[0][static_cast<std::size_t>(i)] =
|
||||||
ix_[1][static_cast<std::size_t>(i)] = i;
|
ix_[1][static_cast<std::size_t>(i)] = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Unit::initTestCase() {
|
void Unit::initTestCase() {
|
||||||
TestSuite::initTestCase();
|
TestSuite::initTestCase();
|
||||||
|
|
||||||
capo::random<> rdm { DataMin, DataMax };
|
capo::random<> rdm { DataMin, DataMax };
|
||||||
for (int i = 0; i < LoopCount; ++i) {
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
sizes__.emplace_back(static_cast<std::size_t>(rdm()));
|
sizes__.emplace_back(static_cast<std::size_t>(rdm()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AllocT>
|
template <typename AllocT>
|
||||||
void benchmark_alloc() {
|
void benchmark_alloc() {
|
||||||
std::cout << std::endl << type_name<AllocT>() << std::endl;
|
std::cout << std::endl << type_name<AllocT>() << std::endl;
|
||||||
|
|
||||||
test_stopwatch sw;
|
test_stopwatch sw;
|
||||||
sw.start();
|
sw.start();
|
||||||
|
|
||||||
for (std::size_t x = 0; x < 10; ++x)
|
for (std::size_t x = 0; x < 10; ++x)
|
||||||
for (std::size_t n = 0; n < LoopCount; ++n) {
|
for (std::size_t n = 0; n < LoopCount; ++n) {
|
||||||
std::size_t s = sizes__[n];
|
std::size_t s = sizes__[n];
|
||||||
AllocT::free(AllocT::alloc(s), s);
|
AllocT::free(AllocT::alloc(s), s);
|
||||||
}
|
}
|
||||||
|
|
||||||
sw.print_elapsed<1>(DataMin, DataMax, LoopCount * 10);
|
sw.print_elapsed<1>(DataMin, DataMax, LoopCount * 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_alloc_free() {
|
void Unit::test_alloc_free() {
|
||||||
benchmark_alloc<ipc::mem::static_alloc>();
|
benchmark_alloc<ipc::mem::static_alloc>();
|
||||||
benchmark_alloc<ipc::mem::async_pool_alloc>();
|
benchmark_alloc<ipc::mem::async_pool_alloc>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AllocT, typename ModeT, int ThreadsN>
|
template <typename AllocT, typename ModeT, int ThreadsN>
|
||||||
void benchmark_alloc() {
|
void benchmark_alloc() {
|
||||||
std::cout << std::endl
|
std::cout << std::endl
|
||||||
<< "[Threads: " << ThreadsN << ", Mode: " << type_name<ModeT>() << "] "
|
<< "[Threads: " << ThreadsN << ", Mode: " << type_name<ModeT>() << "] "
|
||||||
<< type_name<AllocT>() << std::endl;
|
<< type_name<AllocT>() << std::endl;
|
||||||
|
|
||||||
std::vector<void*> ptrs[ThreadsN];
|
std::vector<void*> ptrs[ThreadsN];
|
||||||
for (auto& vec : ptrs) {
|
for (auto& vec : ptrs) {
|
||||||
vec.resize(LoopCount);
|
vec.resize(LoopCount);
|
||||||
}
|
}
|
||||||
ModeT mode;
|
ModeT mode;
|
||||||
|
|
||||||
std::atomic_int fini { 0 };
|
std::atomic_int fini { 0 };
|
||||||
test_stopwatch sw;
|
test_stopwatch sw;
|
||||||
|
|
||||||
std::thread works[ThreadsN];
|
std::thread works[ThreadsN];
|
||||||
int pid = 0;
|
int pid = 0;
|
||||||
|
|
||||||
for (auto& w : works) {
|
for (auto& w : works) {
|
||||||
w = std::thread {[&, pid] {
|
w = std::thread {[&, pid] {
|
||||||
sw.start();
|
sw.start();
|
||||||
for (std::size_t x = 0; x < 2; ++x) {
|
for (std::size_t x = 0; x < 2; ++x) {
|
||||||
for(std::size_t n = 0; n < LoopCount; ++n) {
|
for(std::size_t n = 0; n < LoopCount; ++n) {
|
||||||
int m = mode.ix_[x][n];
|
int m = mode.ix_[x][n];
|
||||||
void*& p = ptrs[pid][static_cast<std::size_t>(m)];
|
void*& p = ptrs[pid][static_cast<std::size_t>(m)];
|
||||||
std::size_t s = sizes__[static_cast<std::size_t>(m)];
|
std::size_t s = sizes__[static_cast<std::size_t>(m)];
|
||||||
if (p == nullptr) {
|
if (p == nullptr) {
|
||||||
p = AllocT::alloc(s);
|
p = AllocT::alloc(s);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
AllocT::free(p, s);
|
AllocT::free(p, s);
|
||||||
p = nullptr;
|
p = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == ThreadsN) {
|
if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == ThreadsN) {
|
||||||
sw.print_elapsed<1>(DataMin, DataMax, LoopCount * ThreadsN);
|
sw.print_elapsed<1>(DataMin, DataMax, LoopCount * ThreadsN);
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
++pid;
|
++pid;
|
||||||
}
|
}
|
||||||
sw.start();
|
sw.start();
|
||||||
|
|
||||||
for (auto& w : works) w.join();
|
for (auto& w : works) w.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AllocT, typename ModeT, int ThreadsN>
|
template <typename AllocT, typename ModeT, int ThreadsN>
|
||||||
struct test_performance {
|
struct test_performance {
|
||||||
static void start() {
|
static void start() {
|
||||||
test_performance<AllocT, ModeT, ThreadsN - 1>::start();
|
test_performance<AllocT, ModeT, ThreadsN - 1>::start();
|
||||||
benchmark_alloc<AllocT, ModeT, ThreadsN>();
|
benchmark_alloc<AllocT, ModeT, ThreadsN>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename AllocT, typename ModeT>
|
template <typename AllocT, typename ModeT>
|
||||||
struct test_performance<AllocT, ModeT, 1> {
|
struct test_performance<AllocT, ModeT, 1> {
|
||||||
static void start() {
|
static void start() {
|
||||||
benchmark_alloc<AllocT, ModeT, 1>();
|
benchmark_alloc<AllocT, ModeT, 1>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Unit::test_static() {
|
void Unit::test_static() {
|
||||||
test_performance<ipc::mem::static_alloc, alloc_FIFO , 8>::start();
|
test_performance<ipc::mem::static_alloc, alloc_FIFO , 8>::start();
|
||||||
test_performance<ipc::mem::static_alloc, alloc_LIFO , 8>::start();
|
test_performance<ipc::mem::static_alloc, alloc_LIFO , 8>::start();
|
||||||
test_performance<ipc::mem::static_alloc, alloc_random, 8>::start();
|
test_performance<ipc::mem::static_alloc, alloc_random, 8>::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_pool() {
|
void Unit::test_pool() {
|
||||||
test_performance<ipc::mem::pool_alloc, alloc_FIFO , 8>::start();
|
// test_performance<ipc::mem::pool_alloc, alloc_FIFO , 8>::start();
|
||||||
test_performance<ipc::mem::pool_alloc, alloc_LIFO , 8>::start();
|
// for (;;) {
|
||||||
test_performance<ipc::mem::pool_alloc, alloc_random, 8>::start();
|
test_performance<ipc::mem::pool_alloc, alloc_FIFO , 8>::start();
|
||||||
}
|
test_performance<ipc::mem::pool_alloc, alloc_LIFO , 8>::start();
|
||||||
|
test_performance<ipc::mem::pool_alloc, alloc_random, 8>::start();
|
||||||
} // internal-linkage
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
} // internal-linkage
|
||||||
|
|||||||
@ -1,114 +1,114 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
using namespace ipc::shm;
|
using namespace ipc::shm;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class Unit : public TestSuite {
|
class Unit : public TestSuite {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
const char* name() const {
|
const char* name() const {
|
||||||
return "test_shm";
|
return "test_shm";
|
||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void cleanupTestCase();
|
void cleanupTestCase();
|
||||||
|
|
||||||
void test_acquire();
|
void test_acquire();
|
||||||
void test_release();
|
void test_release();
|
||||||
void test_get();
|
void test_get();
|
||||||
void test_hello();
|
void test_hello();
|
||||||
void test_mt();
|
void test_mt();
|
||||||
} unit__;
|
} unit__;
|
||||||
|
|
||||||
#include "test_shm.moc"
|
#include "test_shm.moc"
|
||||||
|
|
||||||
handle shm_hd__;
|
handle shm_hd__;
|
||||||
|
|
||||||
void Unit::cleanupTestCase() {
|
void Unit::cleanupTestCase() {
|
||||||
shm_hd__.release();
|
shm_hd__.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_acquire() {
|
void Unit::test_acquire() {
|
||||||
QVERIFY(!shm_hd__.valid());
|
QVERIFY(!shm_hd__.valid());
|
||||||
|
|
||||||
QVERIFY(shm_hd__.acquire("my-test-1", 1024));
|
QVERIFY(shm_hd__.acquire("my-test-1", 1024));
|
||||||
QVERIFY(shm_hd__.valid());
|
QVERIFY(shm_hd__.valid());
|
||||||
QCOMPARE(shm_hd__.name(), "my-test-1");
|
QCOMPARE(shm_hd__.name(), "my-test-1");
|
||||||
|
|
||||||
QVERIFY(shm_hd__.acquire("my-test-2", 2048));
|
QVERIFY(shm_hd__.acquire("my-test-2", 2048));
|
||||||
QVERIFY(shm_hd__.valid());
|
QVERIFY(shm_hd__.valid());
|
||||||
QCOMPARE(shm_hd__.name(), "my-test-2");
|
QCOMPARE(shm_hd__.name(), "my-test-2");
|
||||||
|
|
||||||
QVERIFY(shm_hd__.acquire("my-test-3", 4096));
|
QVERIFY(shm_hd__.acquire("my-test-3", 4096));
|
||||||
QVERIFY(shm_hd__.valid());
|
QVERIFY(shm_hd__.valid());
|
||||||
QCOMPARE(shm_hd__.name(), "my-test-3");
|
QCOMPARE(shm_hd__.name(), "my-test-3");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_release() {
|
void Unit::test_release() {
|
||||||
QVERIFY(shm_hd__.valid());
|
QVERIFY(shm_hd__.valid());
|
||||||
shm_hd__.release();
|
shm_hd__.release();
|
||||||
QVERIFY(!shm_hd__.valid());
|
QVERIFY(!shm_hd__.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_get() {
|
void Unit::test_get() {
|
||||||
QVERIFY(shm_hd__.get() == nullptr);
|
QVERIFY(shm_hd__.get() == nullptr);
|
||||||
QVERIFY(shm_hd__.acquire("my-test", 1024));
|
QVERIFY(shm_hd__.acquire("my-test", 1024));
|
||||||
|
|
||||||
auto mem = shm_hd__.get();
|
auto mem = shm_hd__.get();
|
||||||
QVERIFY(mem != nullptr);
|
QVERIFY(mem != nullptr);
|
||||||
QVERIFY(mem == shm_hd__.get());
|
QVERIFY(mem == shm_hd__.get());
|
||||||
|
|
||||||
std::uint8_t buf[1024] = {};
|
std::uint8_t buf[1024] = {};
|
||||||
QVERIFY(memcmp(mem, buf, sizeof(buf)) == 0);
|
QVERIFY(memcmp(mem, buf, sizeof(buf)) == 0);
|
||||||
|
|
||||||
handle shm_other(shm_hd__.name(), shm_hd__.size());
|
handle shm_other(shm_hd__.name(), shm_hd__.size());
|
||||||
QVERIFY(shm_other.get() != shm_hd__.get());
|
QVERIFY(shm_other.get() != shm_hd__.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_hello() {
|
void Unit::test_hello() {
|
||||||
auto mem = shm_hd__.get();
|
auto mem = shm_hd__.get();
|
||||||
QVERIFY(mem != nullptr);
|
QVERIFY(mem != nullptr);
|
||||||
|
|
||||||
constexpr char hello[] = "hello!";
|
constexpr char hello[] = "hello!";
|
||||||
std::memcpy(mem, hello, sizeof(hello));
|
std::memcpy(mem, hello, sizeof(hello));
|
||||||
QCOMPARE((char*)shm_hd__.get(), hello);
|
QCOMPARE((char*)shm_hd__.get(), hello);
|
||||||
|
|
||||||
shm_hd__.release();
|
shm_hd__.release();
|
||||||
QVERIFY(shm_hd__.get() == nullptr);
|
QVERIFY(shm_hd__.get() == nullptr);
|
||||||
QVERIFY(shm_hd__.acquire("my-test", 1024));
|
QVERIFY(shm_hd__.acquire("my-test", 1024));
|
||||||
|
|
||||||
mem = shm_hd__.get();
|
mem = shm_hd__.get();
|
||||||
QVERIFY(mem != nullptr);
|
QVERIFY(mem != nullptr);
|
||||||
std::uint8_t buf[1024] = {};
|
std::uint8_t buf[1024] = {};
|
||||||
QVERIFY(memcmp(mem, buf, sizeof(buf)) == 0);
|
QVERIFY(memcmp(mem, buf, sizeof(buf)) == 0);
|
||||||
|
|
||||||
std::memcpy(mem, hello, sizeof(hello));
|
std::memcpy(mem, hello, sizeof(hello));
|
||||||
QCOMPARE((char*)shm_hd__.get(), hello);
|
QCOMPARE((char*)shm_hd__.get(), hello);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_mt() {
|
void Unit::test_mt() {
|
||||||
std::thread {
|
std::thread {
|
||||||
[] {
|
[] {
|
||||||
handle shm_mt(shm_hd__.name(), shm_hd__.size());
|
handle shm_mt(shm_hd__.name(), shm_hd__.size());
|
||||||
|
|
||||||
shm_hd__.release();
|
shm_hd__.release();
|
||||||
|
|
||||||
constexpr char hello[] = "hello!";
|
constexpr char hello[] = "hello!";
|
||||||
QCOMPARE((char*)shm_mt.get(), hello);
|
QCOMPARE((char*)shm_mt.get(), hello);
|
||||||
}
|
}
|
||||||
}.join();
|
}.join();
|
||||||
QVERIFY(shm_hd__.get() == nullptr);
|
QVERIFY(shm_hd__.get() == nullptr);
|
||||||
QVERIFY(!shm_hd__.valid());
|
QVERIFY(!shm_hd__.valid());
|
||||||
|
|
||||||
QVERIFY(shm_hd__.acquire("my-test", 1024));
|
QVERIFY(shm_hd__.acquire("my-test", 1024));
|
||||||
std::uint8_t buf[1024] = {};
|
std::uint8_t buf[1024] = {};
|
||||||
QVERIFY(memcmp(shm_hd__.get(), buf, sizeof(buf)) == 0);
|
QVERIFY(memcmp(shm_hd__.get(), buf, sizeof(buf)) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|||||||
@ -1,46 +1,46 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "platform/waiter_wrapper.h"
|
#include "platform/waiter_wrapper.h"
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class Unit : public TestSuite {
|
class Unit : public TestSuite {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
const char* name() const {
|
const char* name() const {
|
||||||
return "test_waiter";
|
return "test_waiter";
|
||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void test_broadcast();
|
void test_broadcast();
|
||||||
} unit__;
|
} unit__;
|
||||||
|
|
||||||
#include "test_waiter.moc"
|
#include "test_waiter.moc"
|
||||||
|
|
||||||
void Unit::test_broadcast() {
|
void Unit::test_broadcast() {
|
||||||
ipc::detail::waiter w;
|
ipc::detail::waiter w;
|
||||||
std::thread ts[10];
|
std::thread ts[10];
|
||||||
|
|
||||||
for (auto& t : ts) {
|
for (auto& t : ts) {
|
||||||
t = std::thread([&w] {
|
t = std::thread([&w] {
|
||||||
ipc::detail::waiter_wrapper wp { &w };
|
ipc::detail::waiter_wrapper wp { &w };
|
||||||
QVERIFY(wp.open("test-ipc-waiter"));
|
QVERIFY(wp.open("test-ipc-waiter"));
|
||||||
QVERIFY(wp.wait_if([] { return true; }));
|
QVERIFY(wp.wait_if([] { return true; }));
|
||||||
wp.close();
|
wp.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc::detail::waiter_wrapper wp { &w };
|
ipc::detail::waiter_wrapper wp { &w };
|
||||||
QVERIFY(wp.open("test-ipc-waiter"));
|
QVERIFY(wp.open("test-ipc-waiter"));
|
||||||
|
|
||||||
std::cout << "waiting for broadcast...\n";
|
std::cout << "waiting for broadcast...\n";
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
QVERIFY(wp.broadcast());
|
QVERIFY(wp.broadcast());
|
||||||
|
|
||||||
for (auto& t : ts) t.join();
|
for (auto& t : ts) t.join();
|
||||||
wp.close();
|
wp.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user