add ipc
This commit is contained in:
parent
bdf6b3f182
commit
3573b09efc
@ -24,13 +24,16 @@ SET(
|
||||
|
||||
INCLUDE_DIRECTORIES(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Ipc
|
||||
${Thirdparty}
|
||||
${Thirdparty}/libcef
|
||||
${Thirdparty}/libipc
|
||||
)
|
||||
|
||||
|
||||
link_directories(
|
||||
${Thirdparty}/libcef/lib/Debug
|
||||
${Thirdparty}/libipc/lib
|
||||
)
|
||||
|
||||
|
||||
|
44
human_render/Constant.h
Normal file
44
human_render/Constant.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
using int8 = char;
|
||||
using uint8 = unsigned char;
|
||||
using int16 = short;
|
||||
using uint16 = unsigned short;
|
||||
using int32 = int;
|
||||
using uint32 = unsigned int;
|
||||
using ulong32 = unsigned long;
|
||||
using int64 = long long;
|
||||
using uint64 = unsigned long long;
|
||||
|
||||
using AtomicRefCount = std::atomic<int32>;
|
||||
//
|
||||
//#ifndef MAKEWORD
|
||||
//#define MAKEWORD(a, b) ((uint16)(((uint8)(((uint64)(a)) & 0xff)) | ((uint16)((uint8)(((uint64)(b)) & 0xff))) << 8))
|
||||
//#endif // !MAKEWORD
|
||||
//
|
||||
//#ifndef MAKELONG
|
||||
//#define MAKELONG(a, b) ((long)(((uint16)(((uint64)(a)) & 0xffff)) | ((unsigned long)((uint16)(((uint64)(b)) & 0xffff))) << 16))
|
||||
//#endif // !MAKELONG
|
||||
//
|
||||
//#ifndef LOWORD
|
||||
//#define LOWORD(l) ((uint16)(((uint64)(l)) & 0xffff))
|
||||
//#endif // !LOWORD
|
||||
//
|
||||
//#ifndef HIWORD
|
||||
//#define HIWORD(l) ((uint16)((((uint64)(l)) >> 16) & 0xffff))
|
||||
//#endif // !HIWORD
|
||||
//
|
||||
//#ifndef LOBYTE
|
||||
//#define LOBYTE(w) ((uint8)(((uint64)(w)) & 0xff))
|
||||
//#endif // !LOBYTE
|
||||
//
|
||||
//#ifndef HIBYTE
|
||||
//#define HIBYTE(w) ((uint8)((((uint64)(w)) >> 8) & 0xff))
|
||||
//#endif // !HIBYTE
|
||||
|
||||
|
||||
#define NON_COPYABLE(class_name) \
|
||||
class_name(const class_name&) = delete; \
|
||||
class_name& operator=(const class_name&) = delete;
|
128
human_render/Ipc/Ipc.cpp
Normal file
128
human_render/Ipc/Ipc.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
#include "Ipc/Ipc.h"
|
||||
|
||||
#include <functional>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
#include "Ipc/ReadCallback.h"
|
||||
|
||||
std::shared_ptr<Ipc> Ipc::create(const char* reader_name, const char* writer_name) {
|
||||
struct Creator : public Ipc {
|
||||
Creator(const char* reader_name, const char* writer_name) : Ipc(reader_name, writer_name) {}
|
||||
~Creator() override = default;
|
||||
};
|
||||
return std::make_shared<Creator>(reader_name, writer_name);
|
||||
}
|
||||
|
||||
Ipc::Ipc(const char* reader_name, const char* writer_name) noexcept
|
||||
: reader_name_(reader_name)
|
||||
, writer_name_(writer_name) {
|
||||
sender_ = std::make_unique<ipc::channel>(toUtf8(reader_name_).c_str(), ipc::sender);
|
||||
fprintf(stderr, "%s, %s\n", reader_name, writer_name);
|
||||
}
|
||||
|
||||
Ipc::~Ipc() {
|
||||
stop();
|
||||
}
|
||||
|
||||
bool Ipc::listen() {
|
||||
if (!reciver_stop_.load(std::memory_order_acquire)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
reciver_stop_.store(false);
|
||||
|
||||
std::weak_ptr<Ipc> wThis = shared_from_this();
|
||||
reciver_thread_ = std::move(std::thread(std::bind(&Ipc::doReciver, this, wThis)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Ipc::stop() {
|
||||
if (reciver_stop_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
|
||||
reciver_stop_.store(true, std::memory_order_release);
|
||||
|
||||
sender_->disconnect();
|
||||
receiver_->disconnect();
|
||||
|
||||
if (reciver_thread_.joinable()) {
|
||||
reciver_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool Ipc::send(const char* data, unsigned int size) {
|
||||
if (!sender_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sender_->send(data, size);
|
||||
}
|
||||
|
||||
|
||||
bool Ipc::isConnected() {
|
||||
/* if (receiver_) {
|
||||
return receiver_->is_connected();
|
||||
}*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Ipc::reConnect() {
|
||||
sender_->reconnect(ipc::sender);
|
||||
if (receiver_) {
|
||||
receiver_->disconnect();
|
||||
receiver_->reconnect(ipc::receiver);
|
||||
}
|
||||
}
|
||||
|
||||
void Ipc::registReadCallback(const std::shared_ptr<IReaderCallback>& reader) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto itor = std::find(reader_callbacks_.begin(), reader_callbacks_.end(), reader);
|
||||
if (reader_callbacks_.end() != itor) {
|
||||
return;
|
||||
}
|
||||
reader_callbacks_.emplace_back(reader);
|
||||
}
|
||||
|
||||
void Ipc::unregistReadCallback(const std::shared_ptr<IReaderCallback>& reader) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto itor = std::find(reader_callbacks_.begin(), reader_callbacks_.end(), reader);
|
||||
if (reader_callbacks_.end() != itor) {
|
||||
return;
|
||||
}
|
||||
reader_callbacks_.erase(itor);
|
||||
}
|
||||
|
||||
void Ipc::doReciver(std::weak_ptr<Ipc> wThis) {
|
||||
std::shared_ptr<Ipc> self(wThis.lock());
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
|
||||
receiver_ = std::make_unique<ipc::channel>(writer_name_.c_str(), ipc::receiver);
|
||||
while (!reciver_stop_.load(std::memory_order_acquire)) {
|
||||
ipc::buff_t buffer = receiver_->recv(5);
|
||||
if (buffer.empty()) {
|
||||
continue;
|
||||
}
|
||||
const char* data = buffer.get<const char*>();
|
||||
onReciveer(data, static_cast<unsigned int>(buffer.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void Ipc::onReciveer(const char* buffer, unsigned int size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto& read : reader_callbacks_) {
|
||||
read->onRead(buffer, size);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Ipc::toUtf8(const std::string& str) {
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
|
||||
std::wstring wide_str = converter.from_bytes(str);
|
||||
return converter.to_bytes(wide_str);
|
||||
}
|
50
human_render/Ipc/Ipc.h
Normal file
50
human_render/Ipc/Ipc.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
#include <libipc/ipc.h>
|
||||
|
||||
class IReaderCallback;
|
||||
|
||||
class Ipc : public std::enable_shared_from_this<Ipc>{
|
||||
public:
|
||||
static std::shared_ptr<Ipc> create(const char* reader_name, const char* writer_name);
|
||||
public:
|
||||
virtual ~Ipc();
|
||||
|
||||
bool listen();
|
||||
void stop();
|
||||
bool send(const char* data, unsigned int size);
|
||||
bool isConnected();
|
||||
void reConnect();
|
||||
|
||||
void registReadCallback(const std::shared_ptr<IReaderCallback>& reader);
|
||||
void unregistReadCallback(const std::shared_ptr<IReaderCallback>& reader);
|
||||
|
||||
protected:
|
||||
Ipc(const char* reader_name, const char* writer_name) noexcept;
|
||||
|
||||
private:
|
||||
void doReciver(std::weak_ptr<Ipc> wThis);
|
||||
|
||||
void onReciveer(const char* buffer, unsigned int size);
|
||||
|
||||
std::string toUtf8(const std::string& str);
|
||||
|
||||
private:
|
||||
std::string reader_name_;
|
||||
std::string writer_name_;
|
||||
std::unique_ptr<ipc::channel> sender_;
|
||||
std::unique_ptr<ipc::channel> receiver_;
|
||||
std::thread reciver_thread_;
|
||||
std::atomic_bool reciver_stop_{true};
|
||||
|
||||
std::mutex mutex_;
|
||||
using ReaderCallbackList = std::vector<std::shared_ptr<IReaderCallback>>;
|
||||
ReaderCallbackList reader_callbacks_;
|
||||
};
|
||||
|
113
human_render/Ipc/IpcMoudle.cpp
Normal file
113
human_render/Ipc/IpcMoudle.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
#include "Ipc/IpcMoudle.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <stb/stb_image_write.h>
|
||||
|
||||
//#include "Core/Core.h"
|
||||
#include "Ipc/ipclib.h"
|
||||
//#include "ImageBuffer.h"
|
||||
//#include "VHI/VHI.h"
|
||||
|
||||
//IpcMoudle* Singleton<IpcMoudle>::instance_ = nullptr;
|
||||
|
||||
void OnParseImageData(const char* data, size_t size) {
|
||||
if (size < 13) { // Minimum size check: 1 byte identifier + 4 bytes width + 4 bytes height + 4 bytes bit depth
|
||||
std::cerr << "Invalid data size" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract identifier
|
||||
char identifier = data[0];
|
||||
const char* buffer = data + 1;
|
||||
/* if (0x01 == identifier) {
|
||||
IpcMoudle::Get()->PushImage(buffer, size - 1);
|
||||
} else if (0x02 == identifier) {
|
||||
IpcMoudle::Get()->PushVoice(buffer, size - 1);
|
||||
}
|
||||
|
||||
INFOLOG("identifier {}", static_cast<int32>(identifier));*/
|
||||
}
|
||||
|
||||
bool IpcMoudle::Initialize() {
|
||||
//if (!initialize("human_render", "human_product")) {
|
||||
// ERRORLOG("ipc initialize failed");
|
||||
// return false;
|
||||
//}
|
||||
|
||||
//setReaderCallback([](const char* data, unsigned int size) {
|
||||
// //INFOLOG("ipc recive data:{}", size);
|
||||
// OnParseImageData(data, size);
|
||||
// }
|
||||
//);
|
||||
|
||||
//if (!listen()) {
|
||||
// ERRORLOG("ipc listen failed");
|
||||
// return false;
|
||||
//}
|
||||
|
||||
////auto callback = [](const char* data, unsigned int size) {
|
||||
//// //INFOLOG("ipc recive data:{}", size);
|
||||
//// OnParseImageData(data, size);
|
||||
////};
|
||||
////zmqMoudle_ = std::make_unique<ZmqMoudle>(callback);
|
||||
////zmqMoudle_->Start();
|
||||
//lastHeartbeatTime_ = std::chrono::steady_clock::now();
|
||||
return true;
|
||||
}
|
||||
|
||||
void IpcMoudle::Uninitialize() {
|
||||
//zmqMoudle_->Stop();
|
||||
uninitialize();
|
||||
}
|
||||
|
||||
bool IpcMoudle::Send(const char* data, unsigned int size) {
|
||||
return send(data, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
void IpcMoudle::OnFrame() {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - lastHeartbeatTime_);
|
||||
|
||||
if (duration.count() >= 2) {
|
||||
/* constexpr char heartbeat[] = "heartbeat";
|
||||
if (!send("heartbeat", sizeof(heartbeat) / sizeof(heartbeat[0]))) {
|
||||
ERRORLOG("send heartbeat failed");
|
||||
reConnect();
|
||||
}*/
|
||||
lastHeartbeatTime_ = now;
|
||||
}
|
||||
}
|
||||
|
||||
void IpcMoudle::PushImage(const char* data, uint32 size) {
|
||||
// Extract width
|
||||
int width;
|
||||
std::memcpy(&width, data, sizeof(int));
|
||||
|
||||
// Extract height
|
||||
int height;
|
||||
std::memcpy(&height, data + 4, sizeof(int));
|
||||
|
||||
// Extract bit depth
|
||||
int bit_depth;
|
||||
std::memcpy(&bit_depth, data + 8, sizeof(int));
|
||||
|
||||
// Extract image bytes
|
||||
const char* img_bytes = data + 12;
|
||||
size_t img_size = size - 12;
|
||||
//std::vector<uint8> img_data(img_size);
|
||||
//memcpy(img_data.data(), img_bytes, img_size);
|
||||
|
||||
//ImageBuffer::Get()->PushImage(ImageBuffer::IBType::Human, std::move(img_data), width, height, bit_depth);
|
||||
}
|
||||
|
||||
void IpcMoudle::PushVoice(const char* data, uint32 size) {
|
||||
/* IAudioRender* audioRender = VHI::Get()->GetAudioRender();
|
||||
if (nullptr == audioRender) {
|
||||
return;
|
||||
}
|
||||
|
||||
audioRender->Write(data, size);*/
|
||||
}
|
32
human_render/Ipc/IpcMoudle.h
Normal file
32
human_render/Ipc/IpcMoudle.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
//#include "Core/Singleton.h"
|
||||
//#include "Core/Constant.h"
|
||||
|
||||
//#include "Ipc/ZmqMoudle.h"
|
||||
#include "Constant.h"
|
||||
|
||||
class IpcMoudle {// : public Singleton<IpcMoudle> {
|
||||
//NON_COPYABLE(IpcMoudle)
|
||||
|
||||
public:
|
||||
IpcMoudle() = default;
|
||||
virtual ~IpcMoudle() = default;
|
||||
bool Initialize();
|
||||
void Uninitialize();
|
||||
|
||||
bool Send(const char* data, uint32 size);
|
||||
|
||||
void OnFrame();
|
||||
|
||||
void PushImage(const char* data, uint32 size);
|
||||
void PushVoice(const char* data, uint32 size);
|
||||
|
||||
private:
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastHeartbeatTime_;
|
||||
|
||||
//std::unique_ptr<ZmqMoudle> zmqMoudle_;
|
||||
};
|
||||
|
9
human_render/Ipc/ReadCallback.h
Normal file
9
human_render/Ipc/ReadCallback.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
class IReaderCallback {
|
||||
public:
|
||||
virtual ~IReaderCallback() = default;
|
||||
|
||||
// read thread callback
|
||||
virtual void onRead(const char* buffer, unsigned int size) = 0;
|
||||
};
|
62
human_render/Ipc/ZmqMoudle.cpp
Normal file
62
human_render/Ipc/ZmqMoudle.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include "Ipc/ZmqMoudle.h"
|
||||
|
||||
#if 0
|
||||
#include <memory>
|
||||
#include <assert.h>
|
||||
|
||||
#include <zmq.hpp>
|
||||
|
||||
ZmqMoudle::ZmqMoudle(ZmqMoudleCallback callback)
|
||||
: callback_(callback){
|
||||
|
||||
}
|
||||
|
||||
void ZmqMoudle::Start() {
|
||||
if (work_) {
|
||||
return;
|
||||
}
|
||||
|
||||
shouldExit_.store(false);
|
||||
auto run = [this]() {
|
||||
zmq::context_t context(1);
|
||||
zmq::socket_t subscriber(context, ZMQ_SUB);
|
||||
subscriber.connect("tcp://127.0.0.1:55661");
|
||||
subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0);
|
||||
|
||||
bool isFirst = true;
|
||||
while (!shouldExit_.load()) {
|
||||
zmq::message_t message;
|
||||
if (subscriber.recv(message, zmq::recv_flags::none)) {
|
||||
if (isFirst) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// 打印自 Epoch 以来的时间(以秒为单位)
|
||||
auto duration = std::chrono::duration<double>(start.time_since_epoch());
|
||||
int64_t time = duration.count();
|
||||
time = 0;
|
||||
isFirst = true;
|
||||
}
|
||||
const char* data = static_cast<const char*>(message.data());
|
||||
if (nullptr != callback_) {
|
||||
callback_(data, message.size());
|
||||
}
|
||||
} else {
|
||||
::_sleep(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
work_.reset(new std::thread(run));
|
||||
}
|
||||
|
||||
void ZmqMoudle::Stop() {
|
||||
if (!work_) {
|
||||
return;
|
||||
}
|
||||
|
||||
shouldExit_.store(true);
|
||||
if (work_->joinable()) {
|
||||
work_->join();
|
||||
}
|
||||
}
|
||||
#endif
|
22
human_render/Ipc/ZmqMoudle.h
Normal file
22
human_render/Ipc/ZmqMoudle.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#if 0
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
using ZmqMoudleCallback = std::function<void(const char* , unsigned int)>;
|
||||
class ZmqMoudle {
|
||||
public:
|
||||
ZmqMoudle(ZmqMoudleCallback callback);
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
ZmqMoudleCallback callback_;
|
||||
std::unique_ptr<std::thread> work_;
|
||||
void* context_{ nullptr };
|
||||
std::atomic<bool> shouldExit_;
|
||||
};
|
||||
#endif
|
20
human_render/Ipc/config.h
Normal file
20
human_render/Ipc/config.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _IPC_EXPORT_
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#define IPC_EXPORT __declspec( dllexport )
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
#else
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#define IPC_EXPORT __declspec( dllimport )
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
#endif // _IPC_EXPORT_
|
||||
|
80
human_render/Ipc/ipclib.cpp
Normal file
80
human_render/Ipc/ipclib.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include "Ipc/ipclib.h"
|
||||
|
||||
#include <memory>
|
||||
#include <assert.h>
|
||||
|
||||
#include "Ipc.h"
|
||||
#include "ReadCallback.h"
|
||||
|
||||
class ReaderCallback : public IReaderCallback {
|
||||
public:
|
||||
ReaderCallback() = default;
|
||||
~ReaderCallback() override = default;
|
||||
void onRead(const char* buffer, unsigned int size) override {
|
||||
//printf("%s", buffer);
|
||||
if (nullptr != m_readerCallbackFunc) {
|
||||
m_readerCallbackFunc(buffer, size);
|
||||
}
|
||||
}
|
||||
|
||||
void setCallback(ReaderCallbackFunc func) {
|
||||
m_readerCallbackFunc = func;
|
||||
}
|
||||
|
||||
private:
|
||||
ReaderCallback(const ReaderCallback&) = delete;
|
||||
ReaderCallback operator= (const ReaderCallback&) = delete;
|
||||
|
||||
private:
|
||||
ReaderCallbackFunc m_readerCallbackFunc{nullptr};
|
||||
};
|
||||
|
||||
std::shared_ptr<Ipc> g_ipc;
|
||||
std::shared_ptr<ReaderCallback> g_readCallback;
|
||||
|
||||
bool __stdcall initialize(const char* sender_name, const char* receiver_name) {
|
||||
assert(!g_ipc);
|
||||
g_ipc = Ipc::create(sender_name, receiver_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
void __stdcall uninitialize() {
|
||||
assert(g_ipc);
|
||||
g_ipc->stop();
|
||||
if (g_readCallback) {
|
||||
g_ipc->unregistReadCallback(g_readCallback);
|
||||
g_readCallback.reset();
|
||||
}
|
||||
g_ipc.reset();
|
||||
}
|
||||
|
||||
bool __stdcall listen() {
|
||||
assert(g_ipc);
|
||||
return g_ipc->listen();
|
||||
}
|
||||
|
||||
bool __stdcall send(const char* data, unsigned int size) {
|
||||
assert(g_ipc);
|
||||
return g_ipc->send(data, size);
|
||||
}
|
||||
|
||||
bool __stdcall setReaderCallback(ReaderCallbackFunc callback) {
|
||||
assert(g_ipc);
|
||||
if (!g_readCallback) {
|
||||
g_readCallback = std::make_shared<ReaderCallback>();
|
||||
g_ipc->registReadCallback(g_readCallback);
|
||||
}
|
||||
g_readCallback->setCallback(callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void __stdcall reConnect() {
|
||||
assert(g_ipc);
|
||||
return g_ipc->reConnect();
|
||||
}
|
||||
|
||||
bool __stdcall isConnect() {
|
||||
assert(g_ipc);
|
||||
return g_ipc->isConnected();
|
||||
}
|
25
human_render/Ipc/ipclib.h
Normal file
25
human_render/Ipc/ipclib.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Ipc/config.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
typedef void(__stdcall *ReaderCallbackFunc)(const char* data, unsigned int size);
|
||||
|
||||
bool __stdcall initialize(const char* sender_name, const char* receiver_name);
|
||||
void __stdcall uninitialize();
|
||||
|
||||
bool __stdcall listen();
|
||||
bool __stdcall send(const char* data, unsigned int size);
|
||||
bool __stdcall setReaderCallback(ReaderCallbackFunc callback);
|
||||
|
||||
void __stdcall reConnect();
|
||||
|
||||
bool __stdcall isConnect();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
87
human_render/Ipc/libipc/buffer.cpp
Normal file
87
human_render/Ipc/libipc/buffer.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#include "libipc/buffer.h"
|
||||
#include "libipc/utility/pimpl.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace ipc {
|
||||
|
||||
bool operator==(buffer const & b1, buffer const & b2) {
|
||||
return (b1.size() == b2.size()) && (std::memcmp(b1.data(), b2.data(), b1.size()) == 0);
|
||||
}
|
||||
|
||||
bool operator!=(buffer const & b1, buffer const & b2) {
|
||||
return !(b1 == b2);
|
||||
}
|
||||
|
||||
class buffer::buffer_ : public pimpl<buffer_> {
|
||||
public:
|
||||
void* p_;
|
||||
std::size_t s_;
|
||||
void* a_;
|
||||
buffer::destructor_t d_;
|
||||
|
||||
buffer_(void* p, std::size_t s, buffer::destructor_t d, void* a)
|
||||
: p_(p), s_(s), a_(a), d_(d) {
|
||||
}
|
||||
|
||||
~buffer_() {
|
||||
if (d_ == nullptr) return;
|
||||
d_((a_ == nullptr) ? p_ : a_, s_);
|
||||
}
|
||||
};
|
||||
|
||||
buffer::buffer()
|
||||
: buffer(nullptr, 0, nullptr, nullptr) {
|
||||
}
|
||||
|
||||
buffer::buffer(void* p, std::size_t s, destructor_t d)
|
||||
: p_(p_->make(p, s, d, nullptr)) {
|
||||
}
|
||||
|
||||
buffer::buffer(void* p, std::size_t s, destructor_t d, void* additional)
|
||||
: p_(p_->make(p, s, d, additional)) {
|
||||
}
|
||||
|
||||
buffer::buffer(void* p, std::size_t s)
|
||||
: buffer(p, s, nullptr) {
|
||||
}
|
||||
|
||||
buffer::buffer(char const & c)
|
||||
: buffer(const_cast<char*>(&c), 1) {
|
||||
}
|
||||
|
||||
buffer::buffer(buffer&& rhs)
|
||||
: buffer() {
|
||||
swap(rhs);
|
||||
}
|
||||
|
||||
buffer::~buffer() {
|
||||
p_->clear();
|
||||
}
|
||||
|
||||
void buffer::swap(buffer& rhs) {
|
||||
std::swap(p_, rhs.p_);
|
||||
}
|
||||
|
||||
buffer& buffer::operator=(buffer rhs) {
|
||||
swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool buffer::empty() const noexcept {
|
||||
return (impl(p_)->p_ == nullptr) || (impl(p_)->s_ == 0);
|
||||
}
|
||||
|
||||
void* buffer::data() noexcept {
|
||||
return impl(p_)->p_;
|
||||
}
|
||||
|
||||
void const * buffer::data() const noexcept {
|
||||
return impl(p_)->p_;
|
||||
}
|
||||
|
||||
std::size_t buffer::size() const noexcept {
|
||||
return impl(p_)->s_;
|
||||
}
|
||||
|
||||
} // namespace ipc
|
67
human_render/Ipc/libipc/buffer.h
Normal file
67
human_render/Ipc/libipc/buffer.h
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
|
||||
#include "libipc/def.h"
|
||||
|
||||
namespace ipc {
|
||||
|
||||
class buffer {
|
||||
public:
|
||||
using destructor_t = void (*)(void*, std::size_t);
|
||||
|
||||
buffer();
|
||||
|
||||
buffer(void* p, std::size_t s, destructor_t d);
|
||||
buffer(void* p, std::size_t s, destructor_t d, void* additional);
|
||||
buffer(void* p, std::size_t s);
|
||||
|
||||
template <std::size_t N>
|
||||
explicit buffer(byte_t const (& data)[N])
|
||||
: buffer(data, sizeof(data)) {
|
||||
}
|
||||
explicit buffer(char const & c);
|
||||
|
||||
buffer(buffer&& rhs);
|
||||
~buffer();
|
||||
|
||||
void swap(buffer& rhs);
|
||||
buffer& operator=(buffer rhs);
|
||||
|
||||
bool empty() const noexcept;
|
||||
|
||||
void * data() noexcept;
|
||||
void const * data() const noexcept;
|
||||
|
||||
template <typename T>
|
||||
T get() const { return T(data()); }
|
||||
|
||||
std::size_t size() const noexcept;
|
||||
|
||||
std::tuple<void*, std::size_t> to_tuple() {
|
||||
return std::make_tuple(data(), size());
|
||||
}
|
||||
|
||||
std::tuple<void const *, std::size_t> to_tuple() const {
|
||||
return std::make_tuple(data(), size());
|
||||
}
|
||||
|
||||
std::vector<byte_t> to_vector() const {
|
||||
return {
|
||||
get<byte_t const *>(),
|
||||
get<byte_t const *>() + size()
|
||||
};
|
||||
}
|
||||
|
||||
friend bool operator==(buffer const & b1, buffer const & b2);
|
||||
friend bool operator!=(buffer const & b1, buffer const & b2);
|
||||
|
||||
private:
|
||||
class buffer_;
|
||||
buffer_* p_;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
141
human_render/Ipc/libipc/circ/elem_array.h
Normal file
141
human_render/Ipc/libipc/circ/elem_array.h
Normal file
@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic> // std::atomic<?>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/rw_lock.h"
|
||||
|
||||
#include "libipc/circ/elem_def.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace circ {
|
||||
|
||||
template <typename Policy,
|
||||
std::size_t DataSize,
|
||||
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))>
|
||||
class elem_array : public ipc::circ::conn_head<Policy> {
|
||||
public:
|
||||
using base_t = ipc::circ::conn_head<Policy>;
|
||||
using policy_t = Policy;
|
||||
using cursor_t = decltype(std::declval<policy_t>().cursor());
|
||||
using elem_t = typename policy_t::template elem_t<DataSize, AlignSize>;
|
||||
|
||||
enum : std::size_t {
|
||||
head_size = sizeof(base_t) + sizeof(policy_t),
|
||||
data_size = DataSize,
|
||||
elem_max = (std::numeric_limits<uint_t<8>>::max)() + 1, // default is 255 + 1
|
||||
elem_size = sizeof(elem_t),
|
||||
block_size = elem_size * elem_max
|
||||
};
|
||||
|
||||
private:
|
||||
policy_t head_;
|
||||
elem_t block_[elem_max] {};
|
||||
|
||||
/**
|
||||
* \remarks 'warning C4348: redefinition of default parameter' with MSVC.
|
||||
* \see
|
||||
* - https://stackoverflow.com/questions/12656239/redefinition-of-default-template-parameter
|
||||
* - https://developercommunity.visualstudio.com/content/problem/425978/incorrect-c4348-warning-in-nested-template-declara.html
|
||||
*/
|
||||
template <typename P, bool/* = relat_trait<P>::is_multi_producer*/>
|
||||
struct sender_checker;
|
||||
|
||||
template <typename P>
|
||||
struct sender_checker<P, true> {
|
||||
constexpr static bool connect() noexcept {
|
||||
// always return true
|
||||
return true;
|
||||
}
|
||||
constexpr static void disconnect() noexcept {}
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
struct sender_checker<P, false> {
|
||||
bool connect() noexcept {
|
||||
return !flag_.test_and_set(std::memory_order_acq_rel);
|
||||
}
|
||||
void disconnect() noexcept {
|
||||
flag_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
// in shm, it should be 0 whether it's initialized or not.
|
||||
std::atomic_flag flag_ = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
||||
template <typename P, bool/* = relat_trait<P>::is_multi_consumer*/>
|
||||
struct receiver_checker;
|
||||
|
||||
template <typename P>
|
||||
struct receiver_checker<P, true> {
|
||||
constexpr static cc_t connect(base_t &conn) noexcept {
|
||||
return conn.connect();
|
||||
}
|
||||
constexpr static cc_t disconnect(base_t &conn, cc_t cc_id) noexcept {
|
||||
return conn.disconnect(cc_id);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
struct receiver_checker<P, false> : protected sender_checker<P, false> {
|
||||
cc_t connect(base_t &conn) noexcept {
|
||||
return sender_checker<P, false>::connect() ? conn.connect() : 0;
|
||||
}
|
||||
cc_t disconnect(base_t &conn, cc_t cc_id) noexcept {
|
||||
sender_checker<P, false>::disconnect();
|
||||
return conn.disconnect(cc_id);
|
||||
}
|
||||
};
|
||||
|
||||
sender_checker <policy_t, relat_trait<policy_t>::is_multi_producer> s_ckr_;
|
||||
receiver_checker<policy_t, relat_trait<policy_t>::is_multi_consumer> r_ckr_;
|
||||
|
||||
// make these be private
|
||||
using base_t::connect;
|
||||
using base_t::disconnect;
|
||||
|
||||
public:
|
||||
bool connect_sender() noexcept {
|
||||
return s_ckr_.connect();
|
||||
}
|
||||
|
||||
void disconnect_sender() noexcept {
|
||||
return s_ckr_.disconnect();
|
||||
}
|
||||
|
||||
cc_t connect_receiver() noexcept {
|
||||
return r_ckr_.connect(*this);
|
||||
}
|
||||
|
||||
cc_t disconnect_receiver(cc_t cc_id) noexcept {
|
||||
return r_ckr_.disconnect(*this, cc_id);
|
||||
}
|
||||
|
||||
cursor_t cursor() const noexcept {
|
||||
return head_.cursor();
|
||||
}
|
||||
|
||||
template <typename Q, typename F>
|
||||
bool push(Q* que, F&& f) {
|
||||
return head_.push(que, std::forward<F>(f), block_);
|
||||
}
|
||||
|
||||
template <typename Q, typename F>
|
||||
bool force_push(Q* que, F&& f) {
|
||||
return head_.force_push(que, std::forward<F>(f), block_);
|
||||
}
|
||||
|
||||
template <typename Q, typename F, typename R>
|
||||
bool pop(Q* que, cursor_t* cur, F&& f, R&& out) {
|
||||
if (cur == nullptr) return false;
|
||||
return head_.pop(que, *cur, std::forward<F>(f), std::forward<R>(out), block_);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace circ
|
||||
} // namespace ipc
|
109
human_render/Ipc/libipc/circ/elem_def.h
Normal file
109
human_render/Ipc/libipc/circ/elem_def.h
Normal file
@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <new>
|
||||
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/rw_lock.h"
|
||||
|
||||
#include "libipc/platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace circ {
|
||||
|
||||
using u1_t = ipc::uint_t<8>;
|
||||
using u2_t = ipc::uint_t<32>;
|
||||
|
||||
/** only supports max 32 connections in broadcast mode */
|
||||
using cc_t = u2_t;
|
||||
|
||||
constexpr u1_t index_of(u2_t c) noexcept {
|
||||
return static_cast<u1_t>(c);
|
||||
}
|
||||
|
||||
class conn_head_base {
|
||||
protected:
|
||||
std::atomic<cc_t> cc_{0}; // connections
|
||||
ipc::spin_lock lc_;
|
||||
std::atomic<bool> constructed_{false};
|
||||
|
||||
public:
|
||||
void init() {
|
||||
/* DCLP */
|
||||
if (!constructed_.load(std::memory_order_acquire)) {
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_);
|
||||
if (!constructed_.load(std::memory_order_relaxed)) {
|
||||
::new (this) conn_head_base;
|
||||
constructed_.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn_head_base() = default;
|
||||
conn_head_base(conn_head_base const &) = delete;
|
||||
conn_head_base &operator=(conn_head_base const &) = delete;
|
||||
|
||||
cc_t connections(std::memory_order order = std::memory_order_acquire) const noexcept {
|
||||
return this->cc_.load(order);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename P, bool = relat_trait<P>::is_broadcast>
|
||||
class conn_head;
|
||||
|
||||
template <typename P>
|
||||
class conn_head<P, true> : public conn_head_base {
|
||||
public:
|
||||
cc_t connect() noexcept {
|
||||
for (unsigned k = 0;; ipc::yield(k)) {
|
||||
cc_t curr = this->cc_.load(std::memory_order_acquire);
|
||||
cc_t next = curr | (curr + 1); // find the first 0, and set it to 1.
|
||||
if (next == 0) {
|
||||
// connection-slot is full.
|
||||
return 0;
|
||||
}
|
||||
if (this->cc_.compare_exchange_weak(curr, next, std::memory_order_release)) {
|
||||
return next ^ curr; // return connected id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cc_t disconnect(cc_t cc_id) noexcept {
|
||||
return this->cc_.fetch_and(~cc_id, std::memory_order_acq_rel) & ~cc_id;
|
||||
}
|
||||
|
||||
std::size_t conn_count(std::memory_order order = std::memory_order_acquire) const noexcept {
|
||||
cc_t cur = this->cc_.load(order);
|
||||
cc_t cnt; // accumulates the total bits set in cc
|
||||
for (cnt = 0; cur; ++cnt) cur &= cur - 1;
|
||||
return cnt;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
class conn_head<P, false> : public conn_head_base {
|
||||
public:
|
||||
cc_t connect() noexcept {
|
||||
return this->cc_.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
}
|
||||
|
||||
cc_t disconnect(cc_t cc_id) noexcept {
|
||||
if (cc_id == ~static_cast<circ::cc_t>(0u)) {
|
||||
// clear all connections
|
||||
this->cc_.store(0, std::memory_order_relaxed);
|
||||
return 0u;
|
||||
}
|
||||
else {
|
||||
return this->cc_.fetch_sub(1, std::memory_order_relaxed) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t conn_count(std::memory_order order = std::memory_order_acquire) const noexcept {
|
||||
return this->connections(order);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace circ
|
||||
} // namespace ipc
|
38
human_render/Ipc/libipc/condition.h
Normal file
38
human_render/Ipc/libipc/condition.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // std::uint64_t
|
||||
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/mutex.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace sync {
|
||||
|
||||
class condition {
|
||||
condition(condition const &) = delete;
|
||||
condition &operator=(condition const &) = delete;
|
||||
|
||||
public:
|
||||
condition();
|
||||
explicit condition(char const *name);
|
||||
~condition();
|
||||
|
||||
void const *native() const noexcept;
|
||||
void *native() noexcept;
|
||||
|
||||
bool valid() const noexcept;
|
||||
|
||||
bool open(char const *name) noexcept;
|
||||
void close() noexcept;
|
||||
|
||||
bool wait(ipc::sync::mutex &mtx, std::uint64_t tm = ipc::invalid_value) noexcept;
|
||||
bool notify(ipc::sync::mutex &mtx) noexcept;
|
||||
bool broadcast(ipc::sync::mutex &mtx) noexcept;
|
||||
|
||||
private:
|
||||
class condition_;
|
||||
condition_* p_;
|
||||
};
|
||||
|
||||
} // namespace sync
|
||||
} // namespace ipc
|
73
human_render/Ipc/libipc/def.h
Normal file
73
human_render/Ipc/libipc/def.h
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
namespace ipc {
|
||||
|
||||
// types
|
||||
|
||||
using byte_t = std::uint8_t;
|
||||
|
||||
template <std::size_t N>
|
||||
struct uint;
|
||||
|
||||
template <> struct uint<8 > { using type = std::uint8_t ; };
|
||||
template <> struct uint<16> { using type = std::uint16_t; };
|
||||
template <> struct uint<32> { using type = std::uint32_t; };
|
||||
template <> struct uint<64> { using type = std::uint64_t; };
|
||||
|
||||
template <std::size_t N>
|
||||
using uint_t = typename uint<N>::type;
|
||||
|
||||
// constants
|
||||
|
||||
enum : std::uint32_t {
|
||||
invalid_value = (std::numeric_limits<std::uint32_t>::max)(),
|
||||
default_timeout = 100, // ms
|
||||
};
|
||||
|
||||
enum : std::size_t {
|
||||
data_length = 64,
|
||||
large_msg_limit = data_length,
|
||||
large_msg_align = 1024,
|
||||
large_msg_cache = 32,
|
||||
};
|
||||
|
||||
enum class relat { // multiplicity of the relationship
|
||||
single,
|
||||
multi
|
||||
};
|
||||
|
||||
enum class trans { // transmission
|
||||
unicast,
|
||||
broadcast
|
||||
};
|
||||
|
||||
// producer-consumer policy flag
|
||||
|
||||
template <relat Rp, relat Rc, trans Ts>
|
||||
struct wr {};
|
||||
|
||||
template <typename WR>
|
||||
struct relat_trait;
|
||||
|
||||
template <relat Rp, relat Rc, trans Ts>
|
||||
struct relat_trait<wr<Rp, Rc, Ts>> {
|
||||
constexpr static bool is_multi_producer = (Rp == relat::multi);
|
||||
constexpr static bool is_multi_consumer = (Rc == relat::multi);
|
||||
constexpr static bool is_broadcast = (Ts == trans::broadcast);
|
||||
};
|
||||
|
||||
template <template <typename> class Policy, typename Flag>
|
||||
struct relat_trait<Policy<Flag>> : relat_trait<Flag> {};
|
||||
|
||||
// the prefix tag of a channel
|
||||
struct prefix {
|
||||
char const *str;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
778
human_render/Ipc/libipc/ipc.cpp
Normal file
778
human_render/Ipc/libipc/ipc.cpp
Normal file
@ -0,0 +1,778 @@
|
||||
|
||||
#include <type_traits>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <utility> // std::pair, std::move, std::forward
|
||||
#include <atomic>
|
||||
#include <type_traits> // aligned_storage_t
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
|
||||
#include "libipc/ipc.h"
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/shm.h"
|
||||
#include "libipc/pool_alloc.h"
|
||||
#include "libipc/queue.h"
|
||||
#include "libipc/policy.h"
|
||||
#include "libipc/rw_lock.h"
|
||||
#include "libipc/waiter.h"
|
||||
|
||||
#include "libipc/utility/log.h"
|
||||
#include "libipc/utility/id_pool.h"
|
||||
#include "libipc/utility/scope_guard.h"
|
||||
#include "libipc/utility/utility.h"
|
||||
|
||||
#include "libipc/memory/resource.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
#include "libipc/circ/elem_array.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using msg_id_t = std::uint32_t;
|
||||
using acc_t = std::atomic<msg_id_t>;
|
||||
|
||||
template <std::size_t DataSize, std::size_t AlignSize>
|
||||
struct msg_t;
|
||||
|
||||
template <std::size_t AlignSize>
|
||||
struct msg_t<0, AlignSize> {
|
||||
msg_id_t cc_id_;
|
||||
msg_id_t id_;
|
||||
std::int32_t remain_;
|
||||
bool storage_;
|
||||
};
|
||||
|
||||
template <std::size_t DataSize, std::size_t AlignSize>
|
||||
struct msg_t : msg_t<0, AlignSize> {
|
||||
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
||||
|
||||
msg_t() = default;
|
||||
msg_t(msg_id_t cc_id, msg_id_t id, std::int32_t remain, void const * data, std::size_t size)
|
||||
: msg_t<0, AlignSize> {cc_id, id, remain, (data == nullptr) || (size == 0)} {
|
||||
if (this->storage_) {
|
||||
if (data != nullptr) {
|
||||
// copy storage-id
|
||||
*reinterpret_cast<ipc::storage_id_t*>(&data_) =
|
||||
*static_cast<ipc::storage_id_t const *>(data);
|
||||
}
|
||||
}
|
||||
else std::memcpy(&data_, data, size);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
ipc::buff_t make_cache(T& data, std::size_t size) {
|
||||
auto ptr = ipc::mem::alloc(size);
|
||||
std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size));
|
||||
return { ptr, size, ipc::mem::free };
|
||||
}
|
||||
|
||||
acc_t *cc_acc(ipc::string const &pref) {
|
||||
static ipc::unordered_map<ipc::string, ipc::shm::handle> handles;
|
||||
static std::mutex lock;
|
||||
std::lock_guard<std::mutex> guard {lock};
|
||||
auto it = handles.find(pref);
|
||||
if (it == handles.end()) {
|
||||
ipc::string shm_name {ipc::make_prefix(pref, {"CA_CONN__"})};
|
||||
ipc::shm::handle h;
|
||||
if (!h.acquire(shm_name.c_str(), sizeof(acc_t))) {
|
||||
ipc::error("[cc_acc] acquire failed: %s\n", shm_name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
it = handles.emplace(pref, std::move(h)).first;
|
||||
}
|
||||
return static_cast<acc_t *>(it->second.get());
|
||||
}
|
||||
|
||||
struct cache_t {
|
||||
std::size_t fill_;
|
||||
ipc::buff_t buff_;
|
||||
|
||||
cache_t(std::size_t f, ipc::buff_t && b)
|
||||
: fill_(f), buff_(std::move(b))
|
||||
{}
|
||||
|
||||
void append(void const * data, std::size_t size) {
|
||||
if (fill_ >= buff_.size() || data == nullptr || size == 0) return;
|
||||
auto new_fill = (ipc::detail::min)(fill_ + size, buff_.size());
|
||||
std::memcpy(static_cast<ipc::byte_t*>(buff_.data()) + fill_, data, new_fill - fill_);
|
||||
fill_ = new_fill;
|
||||
}
|
||||
};
|
||||
|
||||
struct conn_info_head {
|
||||
|
||||
ipc::string prefix_;
|
||||
ipc::string name_;
|
||||
msg_id_t cc_id_; // connection-info id
|
||||
ipc::detail::waiter cc_waiter_, wt_waiter_, rd_waiter_;
|
||||
ipc::shm::handle acc_h_;
|
||||
|
||||
conn_info_head(char const * prefix, char const * name)
|
||||
: prefix_{ipc::make_string(prefix)}
|
||||
, name_ {ipc::make_string(name)}
|
||||
, cc_id_ {} {}
|
||||
|
||||
void init() {
|
||||
if (!cc_waiter_.valid()) cc_waiter_.open(ipc::make_prefix(prefix_, {"CC_CONN__", name_}).c_str());
|
||||
if (!wt_waiter_.valid()) wt_waiter_.open(ipc::make_prefix(prefix_, {"WT_CONN__", name_}).c_str());
|
||||
if (!rd_waiter_.valid()) rd_waiter_.open(ipc::make_prefix(prefix_, {"RD_CONN__", name_}).c_str());
|
||||
if (!acc_h_.valid()) acc_h_.acquire(ipc::make_prefix(prefix_, {"AC_CONN__", name_}).c_str(), sizeof(acc_t));
|
||||
if (cc_id_ != 0) {
|
||||
return;
|
||||
}
|
||||
acc_t *pacc = cc_acc(prefix_);
|
||||
if (pacc == nullptr) {
|
||||
// Failed to obtain the global accumulator.
|
||||
return;
|
||||
}
|
||||
cc_id_ = pacc->fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
if (cc_id_ == 0) {
|
||||
// The identity cannot be 0.
|
||||
cc_id_ = pacc->fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void quit_waiting() {
|
||||
cc_waiter_.quit_waiting();
|
||||
wt_waiter_.quit_waiting();
|
||||
rd_waiter_.quit_waiting();
|
||||
}
|
||||
|
||||
auto acc() {
|
||||
return static_cast<acc_t*>(acc_h_.get());
|
||||
}
|
||||
|
||||
auto& recv_cache() {
|
||||
thread_local ipc::unordered_map<msg_id_t, cache_t> tls;
|
||||
return tls;
|
||||
}
|
||||
};
|
||||
|
||||
IPC_CONSTEXPR_ std::size_t align_chunk_size(std::size_t size) noexcept {
|
||||
return (((size - 1) / ipc::large_msg_align) + 1) * ipc::large_msg_align;
|
||||
}
|
||||
|
||||
IPC_CONSTEXPR_ std::size_t calc_chunk_size(std::size_t size) noexcept {
|
||||
return ipc::make_align(alignof(std::max_align_t), align_chunk_size(
|
||||
ipc::make_align(alignof(std::max_align_t), sizeof(std::atomic<ipc::circ::cc_t>)) + size));
|
||||
}
|
||||
|
||||
struct chunk_t {
|
||||
std::atomic<ipc::circ::cc_t> &conns() noexcept {
|
||||
return *reinterpret_cast<std::atomic<ipc::circ::cc_t> *>(this);
|
||||
}
|
||||
|
||||
void *data() noexcept {
|
||||
return reinterpret_cast<ipc::byte_t *>(this)
|
||||
+ ipc::make_align(alignof(std::max_align_t), sizeof(std::atomic<ipc::circ::cc_t>));
|
||||
}
|
||||
};
|
||||
|
||||
struct chunk_info_t {
|
||||
ipc::id_pool<> pool_;
|
||||
ipc::spin_lock lock_;
|
||||
|
||||
IPC_CONSTEXPR_ static std::size_t chunks_mem_size(std::size_t chunk_size) noexcept {
|
||||
return ipc::id_pool<>::max_count * chunk_size;
|
||||
}
|
||||
|
||||
ipc::byte_t *chunks_mem() noexcept {
|
||||
return reinterpret_cast<ipc::byte_t *>(this + 1);
|
||||
}
|
||||
|
||||
chunk_t *at(std::size_t chunk_size, ipc::storage_id_t id) noexcept {
|
||||
if (id < 0) return nullptr;
|
||||
return reinterpret_cast<chunk_t *>(chunks_mem() + (chunk_size * id));
|
||||
}
|
||||
};
|
||||
|
||||
auto& chunk_storages() {
|
||||
class chunk_handle_t {
|
||||
ipc::unordered_map<ipc::string, ipc::shm::handle> handles_;
|
||||
std::mutex lock_;
|
||||
|
||||
static bool make_handle(ipc::shm::handle &h, ipc::string const &shm_name, std::size_t chunk_size) {
|
||||
if (!h.valid() &&
|
||||
!h.acquire( shm_name.c_str(),
|
||||
sizeof(chunk_info_t) + chunk_info_t::chunks_mem_size(chunk_size) )) {
|
||||
ipc::error("[chunk_storages] chunk_shm.id_info_.acquire failed: chunk_size = %zd\n", chunk_size);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
chunk_info_t *get_info(conn_info_head *inf, std::size_t chunk_size) {
|
||||
ipc::string pref {(inf == nullptr) ? ipc::string{} : inf->prefix_};
|
||||
ipc::string shm_name {ipc::make_prefix(pref, {"CHUNK_INFO__", ipc::to_string(chunk_size)})};
|
||||
ipc::shm::handle *h;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard {lock_};
|
||||
h = &(handles_[pref]);
|
||||
if (!make_handle(*h, shm_name, chunk_size)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
auto *info = static_cast<chunk_info_t*>(h->get());
|
||||
if (info == nullptr) {
|
||||
ipc::error("[chunk_storages] chunk_shm.id_info_.get failed: chunk_size = %zd\n", chunk_size);
|
||||
return nullptr;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
};
|
||||
using deleter_t = void (*)(chunk_handle_t*);
|
||||
using chunk_handle_ptr_t = std::unique_ptr<chunk_handle_t, deleter_t>;
|
||||
static ipc::map<std::size_t, chunk_handle_ptr_t> chunk_hs;
|
||||
return chunk_hs;
|
||||
}
|
||||
|
||||
chunk_info_t *chunk_storage_info(conn_info_head *inf, std::size_t chunk_size) {
|
||||
auto &storages = chunk_storages();
|
||||
std::decay_t<decltype(storages)>::iterator it;
|
||||
{
|
||||
static ipc::rw_lock lock;
|
||||
IPC_UNUSED_ std::shared_lock<ipc::rw_lock> guard {lock};
|
||||
if ((it = storages.find(chunk_size)) == storages.end()) {
|
||||
using chunk_handle_ptr_t = std::decay_t<decltype(storages)>::value_type::second_type;
|
||||
using chunk_handle_t = chunk_handle_ptr_t::element_type;
|
||||
guard.unlock();
|
||||
IPC_UNUSED_ std::lock_guard<ipc::rw_lock> guard {lock};
|
||||
it = storages.emplace(chunk_size, chunk_handle_ptr_t{
|
||||
ipc::mem::alloc<chunk_handle_t>(), [](chunk_handle_t *p) {
|
||||
ipc::mem::destruct(p);
|
||||
}}).first;
|
||||
}
|
||||
}
|
||||
return it->second->get_info(inf, chunk_size);
|
||||
}
|
||||
|
||||
std::pair<ipc::storage_id_t, void*> acquire_storage(conn_info_head *inf, std::size_t size, ipc::circ::cc_t conns) {
|
||||
std::size_t chunk_size = calc_chunk_size(size);
|
||||
auto info = chunk_storage_info(inf, chunk_size);
|
||||
if (info == nullptr) return {};
|
||||
|
||||
info->lock_.lock();
|
||||
info->pool_.prepare();
|
||||
// got an unique id
|
||||
auto id = info->pool_.acquire();
|
||||
info->lock_.unlock();
|
||||
|
||||
auto chunk = info->at(chunk_size, id);
|
||||
if (chunk == nullptr) return {};
|
||||
chunk->conns().store(conns, std::memory_order_relaxed);
|
||||
return { id, chunk->data() };
|
||||
}
|
||||
|
||||
void *find_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size) {
|
||||
if (id < 0) {
|
||||
ipc::error("[find_storage] id is invalid: id = %ld, size = %zd\n", (long)id, size);
|
||||
return nullptr;
|
||||
}
|
||||
std::size_t chunk_size = calc_chunk_size(size);
|
||||
auto info = chunk_storage_info(inf, chunk_size);
|
||||
if (info == nullptr) return nullptr;
|
||||
return info->at(chunk_size, id)->data();
|
||||
}
|
||||
|
||||
void release_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size) {
|
||||
if (id < 0) {
|
||||
ipc::error("[release_storage] id is invalid: id = %ld, size = %zd\n", (long)id, size);
|
||||
return;
|
||||
}
|
||||
std::size_t chunk_size = calc_chunk_size(size);
|
||||
auto info = chunk_storage_info(inf, chunk_size);
|
||||
if (info == nullptr) return;
|
||||
info->lock_.lock();
|
||||
info->pool_.release(id);
|
||||
info->lock_.unlock();
|
||||
}
|
||||
|
||||
template <ipc::relat Rp, ipc::relat Rc>
|
||||
bool sub_rc(ipc::wr<Rp, Rc, ipc::trans::unicast>,
|
||||
std::atomic<ipc::circ::cc_t> &/*conns*/, ipc::circ::cc_t /*curr_conns*/, ipc::circ::cc_t /*conn_id*/) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <ipc::relat Rp, ipc::relat Rc>
|
||||
bool sub_rc(ipc::wr<Rp, Rc, ipc::trans::broadcast>,
|
||||
std::atomic<ipc::circ::cc_t> &conns, ipc::circ::cc_t curr_conns, ipc::circ::cc_t conn_id) noexcept {
|
||||
auto last_conns = curr_conns & ~conn_id;
|
||||
for (unsigned k = 0;;) {
|
||||
auto chunk_conns = conns.load(std::memory_order_acquire);
|
||||
if (conns.compare_exchange_weak(chunk_conns, chunk_conns & last_conns, std::memory_order_release)) {
|
||||
return (chunk_conns & last_conns) == 0;
|
||||
}
|
||||
ipc::yield(k);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
void recycle_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size, ipc::circ::cc_t curr_conns, ipc::circ::cc_t conn_id) {
|
||||
if (id < 0) {
|
||||
ipc::error("[recycle_storage] id is invalid: id = %ld, size = %zd\n", (long)id, size);
|
||||
return;
|
||||
}
|
||||
std::size_t chunk_size = calc_chunk_size(size);
|
||||
auto info = chunk_storage_info(inf, chunk_size);
|
||||
if (info == nullptr) return;
|
||||
|
||||
auto chunk = info->at(chunk_size, id);
|
||||
if (chunk == nullptr) return;
|
||||
|
||||
if (!sub_rc(Flag{}, chunk->conns(), curr_conns, conn_id)) {
|
||||
return;
|
||||
}
|
||||
info->lock_.lock();
|
||||
info->pool_.release(id);
|
||||
info->lock_.unlock();
|
||||
}
|
||||
|
||||
template <typename MsgT>
|
||||
bool clear_message(conn_info_head *inf, void* p) {
|
||||
auto msg = static_cast<MsgT*>(p);
|
||||
if (msg->storage_) {
|
||||
std::int32_t r_size = static_cast<std::int32_t>(ipc::data_length) + msg->remain_;
|
||||
if (r_size <= 0) {
|
||||
ipc::error("[clear_message] invalid msg size: %d\n", (int)r_size);
|
||||
return true;
|
||||
}
|
||||
release_storage(*reinterpret_cast<ipc::storage_id_t*>(&msg->data_),
|
||||
inf, static_cast<std::size_t>(r_size));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename W, typename F>
|
||||
bool wait_for(W& waiter, F&& pred, std::uint64_t tm) {
|
||||
if (tm == 0) return !pred();
|
||||
for (unsigned k = 0; pred();) {
|
||||
bool ret = true;
|
||||
ipc::sleep(k, [&k, &ret, &waiter, &pred, tm] {
|
||||
ret = waiter.wait_if(std::forward<F>(pred), tm);
|
||||
k = 0;
|
||||
});
|
||||
if (!ret) return false; // timeout or fail
|
||||
if (k == 0) break; // k has been reset
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Policy,
|
||||
std::size_t DataSize = ipc::data_length,
|
||||
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))>
|
||||
struct queue_generator {
|
||||
|
||||
using queue_t = ipc::queue<msg_t<DataSize, AlignSize>, Policy>;
|
||||
|
||||
struct conn_info_t : conn_info_head {
|
||||
queue_t que_;
|
||||
|
||||
conn_info_t(char const * pref, char const * name)
|
||||
: conn_info_head{pref, name} { init(); }
|
||||
|
||||
void init() {
|
||||
conn_info_head::init();
|
||||
if (!que_.valid()) {
|
||||
que_.open(ipc::make_prefix(prefix_, {
|
||||
"QU_CONN__",
|
||||
ipc::to_string(DataSize), "__",
|
||||
ipc::to_string(AlignSize), "__",
|
||||
this->name_}).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void disconnect_receiver() {
|
||||
bool dis = que_.disconnect();
|
||||
this->quit_waiting();
|
||||
if (dis) {
|
||||
this->recv_cache().clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Policy>
|
||||
struct detail_impl {
|
||||
|
||||
using policy_t = Policy;
|
||||
using flag_t = typename policy_t::flag_t;
|
||||
using queue_t = typename queue_generator<policy_t>::queue_t;
|
||||
using conn_info_t = typename queue_generator<policy_t>::conn_info_t;
|
||||
|
||||
constexpr static conn_info_t* info_of(ipc::handle_t h) noexcept {
|
||||
return static_cast<conn_info_t*>(h);
|
||||
}
|
||||
|
||||
constexpr static queue_t* queue_of(ipc::handle_t h) noexcept {
|
||||
return (info_of(h) == nullptr) ? nullptr : &(info_of(h)->que_);
|
||||
}
|
||||
|
||||
/* API implementations */
|
||||
|
||||
static bool connect(ipc::handle_t * ph, ipc::prefix pref, char const * name, bool start_to_recv) {
|
||||
assert(ph != nullptr);
|
||||
if (*ph == nullptr) {
|
||||
*ph = ipc::mem::alloc<conn_info_t>(pref.str, name);
|
||||
}
|
||||
return reconnect(ph, start_to_recv);
|
||||
}
|
||||
|
||||
static bool connect(ipc::handle_t * ph, char const * name, bool start_to_recv) {
|
||||
return connect(ph, {nullptr}, name, start_to_recv);
|
||||
}
|
||||
|
||||
static void disconnect(ipc::handle_t h) {
|
||||
auto que = queue_of(h);
|
||||
if (que == nullptr) {
|
||||
return;
|
||||
}
|
||||
que->shut_sending();
|
||||
assert(info_of(h) != nullptr);
|
||||
info_of(h)->disconnect_receiver();
|
||||
}
|
||||
|
||||
static bool reconnect(ipc::handle_t * ph, bool start_to_recv) {
|
||||
assert(ph != nullptr);
|
||||
assert(*ph != nullptr);
|
||||
auto que = queue_of(*ph);
|
||||
if (que == nullptr) {
|
||||
return false;
|
||||
}
|
||||
info_of(*ph)->init();
|
||||
if (start_to_recv) {
|
||||
que->shut_sending();
|
||||
if (que->connect()) { // wouldn't connect twice
|
||||
info_of(*ph)->cc_waiter_.broadcast();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// start_to_recv == false
|
||||
if (que->connected()) {
|
||||
info_of(*ph)->disconnect_receiver();
|
||||
}
|
||||
return que->ready_sending();
|
||||
}
|
||||
|
||||
static void destroy(ipc::handle_t h) {
|
||||
disconnect(h);
|
||||
ipc::mem::free(info_of(h));
|
||||
}
|
||||
|
||||
static std::size_t recv_count(ipc::handle_t h) noexcept {
|
||||
auto que = queue_of(h);
|
||||
if (que == nullptr) {
|
||||
return ipc::invalid_value;
|
||||
}
|
||||
return que->conn_count();
|
||||
}
|
||||
|
||||
static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm) {
|
||||
auto que = queue_of(h);
|
||||
if (que == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return wait_for(info_of(h)->cc_waiter_, [que, r_count] {
|
||||
return que->conn_count() < r_count;
|
||||
}, tm);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t size) {
|
||||
if (data == nullptr || size == 0) {
|
||||
ipc::error("fail: send(%p, %zd)\n", data, size);
|
||||
return false;
|
||||
}
|
||||
auto que = queue_of(h);
|
||||
if (que == nullptr) {
|
||||
ipc::error("fail: send, queue_of(h) == nullptr\n");
|
||||
return false;
|
||||
}
|
||||
if (que->elems() == nullptr) {
|
||||
ipc::error("fail: send, queue_of(h)->elems() == nullptr\n");
|
||||
return false;
|
||||
}
|
||||
if (!que->ready_sending()) {
|
||||
ipc::error("fail: send, que->ready_sending() == false\n");
|
||||
return false;
|
||||
}
|
||||
ipc::circ::cc_t conns = que->elems()->connections(std::memory_order_relaxed);
|
||||
if (conns == 0) {
|
||||
ipc::error("fail: send, there is no receiver on this connection.\n");
|
||||
return false;
|
||||
}
|
||||
// calc a new message id
|
||||
conn_info_t *inf = info_of(h);
|
||||
auto acc = inf->acc();
|
||||
if (acc == nullptr) {
|
||||
ipc::error("fail: send, info_of(h)->acc() == nullptr\n");
|
||||
return false;
|
||||
}
|
||||
auto msg_id = acc->fetch_add(1, std::memory_order_relaxed);
|
||||
auto try_push = std::forward<F>(gen_push)(inf, que, msg_id);
|
||||
if (size > ipc::large_msg_limit) {
|
||||
auto dat = acquire_storage(inf, size, conns);
|
||||
void * buf = dat.second;
|
||||
if (buf != nullptr) {
|
||||
std::memcpy(buf, data, size);
|
||||
return try_push(static_cast<std::int32_t>(size) -
|
||||
static_cast<std::int32_t>(ipc::data_length), &(dat.first), 0);
|
||||
}
|
||||
// try using message fragment
|
||||
//ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd\n", msg_id, size);
|
||||
}
|
||||
// push message fragment
|
||||
std::int32_t offset = 0;
|
||||
for (std::int32_t i = 0; i < static_cast<std::int32_t>(size / ipc::data_length); ++i, offset += ipc::data_length) {
|
||||
if (!try_push(static_cast<std::int32_t>(size) - offset - static_cast<std::int32_t>(ipc::data_length),
|
||||
static_cast<ipc::byte_t const *>(data) + offset, ipc::data_length)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// if remain > 0, this is the last message fragment
|
||||
std::int32_t remain = static_cast<std::int32_t>(size) - offset;
|
||||
if (remain > 0) {
|
||||
if (!try_push(remain - static_cast<std::int32_t>(ipc::data_length),
|
||||
static_cast<ipc::byte_t const *>(data) + offset,
|
||||
static_cast<std::size_t>(remain))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) {
|
||||
return send([tm](auto *info, auto *que, auto msg_id) {
|
||||
return [tm, info, que, msg_id](std::int32_t remain, void const * data, std::size_t size) {
|
||||
if (!wait_for(info->wt_waiter_, [&] {
|
||||
return !que->push(
|
||||
[](void*) { return true; },
|
||||
info->cc_id_, msg_id, remain, data, size);
|
||||
}, tm)) {
|
||||
ipc::log("force_push: msg_id = %zd, remain = %d, size = %zd\n", msg_id, remain, size);
|
||||
if (!que->force_push(
|
||||
[info](void* p) { return clear_message<typename queue_t::value_t>(info, p); },
|
||||
info->cc_id_, msg_id, remain, data, size)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
info->rd_waiter_.broadcast();
|
||||
return true;
|
||||
};
|
||||
}, h, data, size);
|
||||
}
|
||||
|
||||
static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) {
|
||||
return send([tm](auto *info, auto *que, auto msg_id) {
|
||||
return [tm, info, que, msg_id](std::int32_t remain, void const * data, std::size_t size) {
|
||||
if (!wait_for(info->wt_waiter_, [&] {
|
||||
return !que->push(
|
||||
[](void*) { return true; },
|
||||
info->cc_id_, msg_id, remain, data, size);
|
||||
}, tm)) {
|
||||
return false;
|
||||
}
|
||||
info->rd_waiter_.broadcast();
|
||||
return true;
|
||||
};
|
||||
}, h, data, size);
|
||||
}
|
||||
|
||||
static ipc::buff_t recv(ipc::handle_t h, std::uint64_t tm) {
|
||||
auto que = queue_of(h);
|
||||
if (que == nullptr) {
|
||||
ipc::error("fail: recv, queue_of(h) == nullptr\n");
|
||||
return {};
|
||||
}
|
||||
if (!que->connected()) {
|
||||
// hasn't connected yet, just return.
|
||||
return {};
|
||||
}
|
||||
conn_info_t *inf = info_of(h);
|
||||
auto& rc = inf->recv_cache();
|
||||
for (;;) {
|
||||
// pop a new message
|
||||
typename queue_t::value_t msg {};
|
||||
if (!wait_for(inf->rd_waiter_, [que, &msg] {
|
||||
return !que->pop(msg);
|
||||
}, tm)) {
|
||||
// pop failed, just return.
|
||||
return {};
|
||||
}
|
||||
inf->wt_waiter_.broadcast();
|
||||
if ((inf->acc() != nullptr) && (msg.cc_id_ == inf->cc_id_)) {
|
||||
continue; // ignore message to self
|
||||
}
|
||||
// msg.remain_ may minus & abs(msg.remain_) < data_length
|
||||
std::int32_t r_size = static_cast<std::int32_t>(ipc::data_length) + msg.remain_;
|
||||
if (r_size <= 0) {
|
||||
ipc::error("fail: recv, r_size = %d\n", (int)r_size);
|
||||
return {};
|
||||
}
|
||||
std::size_t msg_size = static_cast<std::size_t>(r_size);
|
||||
// large message
|
||||
if (msg.storage_) {
|
||||
ipc::storage_id_t buf_id = *reinterpret_cast<ipc::storage_id_t*>(&msg.data_);
|
||||
void* buf = find_storage(buf_id, inf, msg_size);
|
||||
if (buf != nullptr) {
|
||||
struct recycle_t {
|
||||
ipc::storage_id_t storage_id;
|
||||
conn_info_t * inf;
|
||||
ipc::circ::cc_t curr_conns;
|
||||
ipc::circ::cc_t conn_id;
|
||||
} *r_info = ipc::mem::alloc<recycle_t>(recycle_t{
|
||||
buf_id,
|
||||
inf,
|
||||
que->elems()->connections(std::memory_order_relaxed),
|
||||
que->connected_id()
|
||||
});
|
||||
if (r_info == nullptr) {
|
||||
ipc::log("fail: ipc::mem::alloc<recycle_t>.\n");
|
||||
return ipc::buff_t{buf, msg_size}; // no recycle
|
||||
} else {
|
||||
return ipc::buff_t{buf, msg_size, [](void* p_info, std::size_t size) {
|
||||
auto r_info = static_cast<recycle_t *>(p_info);
|
||||
IPC_UNUSED_ auto finally = ipc::guard([r_info] {
|
||||
ipc::mem::free(r_info);
|
||||
});
|
||||
recycle_storage<flag_t>(r_info->storage_id,
|
||||
r_info->inf,
|
||||
size,
|
||||
r_info->curr_conns,
|
||||
r_info->conn_id);
|
||||
}, r_info};
|
||||
}
|
||||
} else {
|
||||
ipc::log("fail: shm::handle for large message. msg_id: %zd, buf_id: %zd, size: %zd\n", msg.id_, buf_id, msg_size);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// find cache with msg.id_
|
||||
auto cac_it = rc.find(msg.id_);
|
||||
if (cac_it == rc.end()) {
|
||||
if (msg_size <= ipc::data_length) {
|
||||
return make_cache(msg.data_, msg_size);
|
||||
}
|
||||
// gc
|
||||
if (rc.size() > 1024) {
|
||||
std::vector<msg_id_t> need_del;
|
||||
for (auto const & pair : rc) {
|
||||
auto cmp = std::minmax(msg.id_, pair.first);
|
||||
if (cmp.second - cmp.first > 8192) {
|
||||
need_del.push_back(pair.first);
|
||||
}
|
||||
}
|
||||
for (auto id : need_del) rc.erase(id);
|
||||
}
|
||||
// cache the first message fragment
|
||||
rc.emplace(msg.id_, cache_t { ipc::data_length, make_cache(msg.data_, msg_size) });
|
||||
}
|
||||
// has cached before this message
|
||||
else {
|
||||
auto& cac = cac_it->second;
|
||||
// this is the last message fragment
|
||||
if (msg.remain_ <= 0) {
|
||||
cac.append(&(msg.data_), msg_size);
|
||||
// finish this message, erase it from cache
|
||||
auto buff = std::move(cac.buff_);
|
||||
rc.erase(cac_it);
|
||||
return buff;
|
||||
}
|
||||
// there are remain datas after this message
|
||||
cac.append(&(msg.data_), ipc::data_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ipc::buff_t try_recv(ipc::handle_t h) {
|
||||
return recv(h, 0);
|
||||
}
|
||||
|
||||
}; // detail_impl<Policy>
|
||||
|
||||
template <typename Flag>
|
||||
using policy_t = ipc::policy::choose<ipc::circ::elem_array, Flag>;
|
||||
|
||||
} // internal-linkage
|
||||
|
||||
namespace ipc {
|
||||
|
||||
template <typename Flag>
|
||||
ipc::handle_t chan_impl<Flag>::inited() {
|
||||
ipc::detail::waiter::init();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
bool chan_impl<Flag>::connect(ipc::handle_t * ph, char const * name, unsigned mode) {
|
||||
return detail_impl<policy_t<Flag>>::connect(ph, name, mode & receiver);
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
bool chan_impl<Flag>::connect(ipc::handle_t * ph, prefix pref, char const * name, unsigned mode) {
|
||||
return detail_impl<policy_t<Flag>>::connect(ph, pref, name, mode & receiver);
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
bool chan_impl<Flag>::reconnect(ipc::handle_t * ph, unsigned mode) {
|
||||
return detail_impl<policy_t<Flag>>::reconnect(ph, mode & receiver);
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
void chan_impl<Flag>::disconnect(ipc::handle_t h) {
|
||||
detail_impl<policy_t<Flag>>::disconnect(h);
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
void chan_impl<Flag>::destroy(ipc::handle_t h) {
|
||||
detail_impl<policy_t<Flag>>::destroy(h);
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
char const * chan_impl<Flag>::name(ipc::handle_t h) {
|
||||
auto *info = detail_impl<policy_t<Flag>>::info_of(h);
|
||||
return (info == nullptr) ? nullptr : info->name_.c_str();
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
std::size_t chan_impl<Flag>::recv_count(ipc::handle_t h) {
|
||||
return detail_impl<policy_t<Flag>>::recv_count(h);
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
bool chan_impl<Flag>::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm) {
|
||||
return detail_impl<policy_t<Flag>>::wait_for_recv(h, r_count, tm);
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
bool chan_impl<Flag>::send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) {
|
||||
return detail_impl<policy_t<Flag>>::send(h, data, size, tm);
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
buff_t chan_impl<Flag>::recv(ipc::handle_t h, std::uint64_t tm) {
|
||||
return detail_impl<policy_t<Flag>>::recv(h, tm);
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
bool chan_impl<Flag>::try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) {
|
||||
return detail_impl<policy_t<Flag>>::try_send(h, data, size, tm);
|
||||
}
|
||||
|
||||
template <typename Flag>
|
||||
buff_t chan_impl<Flag>::try_recv(ipc::handle_t h) {
|
||||
return detail_impl<policy_t<Flag>>::try_recv(h);
|
||||
}
|
||||
|
||||
template struct chan_impl<ipc::wr<relat::single, relat::single, trans::unicast >>;
|
||||
// template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::unicast >>; // TBD
|
||||
// template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::unicast >>; // TBD
|
||||
template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::broadcast>>;
|
||||
template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::broadcast>>;
|
||||
|
||||
} // namespace ipc
|
199
human_render/Ipc/libipc/ipc.h
Normal file
199
human_render/Ipc/libipc/ipc.h
Normal file
@ -0,0 +1,199 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/buffer.h"
|
||||
#include "libipc/shm.h"
|
||||
|
||||
namespace ipc {
|
||||
|
||||
using handle_t = void*;
|
||||
using buff_t = buffer;
|
||||
|
||||
enum : unsigned {
|
||||
sender,
|
||||
receiver
|
||||
};
|
||||
|
||||
template <typename Flag>
|
||||
struct chan_impl {
|
||||
static ipc::handle_t inited();
|
||||
|
||||
static bool connect (ipc::handle_t * ph, char const * name, unsigned mode);
|
||||
static bool connect (ipc::handle_t * ph, prefix, char const * name, unsigned mode);
|
||||
static bool reconnect (ipc::handle_t * ph, unsigned mode);
|
||||
static void disconnect(ipc::handle_t h);
|
||||
static void destroy (ipc::handle_t h);
|
||||
|
||||
static char const * name(ipc::handle_t h);
|
||||
|
||||
static std::size_t recv_count(ipc::handle_t h);
|
||||
static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm);
|
||||
|
||||
static bool send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm);
|
||||
static buff_t recv(ipc::handle_t h, std::uint64_t tm);
|
||||
|
||||
static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm);
|
||||
static buff_t try_recv(ipc::handle_t h);
|
||||
};
|
||||
|
||||
template <typename Flag>
|
||||
class chan_wrapper {
|
||||
private:
|
||||
using detail_t = chan_impl<Flag>;
|
||||
|
||||
ipc::handle_t h_ = detail_t::inited();
|
||||
unsigned mode_ = ipc::sender;
|
||||
bool connected_ = false;
|
||||
|
||||
public:
|
||||
chan_wrapper() noexcept = default;
|
||||
|
||||
explicit chan_wrapper(char const * name, unsigned mode = ipc::sender)
|
||||
: connected_{this->connect(name, mode)} {
|
||||
}
|
||||
|
||||
chan_wrapper(prefix pref, char const * name, unsigned mode = ipc::sender)
|
||||
: connected_{this->connect(pref, name, mode)} {
|
||||
}
|
||||
|
||||
chan_wrapper(chan_wrapper&& rhs) noexcept
|
||||
: chan_wrapper{} {
|
||||
swap(rhs);
|
||||
}
|
||||
|
||||
~chan_wrapper() {
|
||||
detail_t::destroy(h_);
|
||||
}
|
||||
|
||||
void swap(chan_wrapper& rhs) noexcept {
|
||||
std::swap(h_ , rhs.h_);
|
||||
std::swap(mode_ , rhs.mode_);
|
||||
std::swap(connected_, rhs.connected_);
|
||||
}
|
||||
|
||||
chan_wrapper& operator=(chan_wrapper rhs) noexcept {
|
||||
swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
char const * name() const noexcept {
|
||||
return detail_t::name(h_);
|
||||
}
|
||||
|
||||
ipc::handle_t handle() const noexcept {
|
||||
return h_;
|
||||
}
|
||||
|
||||
bool valid() const noexcept {
|
||||
return (handle() != nullptr);
|
||||
}
|
||||
|
||||
unsigned mode() const noexcept {
|
||||
return mode_;
|
||||
}
|
||||
|
||||
chan_wrapper clone() const {
|
||||
return chan_wrapper { name(), mode_ };
|
||||
}
|
||||
|
||||
/**
|
||||
* Building handle, then try connecting with name & mode flags.
|
||||
*/
|
||||
bool connect(char const * name, unsigned mode = ipc::sender | ipc::receiver) {
|
||||
if (name == nullptr || name[0] == '\0') return false;
|
||||
detail_t::disconnect(h_); // clear old connection
|
||||
return connected_ = detail_t::connect(&h_, name, mode_ = mode);
|
||||
}
|
||||
bool connect(prefix pref, char const * name, unsigned mode = ipc::sender | ipc::receiver) {
|
||||
if (name == nullptr || name[0] == '\0') return false;
|
||||
detail_t::disconnect(h_); // clear old connection
|
||||
return connected_ = detail_t::connect(&h_, pref, name, mode_ = mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try connecting with new mode flags.
|
||||
*/
|
||||
bool reconnect(unsigned mode) {
|
||||
if (!valid()) return false;
|
||||
if (connected_ && (mode_ == mode)) return true;
|
||||
return connected_ = detail_t::reconnect(&h_, mode_ = mode);
|
||||
}
|
||||
|
||||
void disconnect() {
|
||||
if (!valid()) return;
|
||||
detail_t::disconnect(h_);
|
||||
connected_ = false;
|
||||
}
|
||||
|
||||
std::size_t recv_count() const {
|
||||
return detail_t::recv_count(h_);
|
||||
}
|
||||
|
||||
bool wait_for_recv(std::size_t r_count, std::uint64_t tm = invalid_value) const {
|
||||
return detail_t::wait_for_recv(h_, r_count, tm);
|
||||
}
|
||||
|
||||
static bool wait_for_recv(char const * name, std::size_t r_count, std::uint64_t tm = invalid_value) {
|
||||
return chan_wrapper(name).wait_for_recv(r_count, tm);
|
||||
}
|
||||
|
||||
/**
|
||||
* If timeout, this function would call 'force_push' to send the data forcibly.
|
||||
*/
|
||||
bool send(void const * data, std::size_t size, std::uint64_t tm = default_timeout) {
|
||||
return detail_t::send(h_, data, size, tm);
|
||||
}
|
||||
bool send(buff_t const & buff, std::uint64_t tm = default_timeout) {
|
||||
return this->send(buff.data(), buff.size(), tm);
|
||||
}
|
||||
bool send(std::string const & str, std::uint64_t tm = default_timeout) {
|
||||
return this->send(str.c_str(), str.size() + 1, tm);
|
||||
}
|
||||
|
||||
/**
|
||||
* If timeout, this function would just return false.
|
||||
*/
|
||||
bool try_send(void const * data, std::size_t size, std::uint64_t tm = default_timeout) {
|
||||
return detail_t::try_send(h_, data, size, tm);
|
||||
}
|
||||
bool try_send(buff_t const & buff, std::uint64_t tm = default_timeout) {
|
||||
return this->try_send(buff.data(), buff.size(), tm);
|
||||
}
|
||||
bool try_send(std::string const & str, std::uint64_t tm = default_timeout) {
|
||||
return this->try_send(str.c_str(), str.size() + 1, tm);
|
||||
}
|
||||
|
||||
buff_t recv(std::uint64_t tm = invalid_value) {
|
||||
return detail_t::recv(h_, tm);
|
||||
}
|
||||
|
||||
buff_t try_recv() {
|
||||
return detail_t::try_recv(h_);
|
||||
}
|
||||
};
|
||||
|
||||
template <relat Rp, relat Rc, trans Ts>
|
||||
using chan = chan_wrapper<ipc::wr<Rp, Rc, Ts>>;
|
||||
|
||||
/**
|
||||
* \class route
|
||||
*
|
||||
* \note You could use one producer/server/sender for sending messages to a route,
|
||||
* then all the consumers/clients/receivers which are receiving with this route,
|
||||
* would receive your sent messages.
|
||||
* A route could only be used in 1 to N (one producer/writer to multi consumers/readers).
|
||||
*/
|
||||
using route = chan<relat::single, relat::multi, trans::broadcast>;
|
||||
|
||||
/**
|
||||
* \class channel
|
||||
*
|
||||
* \note You could use multi producers/writers for sending messages to a channel,
|
||||
* then all the consumers/readers which are receiving with this channel,
|
||||
* would receive your sent messages.
|
||||
*/
|
||||
using channel = chan<relat::multi, relat::multi, trans::broadcast>;
|
||||
|
||||
} // namespace ipc
|
424
human_render/Ipc/libipc/memory/alloc.h
Normal file
424
human_render/Ipc/libipc/memory/alloc.h
Normal file
@ -0,0 +1,424 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <cstdlib>
|
||||
#include <cassert> // assert
|
||||
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/rw_lock.h"
|
||||
|
||||
#include "libipc/utility/concept.h"
|
||||
#include "libipc/memory/allocator_wrapper.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace mem {
|
||||
|
||||
class static_alloc {
|
||||
public:
|
||||
static void swap(static_alloc&) {}
|
||||
|
||||
static void* alloc(std::size_t size) {
|
||||
return size ? std::malloc(size) : nullptr;
|
||||
}
|
||||
|
||||
static void free(void* p) {
|
||||
std::free(p);
|
||||
}
|
||||
|
||||
static void free(void* p, std::size_t /*size*/) {
|
||||
free(p);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Scope allocation -- The destructor will release all allocated blocks.
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept {
|
||||
return ( (size - 1) & ~(alignment - 1) ) + alignment;
|
||||
}
|
||||
|
||||
IPC_CONCEPT_(has_take, take(std::move(std::declval<Type>())));
|
||||
|
||||
class scope_alloc_base {
|
||||
protected:
|
||||
struct block_t {
|
||||
std::size_t size_;
|
||||
block_t * next_;
|
||||
} * head_ = nullptr, * tail_ = nullptr;
|
||||
|
||||
enum : std::size_t {
|
||||
aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t))
|
||||
};
|
||||
|
||||
public:
|
||||
void swap(scope_alloc_base & rhs) {
|
||||
std::swap(head_, rhs.head_);
|
||||
std::swap(tail_, rhs.tail_);
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return head_ == nullptr;
|
||||
}
|
||||
|
||||
void take(scope_alloc_base && rhs) {
|
||||
if (rhs.empty()) return;
|
||||
if (empty()) swap(rhs);
|
||||
else {
|
||||
std::swap(tail_->next_, rhs.head_);
|
||||
// rhs.head_ should be nullptr here
|
||||
tail_ = rhs.tail_;
|
||||
rhs.tail_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void free(void* /*p*/) {}
|
||||
void free(void* /*p*/, std::size_t) {}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename AllocP = static_alloc>
|
||||
class scope_alloc : public detail::scope_alloc_base {
|
||||
public:
|
||||
using base_t = detail::scope_alloc_base;
|
||||
using alloc_policy = AllocP;
|
||||
|
||||
private:
|
||||
alloc_policy alloc_;
|
||||
|
||||
void free_all() {
|
||||
while (!empty()) {
|
||||
auto curr = head_;
|
||||
head_ = head_->next_;
|
||||
alloc_.free(curr, curr->size_);
|
||||
}
|
||||
// now head_ is nullptr
|
||||
}
|
||||
|
||||
public:
|
||||
scope_alloc() = default;
|
||||
|
||||
scope_alloc(scope_alloc && rhs) { swap(rhs); }
|
||||
scope_alloc& operator=(scope_alloc rhs) { swap(rhs); return (*this); }
|
||||
|
||||
~scope_alloc() { free_all(); }
|
||||
|
||||
void swap(scope_alloc& rhs) {
|
||||
alloc_.swap(rhs.alloc_);
|
||||
base_t::swap(rhs);
|
||||
}
|
||||
|
||||
template <typename A = AllocP>
|
||||
auto take(scope_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||
base_t::take(std::move(rhs));
|
||||
alloc_.take(std::move(rhs.alloc_));
|
||||
}
|
||||
|
||||
template <typename A = AllocP>
|
||||
auto take(scope_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
|
||||
base_t::take(std::move(rhs));
|
||||
}
|
||||
|
||||
void* alloc(std::size_t size) {
|
||||
std::size_t real_size = aligned_block_size + size;
|
||||
auto curr = static_cast<block_t*>(alloc_.alloc(real_size));
|
||||
curr->size_ = real_size;
|
||||
curr->next_ = head_;
|
||||
head_ = curr;
|
||||
if (tail_ == nullptr) {
|
||||
tail_ = curr;
|
||||
}
|
||||
return (reinterpret_cast<byte_t*>(curr) + aligned_block_size);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Fixed-size blocks allocation
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
class fixed_alloc_base {
|
||||
protected:
|
||||
std::size_t block_size_;
|
||||
std::size_t init_expand_;
|
||||
void * cursor_;
|
||||
|
||||
void init(std::size_t block_size, std::size_t init_expand) {
|
||||
block_size_ = block_size;
|
||||
init_expand_ = init_expand;
|
||||
cursor_ = nullptr;
|
||||
}
|
||||
|
||||
static void** node_p(void* node) {
|
||||
return reinterpret_cast<void**>(node);
|
||||
}
|
||||
|
||||
static auto& next(void* node) {
|
||||
return *node_p(node);
|
||||
}
|
||||
|
||||
public:
|
||||
bool operator<(fixed_alloc_base const & right) const {
|
||||
return init_expand_ < right.init_expand_;
|
||||
}
|
||||
|
||||
void set_block_size(std::size_t block_size) {
|
||||
block_size_ = block_size;
|
||||
}
|
||||
|
||||
void swap(fixed_alloc_base& rhs) {
|
||||
std::swap(block_size_ , rhs.block_size_);
|
||||
std::swap(init_expand_, rhs.init_expand_);
|
||||
std::swap(cursor_ , rhs.cursor_);
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return cursor_ == nullptr;
|
||||
}
|
||||
|
||||
void take(fixed_alloc_base && rhs) {
|
||||
assert(block_size_ == rhs.block_size_);
|
||||
init_expand_ = (ipc::detail::max)(init_expand_, rhs.init_expand_);
|
||||
if (rhs.empty()) return;
|
||||
auto curr = cursor_;
|
||||
if (curr != nullptr) while (1) {
|
||||
auto next_cur = next(curr);
|
||||
if (next_cur == nullptr) {
|
||||
std::swap(next(curr), rhs.cursor_);
|
||||
return;
|
||||
}
|
||||
// next_cur != nullptr
|
||||
else curr = next_cur;
|
||||
}
|
||||
// curr == nullptr, means cursor_ == nullptr
|
||||
else std::swap(cursor_, rhs.cursor_);
|
||||
// rhs.cursor_ must be nullptr
|
||||
}
|
||||
|
||||
void free(void* p) {
|
||||
if (p == nullptr) return;
|
||||
next(p) = cursor_;
|
||||
cursor_ = p;
|
||||
}
|
||||
|
||||
void free(void* p, std::size_t) {
|
||||
free(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename AllocP, typename ExpandP>
|
||||
class fixed_alloc : public detail::fixed_alloc_base {
|
||||
public:
|
||||
using base_t = detail::fixed_alloc_base;
|
||||
using alloc_policy = AllocP;
|
||||
|
||||
private:
|
||||
alloc_policy alloc_;
|
||||
|
||||
void* try_expand() {
|
||||
if (empty()) {
|
||||
auto size = ExpandP::next(block_size_, init_expand_);
|
||||
auto p = node_p(cursor_ = alloc_.alloc(size));
|
||||
for (std::size_t i = 0; i < (size / block_size_) - 1; ++i)
|
||||
p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size_);
|
||||
(*p) = nullptr;
|
||||
}
|
||||
return cursor_;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit fixed_alloc(std::size_t block_size, std::size_t init_expand = 1) {
|
||||
init(block_size, init_expand);
|
||||
}
|
||||
|
||||
fixed_alloc(fixed_alloc && rhs) {
|
||||
init(0, 0);
|
||||
swap(rhs);
|
||||
}
|
||||
|
||||
fixed_alloc& operator=(fixed_alloc rhs) {
|
||||
swap(rhs);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
void swap(fixed_alloc& rhs) {
|
||||
alloc_.swap(rhs.alloc_);
|
||||
base_t::swap(rhs);
|
||||
}
|
||||
|
||||
template <typename A = AllocP>
|
||||
auto take(fixed_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||
base_t::take(std::move(rhs));
|
||||
alloc_.take(std::move(rhs.alloc_));
|
||||
}
|
||||
|
||||
void* alloc() {
|
||||
void* p = try_expand();
|
||||
cursor_ = next(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void* alloc(std::size_t) {
|
||||
return alloc();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <std::size_t BaseSize = sizeof(void*) * 1024,
|
||||
std::size_t LimitSize = (std::numeric_limits<std::uint32_t>::max)()>
|
||||
struct fixed_expand_policy {
|
||||
|
||||
enum : std::size_t {
|
||||
base_size = BaseSize,
|
||||
limit_size = LimitSize
|
||||
};
|
||||
|
||||
constexpr static std::size_t prev(std::size_t e) noexcept {
|
||||
return ((e / 2) == 0) ? 1 : (e / 2);
|
||||
}
|
||||
|
||||
constexpr static std::size_t next(std::size_t e) noexcept {
|
||||
return e * 2;
|
||||
}
|
||||
|
||||
static std::size_t next(std::size_t block_size, std::size_t & e) {
|
||||
auto n = ipc::detail::max<std::size_t>(block_size, base_size) * e;
|
||||
e = ipc::detail::min<std::size_t>(limit_size, next(e));
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t BlockSize,
|
||||
typename AllocP = scope_alloc<>,
|
||||
typename ExpandP = fixed_expand_policy<>>
|
||||
class fixed_alloc : public detail::fixed_alloc<AllocP, ExpandP> {
|
||||
public:
|
||||
using base_t = detail::fixed_alloc<AllocP, ExpandP>;
|
||||
|
||||
enum : std::size_t {
|
||||
block_size = ipc::detail::max<std::size_t>(BlockSize, sizeof(void*))
|
||||
};
|
||||
|
||||
public:
|
||||
explicit fixed_alloc(std::size_t init_expand)
|
||||
: base_t(block_size, init_expand) {
|
||||
}
|
||||
|
||||
fixed_alloc() : fixed_alloc(1) {}
|
||||
|
||||
fixed_alloc(fixed_alloc && rhs)
|
||||
: base_t(std::move(rhs)) {
|
||||
}
|
||||
|
||||
fixed_alloc& operator=(fixed_alloc rhs) {
|
||||
swap(rhs);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
void swap(fixed_alloc& rhs) {
|
||||
base_t::swap(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Variable-size blocks allocation (without alignment)
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
class variable_alloc_base {
|
||||
protected:
|
||||
byte_t * head_ = nullptr, * tail_ = nullptr;
|
||||
|
||||
public:
|
||||
void swap(variable_alloc_base & rhs) {
|
||||
std::swap(head_, rhs.head_);
|
||||
std::swap(tail_, rhs.tail_);
|
||||
}
|
||||
|
||||
std::size_t remain() const noexcept {
|
||||
return static_cast<std::size_t>(tail_ - head_);
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return remain() == 0;
|
||||
}
|
||||
|
||||
void take(variable_alloc_base && rhs) {
|
||||
if (remain() < rhs.remain()) {
|
||||
// replace this by rhs
|
||||
head_ = rhs.head_;
|
||||
tail_ = rhs.tail_;
|
||||
}
|
||||
// discard rhs
|
||||
rhs.head_ = rhs.tail_ = nullptr;
|
||||
}
|
||||
|
||||
void free(void* /*p*/) {}
|
||||
void free(void* /*p*/, std::size_t) {}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <std::size_t ChunkSize = (sizeof(void*) * 1024), typename AllocP = scope_alloc<>>
|
||||
class variable_alloc : public detail::variable_alloc_base {
|
||||
public:
|
||||
using base_t = detail::variable_alloc_base;
|
||||
using alloc_policy = AllocP;
|
||||
|
||||
enum : std::size_t {
|
||||
aligned_chunk_size = detail::aligned(ChunkSize, alignof(std::max_align_t))
|
||||
};
|
||||
|
||||
private:
|
||||
alloc_policy alloc_;
|
||||
|
||||
public:
|
||||
variable_alloc() = default;
|
||||
|
||||
variable_alloc(variable_alloc && rhs) { swap(rhs); }
|
||||
variable_alloc& operator=(variable_alloc rhs) { swap(rhs); return (*this); }
|
||||
|
||||
void swap(variable_alloc& rhs) {
|
||||
alloc_.swap(rhs.alloc_);
|
||||
base_t::swap(rhs);
|
||||
}
|
||||
|
||||
template <typename A = AllocP>
|
||||
auto take(variable_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||
base_t::take(std::move(rhs));
|
||||
alloc_.take(std::move(rhs.alloc_));
|
||||
}
|
||||
|
||||
void* alloc(std::size_t size) {
|
||||
/*
|
||||
* byte alignment is always alignof(std::max_align_t).
|
||||
*/
|
||||
size = detail::aligned(size, alignof(std::max_align_t));
|
||||
void* ptr;
|
||||
// size would never be 0 here
|
||||
if (remain() < size) {
|
||||
std::size_t chunk_size = ipc::detail::max<std::size_t>(aligned_chunk_size, size);
|
||||
ptr = alloc_.alloc(chunk_size);
|
||||
tail_ = static_cast<byte_t*>(ptr) + chunk_size;
|
||||
head_ = tail_ - (chunk_size - size);
|
||||
}
|
||||
else {
|
||||
ptr = head_;
|
||||
head_ += size;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mem
|
||||
} // namespace ipc
|
121
human_render/Ipc/libipc/memory/allocator_wrapper.h
Normal file
121
human_render/Ipc/libipc/memory/allocator_wrapper.h
Normal file
@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <utility> // std::forward
|
||||
#include <cstddef>
|
||||
|
||||
#include "libipc/pool_alloc.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace mem {
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// The allocator wrapper class for STL
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename AllocP>
|
||||
struct rebind {
|
||||
template <typename U>
|
||||
using alloc_t = AllocP;
|
||||
};
|
||||
|
||||
template <typename T, template <typename> class AllocT>
|
||||
struct rebind<T, AllocT<T>> {
|
||||
template <typename U>
|
||||
using alloc_t = AllocT<U>;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename AllocP>
|
||||
class allocator_wrapper {
|
||||
|
||||
template <typename U, typename AllocU>
|
||||
friend class allocator_wrapper;
|
||||
|
||||
public:
|
||||
// type definitions
|
||||
typedef T value_type;
|
||||
typedef value_type* pointer;
|
||||
typedef const value_type* const_pointer;
|
||||
typedef value_type& reference;
|
||||
typedef const value_type& const_reference;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef AllocP alloc_policy;
|
||||
|
||||
private:
|
||||
alloc_policy alloc_;
|
||||
|
||||
public:
|
||||
allocator_wrapper() noexcept {}
|
||||
|
||||
// construct by copying (do nothing)
|
||||
allocator_wrapper (const allocator_wrapper<T, AllocP>&) noexcept {}
|
||||
allocator_wrapper& operator=(const allocator_wrapper<T, AllocP>&) noexcept { return *this; }
|
||||
|
||||
// construct from a related allocator (do nothing)
|
||||
template <typename U, typename AllocU> allocator_wrapper (const allocator_wrapper<U, AllocU>&) noexcept {}
|
||||
template <typename U, typename AllocU> allocator_wrapper& operator=(const allocator_wrapper<U, AllocU>&) noexcept { return *this; }
|
||||
|
||||
allocator_wrapper (allocator_wrapper && rhs) noexcept : alloc_ ( std::move(rhs.alloc_) ) {}
|
||||
allocator_wrapper& operator=(allocator_wrapper && rhs) noexcept { alloc_ = std::move(rhs.alloc_); return *this; }
|
||||
|
||||
public:
|
||||
// the other type of std_allocator
|
||||
template <typename U>
|
||||
struct rebind {
|
||||
using other = allocator_wrapper< U, typename detail::rebind<T, AllocP>::template alloc_t<U> >;
|
||||
};
|
||||
|
||||
constexpr size_type max_size(void) const noexcept {
|
||||
return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
|
||||
}
|
||||
|
||||
public:
|
||||
pointer allocate(size_type count) noexcept {
|
||||
if (count == 0) return nullptr;
|
||||
if (count > this->max_size()) return nullptr;
|
||||
return static_cast<pointer>(alloc_.alloc(count * sizeof(value_type)));
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type count) noexcept {
|
||||
alloc_.free(p, count * sizeof(value_type));
|
||||
}
|
||||
|
||||
template <typename... P>
|
||||
static void construct(pointer p, P && ... params) {
|
||||
ipc::mem::construct(p, std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
static void destroy(pointer p) {
|
||||
ipc::mem::destruct(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <class AllocP>
|
||||
class allocator_wrapper<void, AllocP> {
|
||||
public:
|
||||
// type definitions
|
||||
typedef void value_type;
|
||||
typedef value_type* pointer;
|
||||
typedef const value_type* const_pointer;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef AllocP alloc_policy;
|
||||
};
|
||||
|
||||
template <typename T, typename U, class AllocP>
|
||||
constexpr bool operator==(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename U, class AllocP>
|
||||
constexpr bool operator!=(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace mem
|
||||
} // namespace ipc
|
110
human_render/Ipc/libipc/memory/resource.h
Normal file
110
human_render/Ipc/libipc/memory/resource.h
Normal file
@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include "libipc/def.h"
|
||||
|
||||
#include "libipc/memory/alloc.h"
|
||||
#include "libipc/memory/wrapper.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace mem {
|
||||
|
||||
//using async_pool_alloc = static_wrapper<variable_wrapper<async_wrapper<
|
||||
// detail::fixed_alloc<
|
||||
// variable_alloc <sizeof(void*) * 1024 * 256>,
|
||||
// fixed_expand_policy<sizeof(void*) * 1024, sizeof(void*) * 1024 * 256>
|
||||
// >,
|
||||
// default_recycler >>>;
|
||||
using async_pool_alloc = ipc::mem::static_alloc;
|
||||
|
||||
template <typename T>
|
||||
using allocator = allocator_wrapper<T, async_pool_alloc>;
|
||||
|
||||
} // namespace mem
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char const * pf(int) { return "%d" ; }
|
||||
constexpr char const * pf(long) { return "%ld" ; }
|
||||
constexpr char const * pf(long long) { return "%lld"; }
|
||||
constexpr char const * pf(unsigned int) { return "%u" ; }
|
||||
constexpr char const * pf(unsigned long) { return "%lu" ; }
|
||||
constexpr char const * pf(unsigned long long) { return "%llu"; }
|
||||
constexpr char const * pf(float) { return "%f" ; }
|
||||
constexpr char const * pf(double) { return "%f" ; }
|
||||
constexpr char const * pf(long double) { return "%Lf" ; }
|
||||
|
||||
} // internal-linkage
|
||||
|
||||
template <typename T>
|
||||
struct hash : public std::hash<T> {};
|
||||
|
||||
template <typename Key, typename T>
|
||||
using unordered_map = std::unordered_map<
|
||||
Key, T, ipc::hash<Key>, std::equal_to<Key>, ipc::mem::allocator<std::pair<Key const, T>>
|
||||
>;
|
||||
|
||||
template <typename Key, typename T>
|
||||
using map = std::map<
|
||||
Key, T, std::less<Key>, ipc::mem::allocator<std::pair<Key const, T>>
|
||||
>;
|
||||
|
||||
template <typename Char>
|
||||
using basic_string = std::basic_string<
|
||||
Char, std::char_traits<Char>, ipc::mem::allocator<Char>
|
||||
>;
|
||||
|
||||
using string = basic_string<char>;
|
||||
using wstring = basic_string<wchar_t>;
|
||||
|
||||
template <> struct hash<string> {
|
||||
std::size_t operator()(string const &val) const noexcept {
|
||||
return std::hash<char const *>{}(val.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct hash<wstring> {
|
||||
std::size_t operator()(wstring const &val) const noexcept {
|
||||
return std::hash<wchar_t const *>{}(val.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
ipc::string to_string(T val) {
|
||||
char buf[std::numeric_limits<T>::digits10 + 1] {};
|
||||
if (std::snprintf(buf, sizeof(buf), pf(val), val) > 0) {
|
||||
return buf;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/// \brief Check string validity.
|
||||
constexpr bool is_valid_string(char const *str) noexcept {
|
||||
return (str != nullptr) && (str[0] != '\0');
|
||||
}
|
||||
|
||||
/// \brief Make a valid string.
|
||||
inline ipc::string make_string(char const *str) {
|
||||
return is_valid_string(str) ? ipc::string{str} : ipc::string{};
|
||||
}
|
||||
|
||||
/// \brief Combine prefix from a list of strings.
|
||||
inline ipc::string make_prefix(ipc::string prefix, std::initializer_list<ipc::string> args) {
|
||||
prefix += "__IPC_SHM__";
|
||||
for (auto const &txt: args) {
|
||||
if (txt.empty()) continue;
|
||||
prefix += txt;
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
} // namespace ipc
|
327
human_render/Ipc/libipc/memory/wrapper.h
Normal file
327
human_render/Ipc/libipc/memory/wrapper.h
Normal file
@ -0,0 +1,327 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <thread>
|
||||
#include <deque> // std::deque
|
||||
#include <functional> // std::function
|
||||
#include <utility> // std::forward
|
||||
#include <cstddef>
|
||||
#include <cassert> // assert
|
||||
#include <type_traits> // std::aligned_storage_t
|
||||
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/rw_lock.h"
|
||||
#include "libipc/pool_alloc.h"
|
||||
|
||||
#include "libipc/utility/concept.h"
|
||||
#include "libipc/memory/alloc.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace mem {
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Thread-safe allocation wrapper
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
IPC_CONCEPT_(is_comparable, operator<(std::declval<Type>()));
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename AllocP, bool = detail::is_comparable<AllocP>::value>
|
||||
class limited_recycler;
|
||||
|
||||
template <typename AllocP>
|
||||
class limited_recycler<AllocP, true> {
|
||||
public:
|
||||
using alloc_policy = AllocP;
|
||||
|
||||
protected:
|
||||
std::deque<alloc_policy> master_allocs_;
|
||||
ipc::spin_lock master_lock_;
|
||||
|
||||
template <typename F>
|
||||
void take_first_do(F && pred) {
|
||||
auto it = master_allocs_.begin();
|
||||
pred(const_cast<alloc_policy&>(*it));
|
||||
master_allocs_.erase(it);
|
||||
}
|
||||
|
||||
public:
|
||||
void try_recover(alloc_policy & alc) {
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||
if (master_allocs_.empty()) return;
|
||||
take_first_do([&alc](alloc_policy & first) { alc.swap(first); });
|
||||
}
|
||||
|
||||
void collect(alloc_policy && alc) {
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||
if (master_allocs_.size() >= 32) {
|
||||
take_first_do([](alloc_policy &) {}); // erase first
|
||||
}
|
||||
master_allocs_.emplace_back(std::move(alc));
|
||||
}
|
||||
|
||||
IPC_CONSTEXPR_ auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
||||
};
|
||||
|
||||
template <typename AllocP>
|
||||
class default_recycler : public limited_recycler<AllocP> {
|
||||
|
||||
IPC_CONCEPT_(has_remain, remain());
|
||||
IPC_CONCEPT_(has_empty , empty());
|
||||
|
||||
template <typename A>
|
||||
void try_fill(A & alc) {
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(this->master_lock_);
|
||||
if (this->master_allocs_.empty()) return;
|
||||
this->take_first_do([&alc](alloc_policy & first) { alc.take(std::move(first)); });
|
||||
}
|
||||
|
||||
public:
|
||||
using alloc_policy = typename limited_recycler<AllocP>::alloc_policy;
|
||||
|
||||
template <typename A = AllocP>
|
||||
auto try_replenish(alloc_policy & alc, std::size_t size)
|
||||
-> ipc::require<detail::has_take<A>::value && has_remain<A>::value> {
|
||||
if (alc.remain() >= size) return;
|
||||
this->try_fill(alc);
|
||||
}
|
||||
|
||||
template <typename A = AllocP>
|
||||
auto try_replenish(alloc_policy & alc, std::size_t /*size*/)
|
||||
-> ipc::require<detail::has_take<A>::value && !has_remain<A>::value && has_empty<A>::value> {
|
||||
if (!alc.empty()) return;
|
||||
this->try_fill(alc);
|
||||
}
|
||||
|
||||
template <typename A = AllocP>
|
||||
auto try_replenish(alloc_policy & alc, std::size_t /*size*/)
|
||||
-> ipc::require<!detail::has_take<A>::value && has_empty<A>::value> {
|
||||
if (!alc.empty()) return;
|
||||
this->try_recover(alc);
|
||||
}
|
||||
|
||||
template <typename A = AllocP>
|
||||
IPC_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> {
|
||||
// Do Nothing.
|
||||
}
|
||||
};
|
||||
|
||||
template <typename AllocP>
|
||||
class empty_recycler {
|
||||
public:
|
||||
using alloc_policy = AllocP;
|
||||
|
||||
IPC_CONSTEXPR_ void try_recover(alloc_policy&) noexcept {}
|
||||
IPC_CONSTEXPR_ auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
||||
IPC_CONSTEXPR_ void collect(alloc_policy&&) noexcept {}
|
||||
};
|
||||
|
||||
template <typename AllocP,
|
||||
template <typename> class RecyclerP = default_recycler>
|
||||
class async_wrapper {
|
||||
public:
|
||||
using alloc_policy = AllocP;
|
||||
|
||||
private:
|
||||
RecyclerP<alloc_policy> recycler_;
|
||||
|
||||
class alloc_proxy : public AllocP {
|
||||
async_wrapper * w_ = nullptr;
|
||||
|
||||
public:
|
||||
alloc_proxy(alloc_proxy && rhs) = default;
|
||||
|
||||
template <typename ... P>
|
||||
alloc_proxy(async_wrapper* w, P && ... pars)
|
||||
: AllocP(std::forward<P>(pars) ...), w_(w) {
|
||||
assert(w_ != nullptr);
|
||||
w_->recycler_.try_recover(*this);
|
||||
}
|
||||
|
||||
~alloc_proxy() {
|
||||
w_->recycler_.collect(std::move(*this));
|
||||
}
|
||||
|
||||
auto alloc(std::size_t size) {
|
||||
w_->recycler_.try_replenish(*this, size);
|
||||
return AllocP::alloc(size);
|
||||
}
|
||||
};
|
||||
|
||||
friend class alloc_proxy;
|
||||
using ref_t = alloc_proxy&;
|
||||
|
||||
std::function<ref_t()> get_alloc_;
|
||||
|
||||
public:
|
||||
template <typename ... P>
|
||||
async_wrapper(P ... pars) {
|
||||
get_alloc_ = [this, pars ...]()->ref_t {
|
||||
thread_local alloc_proxy tls(pars ...);
|
||||
return tls;
|
||||
};
|
||||
}
|
||||
|
||||
void* alloc(std::size_t size) {
|
||||
return get_alloc_().alloc(size);
|
||||
}
|
||||
|
||||
void free(void* p, std::size_t size) {
|
||||
get_alloc_().free(p, size);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Thread-safe allocation wrapper (with spin_lock)
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename AllocP, typename MutexT = ipc::spin_lock>
|
||||
class sync_wrapper {
|
||||
public:
|
||||
using alloc_policy = AllocP;
|
||||
using mutex_type = MutexT;
|
||||
|
||||
private:
|
||||
mutex_type lock_;
|
||||
alloc_policy alloc_;
|
||||
|
||||
public:
|
||||
template <typename ... P>
|
||||
sync_wrapper(P && ... pars)
|
||||
: alloc_(std::forward<P>(pars) ...)
|
||||
{}
|
||||
|
||||
void swap(sync_wrapper& rhs) {
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||
alloc_.swap(rhs.alloc_);
|
||||
}
|
||||
|
||||
void* alloc(std::size_t size) {
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||
return alloc_.alloc(size);
|
||||
}
|
||||
|
||||
void free(void* p, std::size_t size) {
|
||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||
alloc_.free(p, size);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Variable memory allocation wrapper
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
template <std::size_t BaseSize = 0, std::size_t IterSize = sizeof(void*)>
|
||||
struct default_mapping_policy {
|
||||
|
||||
enum : std::size_t {
|
||||
base_size = BaseSize,
|
||||
iter_size = IterSize,
|
||||
classes_size = 64
|
||||
};
|
||||
|
||||
template <typename F, typename ... P>
|
||||
IPC_CONSTEXPR_ static void foreach(F && f, P && ... params) {
|
||||
for (std::size_t i = 0; i < classes_size; ++i) {
|
||||
f(i, std::forward<P>(params)...);
|
||||
}
|
||||
}
|
||||
|
||||
IPC_CONSTEXPR_ static std::size_t block_size(std::size_t id) noexcept {
|
||||
return (id < classes_size) ? (base_size + (id + 1) * iter_size) : 0;
|
||||
}
|
||||
|
||||
template <typename F, typename D, typename ... P>
|
||||
IPC_CONSTEXPR_ static auto classify(F && f, D && d, std::size_t size, P && ... params) {
|
||||
std::size_t id = (size - base_size - 1) / iter_size;
|
||||
return (id < classes_size) ?
|
||||
f(id, size, std::forward<P>(params)...) :
|
||||
d(size, std::forward<P>(params)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename FixedAlloc,
|
||||
typename DefaultAlloc = mem::static_alloc,
|
||||
typename MappingP = default_mapping_policy<>>
|
||||
class variable_wrapper {
|
||||
|
||||
struct initiator {
|
||||
|
||||
using falc_t = std::aligned_storage_t<sizeof(FixedAlloc), alignof(FixedAlloc)>;
|
||||
falc_t arr_[MappingP::classes_size];
|
||||
|
||||
initiator() {
|
||||
MappingP::foreach([](std::size_t id, falc_t * a) {
|
||||
ipc::mem::construct(&initiator::at(a, id), MappingP::block_size(id));
|
||||
}, arr_);
|
||||
}
|
||||
|
||||
~initiator() {
|
||||
MappingP::foreach([](std::size_t id, falc_t * a) {
|
||||
ipc::mem::destruct(&initiator::at(a, id));
|
||||
}, arr_);
|
||||
}
|
||||
|
||||
static FixedAlloc & at(falc_t * arr, std::size_t id) noexcept {
|
||||
return reinterpret_cast<FixedAlloc&>(arr[id]);
|
||||
}
|
||||
} init_;
|
||||
|
||||
using falc_t = typename initiator::falc_t;
|
||||
|
||||
public:
|
||||
void swap(variable_wrapper & other) {
|
||||
MappingP::foreach([](std::size_t id, falc_t * in, falc_t * ot) {
|
||||
initiator::at(in, id).swap(initiator::at(ot, id));
|
||||
}, init_.arr_, other.init_.arr_);
|
||||
}
|
||||
|
||||
void* alloc(std::size_t size) {
|
||||
return MappingP::classify([](std::size_t id, std::size_t size, falc_t * a) {
|
||||
return initiator::at(a, id).alloc(size);
|
||||
}, [](std::size_t size, falc_t *) {
|
||||
return DefaultAlloc::alloc(size);
|
||||
}, size, init_.arr_);
|
||||
}
|
||||
|
||||
void free(void* p, std::size_t size) {
|
||||
MappingP::classify([](std::size_t id, std::size_t size, void* p, falc_t * a) {
|
||||
initiator::at(a, id).free(p, size);
|
||||
}, [](std::size_t size, void* p, falc_t *) {
|
||||
DefaultAlloc::free(p, size);
|
||||
}, size, p, init_.arr_);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Static allocation wrapper
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename AllocP>
|
||||
class static_wrapper {
|
||||
public:
|
||||
using alloc_policy = AllocP;
|
||||
|
||||
static alloc_policy& instance() {
|
||||
static alloc_policy alloc;
|
||||
return alloc;
|
||||
}
|
||||
|
||||
static void swap(static_wrapper&) {}
|
||||
|
||||
static void* alloc(std::size_t size) {
|
||||
return instance().alloc(size);
|
||||
}
|
||||
|
||||
static void free(void* p, std::size_t size) {
|
||||
instance().free(p, size);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mem
|
||||
} // namespace ipc
|
38
human_render/Ipc/libipc/mutex.h
Normal file
38
human_render/Ipc/libipc/mutex.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // std::uint64_t
|
||||
#include <system_error>
|
||||
|
||||
#include "libipc/def.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace sync {
|
||||
|
||||
class mutex {
|
||||
mutex(mutex const &) = delete;
|
||||
mutex &operator=(mutex const &) = delete;
|
||||
|
||||
public:
|
||||
mutex();
|
||||
explicit mutex(char const *name);
|
||||
~mutex();
|
||||
|
||||
void const *native() const noexcept;
|
||||
void *native() noexcept;
|
||||
|
||||
bool valid() const noexcept;
|
||||
|
||||
bool open(char const *name) noexcept;
|
||||
void close() noexcept;
|
||||
|
||||
bool lock(std::uint64_t tm = ipc::invalid_value) noexcept;
|
||||
bool try_lock() noexcept(false); // std::system_error
|
||||
bool unlock() noexcept;
|
||||
|
||||
private:
|
||||
class mutex_;
|
||||
mutex_* p_;
|
||||
};
|
||||
|
||||
} // namespace sync
|
||||
} // namespace ipc
|
136
human_render/Ipc/libipc/platform/detail.h
Normal file
136
human_render/Ipc/libipc/platform/detail.h
Normal file
@ -0,0 +1,136 @@
|
||||
#ifndef LIBIPC_SRC_PLATFORM_DETAIL_H_
|
||||
#define LIBIPC_SRC_PLATFORM_DETAIL_H_
|
||||
|
||||
// detect platform
|
||||
|
||||
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \
|
||||
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
|
||||
defined(WINCE) || defined(_WIN32_WCE)
|
||||
# define IPC_OS_WINDOWS_
|
||||
#elif defined(__linux__) || defined(__linux)
|
||||
# define IPC_OS_LINUX_
|
||||
#elif defined(__QNX__)
|
||||
# define IPC_OS_QNX_
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__ANDROID__)
|
||||
// TBD
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <type_traits>
|
||||
#include <tuple>
|
||||
#include <algorithm>
|
||||
|
||||
// pre-defined
|
||||
|
||||
#ifdef IPC_UNUSED_
|
||||
# error "IPC_UNUSED_ has been defined."
|
||||
#endif
|
||||
#ifdef IPC_FALLTHROUGH_
|
||||
# error "IPC_FALLTHROUGH_ has been defined."
|
||||
#endif
|
||||
#ifdef IPC_STBIND_
|
||||
# error "IPC_STBIND_ has been defined."
|
||||
#endif
|
||||
#ifdef IPC_CONSTEXPR_
|
||||
# error "IPC_CONSTEXPR_ has been defined."
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
|
||||
#define IPC_UNUSED_ [[maybe_unused]]
|
||||
#define IPC_FALLTHROUGH_ [[fallthrough]]
|
||||
#define IPC_STBIND_(A, B, ...) auto [A, B] = __VA_ARGS__
|
||||
#define IPC_CONSTEXPR_ constexpr
|
||||
|
||||
#else /*__cplusplus < 201703L*/
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define IPC_UNUSED_ __pragma(warning(suppress: 4100 4101 4189))
|
||||
#elif defined(__GNUC__)
|
||||
# define IPC_UNUSED_ __attribute__((__unused__))
|
||||
#else
|
||||
# define IPC_UNUSED_
|
||||
#endif
|
||||
|
||||
#define IPC_FALLTHROUGH_
|
||||
|
||||
#define IPC_STBIND_(A, B, ...) \
|
||||
auto tp___ = __VA_ARGS__ \
|
||||
auto A = std::get<0>(tp___); \
|
||||
auto B = std::get<1>(tp___)
|
||||
|
||||
#define IPC_CONSTEXPR_ inline
|
||||
|
||||
#endif/*__cplusplus < 201703L*/
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
|
||||
namespace std {
|
||||
|
||||
// deduction guides for std::unique_ptr
|
||||
template <typename T>
|
||||
unique_ptr(T* p) -> unique_ptr<T>;
|
||||
template <typename T, typename D>
|
||||
unique_ptr(T* p, D&& d) -> unique_ptr<T, std::decay_t<D>>;
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
|
||||
using std::unique_ptr;
|
||||
using std::unique_lock;
|
||||
using std::shared_lock;
|
||||
using std::max;
|
||||
using std::min;
|
||||
|
||||
#else /*__cplusplus < 201703L*/
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
|
||||
// deduction guides for std::unique_ptr
|
||||
template <typename T>
|
||||
constexpr auto unique_ptr(T* p) noexcept {
|
||||
return std::unique_ptr<T> { p };
|
||||
}
|
||||
|
||||
template <typename T, typename D>
|
||||
constexpr auto unique_ptr(T* p, D&& d) noexcept {
|
||||
return std::unique_ptr<T, std::decay_t<D>> { p, std::forward<D>(d) };
|
||||
}
|
||||
|
||||
// deduction guides for std::unique_lock
|
||||
template <typename T>
|
||||
constexpr auto unique_lock(T&& lc) noexcept {
|
||||
return std::unique_lock<std::decay_t<T>> { std::forward<T>(lc) };
|
||||
}
|
||||
|
||||
// deduction guides for std::shared_lock
|
||||
template <typename T>
|
||||
constexpr auto shared_lock(T&& lc) noexcept {
|
||||
return std::shared_lock<std::decay_t<T>> { std::forward<T>(lc) };
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr const T& (max)(const T& a, const T& b) {
|
||||
return (a < b) ? b : a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr const T& (min)(const T& a, const T& b) {
|
||||
return (b < a) ? b : a;
|
||||
}
|
||||
|
||||
#endif/*__cplusplus < 201703L*/
|
||||
|
||||
} // namespace detail
|
||||
} // namespace ipc
|
||||
|
||||
#endif // defined(__cplusplus)
|
||||
#endif // LIBIPC_SRC_PLATFORM_DETAIL_H_
|
3
human_render/Ipc/libipc/platform/platform.cpp1
Normal file
3
human_render/Ipc/libipc/platform/platform.cpp1
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
#include "libipc/platform/detail.h"
|
||||
#include "libipc/platform/win/shm_win.cpp"
|
119
human_render/Ipc/libipc/platform/win/condition.h
Normal file
119
human_render/Ipc/libipc/platform/win/condition.h
Normal file
@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "libipc/utility/log.h"
|
||||
#include "libipc/utility/scope_guard.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
#include "libipc/mutex.h"
|
||||
#include "libipc/semaphore.h"
|
||||
#include "libipc/shm.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
namespace sync {
|
||||
|
||||
class condition {
|
||||
ipc::sync::semaphore sem_;
|
||||
ipc::sync::mutex lock_;
|
||||
ipc::shm::handle shm_;
|
||||
|
||||
std::int32_t &counter() {
|
||||
return *static_cast<std::int32_t *>(shm_.get());
|
||||
}
|
||||
|
||||
public:
|
||||
condition() = default;
|
||||
~condition() noexcept = default;
|
||||
|
||||
auto native() noexcept {
|
||||
return sem_.native();
|
||||
}
|
||||
|
||||
auto native() const noexcept {
|
||||
return sem_.native();
|
||||
}
|
||||
|
||||
bool valid() const noexcept {
|
||||
return sem_.valid() && lock_.valid() && shm_.valid();
|
||||
}
|
||||
|
||||
bool open(char const *name) noexcept {
|
||||
close();
|
||||
if (!sem_.open((std::string{name} + "_COND_SEM_").c_str())) {
|
||||
return false;
|
||||
}
|
||||
auto finally_sem = ipc::guard([this] { sem_.close(); }); // close when failed
|
||||
if (!lock_.open((std::string{name} + "_COND_LOCK_").c_str())) {
|
||||
return false;
|
||||
}
|
||||
auto finally_lock = ipc::guard([this] { lock_.close(); }); // close when failed
|
||||
if (!shm_.acquire((std::string{name} + "_COND_SHM_").c_str(), sizeof(std::int32_t))) {
|
||||
return false;
|
||||
}
|
||||
finally_lock.dismiss();
|
||||
finally_sem.dismiss();
|
||||
return valid();
|
||||
}
|
||||
|
||||
void close() noexcept {
|
||||
if (!valid()) return;
|
||||
sem_.close();
|
||||
lock_.close();
|
||||
shm_.release();
|
||||
}
|
||||
|
||||
bool wait(ipc::sync::mutex &mtx, std::uint64_t tm) noexcept {
|
||||
if (!valid()) return false;
|
||||
auto &cnt = counter();
|
||||
{
|
||||
IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> guard {lock_};
|
||||
cnt = (cnt < 0) ? 1 : cnt + 1;
|
||||
}
|
||||
DWORD ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
||||
/**
|
||||
* \see
|
||||
* - https://www.microsoft.com/en-us/research/wp-content/uploads/2004/12/ImplementingCVs.pdf
|
||||
* - https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-signalobjectandwait
|
||||
*/
|
||||
bool rs = ::SignalObjectAndWait(mtx.native(), sem_.native(), ms, FALSE) == WAIT_OBJECT_0;
|
||||
bool rl = mtx.lock(); // INFINITE
|
||||
if (!rs) {
|
||||
IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> guard {lock_};
|
||||
cnt -= 1;
|
||||
}
|
||||
return rs && rl;
|
||||
}
|
||||
|
||||
bool notify(ipc::sync::mutex &) noexcept {
|
||||
if (!valid()) return false;
|
||||
auto &cnt = counter();
|
||||
if (!lock_.lock()) return false;
|
||||
bool ret = false;
|
||||
if (cnt > 0) {
|
||||
ret = sem_.post(1);
|
||||
cnt -= 1;
|
||||
}
|
||||
return lock_.unlock() && ret;
|
||||
}
|
||||
|
||||
bool broadcast(ipc::sync::mutex &) noexcept {
|
||||
if (!valid()) return false;
|
||||
auto &cnt = counter();
|
||||
if (!lock_.lock()) return false;
|
||||
bool ret = false;
|
||||
if (cnt > 0) {
|
||||
ret = sem_.post(cnt);
|
||||
cnt = 0;
|
||||
}
|
||||
return lock_.unlock() && ret;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sync
|
||||
} // namespace detail
|
||||
} // namespace ipc
|
35
human_render/Ipc/libipc/platform/win/get_sa.h
Normal file
35
human_render/Ipc/libipc/platform/win/get_sa.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <securitybaseapi.h>
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
|
||||
inline LPSECURITY_ATTRIBUTES get_sa() {
|
||||
static struct initiator {
|
||||
|
||||
SECURITY_DESCRIPTOR sd_;
|
||||
SECURITY_ATTRIBUTES sa_;
|
||||
|
||||
bool succ_ = false;
|
||||
|
||||
initiator() {
|
||||
if (!::InitializeSecurityDescriptor(&sd_, SECURITY_DESCRIPTOR_REVISION)) {
|
||||
ipc::error("fail InitializeSecurityDescriptor[%d]\n", static_cast<int>(::GetLastError()));
|
||||
return;
|
||||
}
|
||||
if (!::SetSecurityDescriptorDacl(&sd_, TRUE, NULL, FALSE)) {
|
||||
ipc::error("fail SetSecurityDescriptorDacl[%d]\n", static_cast<int>(::GetLastError()));
|
||||
return;
|
||||
}
|
||||
sa_.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
sa_.bInheritHandle = FALSE;
|
||||
sa_.lpSecurityDescriptor = &sd_;
|
||||
succ_ = true;
|
||||
}
|
||||
} handle;
|
||||
return handle.succ_ ? &handle.sa_ : nullptr;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace ipc
|
98
human_render/Ipc/libipc/platform/win/mutex.h
Normal file
98
human_render/Ipc/libipc/platform/win/mutex.h
Normal file
@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <system_error>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "libipc/utility/log.h"
|
||||
|
||||
#include "to_tchar.h"
|
||||
#include "get_sa.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
namespace sync {
|
||||
|
||||
class mutex {
|
||||
HANDLE h_ = NULL;
|
||||
|
||||
public:
|
||||
mutex() noexcept = default;
|
||||
~mutex() noexcept = default;
|
||||
|
||||
static void init() {}
|
||||
|
||||
HANDLE native() const noexcept {
|
||||
return h_;
|
||||
}
|
||||
|
||||
bool valid() const noexcept {
|
||||
return h_ != NULL;
|
||||
}
|
||||
|
||||
bool open(char const *name) noexcept {
|
||||
close();
|
||||
h_ = ::CreateMutex(detail::get_sa(), FALSE, detail::to_tchar(name).c_str());
|
||||
if (h_ == NULL) {
|
||||
ipc::error("fail CreateMutex[%lu]: %s\n", ::GetLastError(), name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void close() noexcept {
|
||||
if (!valid()) return;
|
||||
::CloseHandle(h_);
|
||||
h_ = NULL;
|
||||
}
|
||||
|
||||
bool lock(std::uint64_t tm) noexcept {
|
||||
DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
||||
for(;;) {
|
||||
switch ((ret = ::WaitForSingleObject(h_, ms))) {
|
||||
case WAIT_OBJECT_0:
|
||||
return true;
|
||||
case WAIT_TIMEOUT:
|
||||
return false;
|
||||
case WAIT_ABANDONED:
|
||||
ipc::log("fail WaitForSingleObject[%lu]: WAIT_ABANDONED, try again.\n", ::GetLastError());
|
||||
if (!unlock()) {
|
||||
return false;
|
||||
}
|
||||
break; // loop again
|
||||
default:
|
||||
ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock() noexcept(false) {
|
||||
DWORD ret = ::WaitForSingleObject(h_, 0);
|
||||
switch (ret) {
|
||||
case WAIT_OBJECT_0:
|
||||
return true;
|
||||
case WAIT_TIMEOUT:
|
||||
return false;
|
||||
case WAIT_ABANDONED:
|
||||
unlock();
|
||||
IPC_FALLTHROUGH_;
|
||||
default:
|
||||
ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret);
|
||||
throw std::system_error{static_cast<int>(ret), std::system_category()};
|
||||
}
|
||||
}
|
||||
|
||||
bool unlock() noexcept {
|
||||
if (!::ReleaseMutex(h_)) {
|
||||
ipc::error("fail ReleaseMutex[%lu]\n", ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sync
|
||||
} // namespace detail
|
||||
} // namespace ipc
|
74
human_render/Ipc/libipc/platform/win/semaphore.h
Normal file
74
human_render/Ipc/libipc/platform/win/semaphore.h
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "libipc/utility/log.h"
|
||||
|
||||
#include "to_tchar.h"
|
||||
#include "get_sa.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
namespace sync {
|
||||
|
||||
class semaphore {
|
||||
HANDLE h_ = NULL;
|
||||
|
||||
public:
|
||||
semaphore() noexcept = default;
|
||||
~semaphore() noexcept = default;
|
||||
|
||||
HANDLE native() const noexcept {
|
||||
return h_;
|
||||
}
|
||||
|
||||
bool valid() const noexcept {
|
||||
return h_ != NULL;
|
||||
}
|
||||
|
||||
bool open(char const *name, std::uint32_t count) noexcept {
|
||||
close();
|
||||
h_ = ::CreateSemaphore(detail::get_sa(),
|
||||
static_cast<LONG>(count), LONG_MAX,
|
||||
detail::to_tchar(name).c_str());
|
||||
if (h_ == NULL) {
|
||||
ipc::error("fail CreateSemaphore[%lu]: %s\n", ::GetLastError(), name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void close() noexcept {
|
||||
if (!valid()) return;
|
||||
::CloseHandle(h_);
|
||||
h_ = NULL;
|
||||
}
|
||||
|
||||
bool wait(std::uint64_t tm) noexcept {
|
||||
DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
||||
switch ((ret = ::WaitForSingleObject(h_, ms))) {
|
||||
case WAIT_OBJECT_0:
|
||||
return true;
|
||||
case WAIT_TIMEOUT:
|
||||
return false;
|
||||
case WAIT_ABANDONED:
|
||||
default:
|
||||
ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool post(std::uint32_t count) noexcept {
|
||||
if (!::ReleaseSemaphore(h_, static_cast<LONG>(count), NULL)) {
|
||||
ipc::error("fail ReleaseSemaphore[%lu]\n", ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sync
|
||||
} // namespace detail
|
||||
} // namespace ipc
|
140
human_render/Ipc/libipc/platform/win/shm_win.cpp
Normal file
140
human_render/Ipc/libipc/platform/win/shm_win.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "libipc/shm.h"
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/pool_alloc.h"
|
||||
|
||||
#include "libipc/utility/log.h"
|
||||
#include "libipc/memory/resource.h"
|
||||
|
||||
#include "to_tchar.h"
|
||||
#include "get_sa.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct id_info_t {
|
||||
HANDLE h_ = NULL;
|
||||
void* mem_ = nullptr;
|
||||
std::size_t size_ = 0;
|
||||
};
|
||||
|
||||
} // internal-linkage
|
||||
|
||||
namespace ipc {
|
||||
namespace shm {
|
||||
|
||||
id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
||||
if (!is_valid_string(name)) {
|
||||
ipc::error("fail acquire: name is empty\n");
|
||||
return nullptr;
|
||||
}
|
||||
HANDLE h;
|
||||
auto fmt_name = ipc::detail::to_tchar(name);
|
||||
// Opens a named file mapping object.
|
||||
if (mode == open) {
|
||||
h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, fmt_name.c_str());
|
||||
if (h == NULL) {
|
||||
ipc::error("fail OpenFileMapping[%d]: %s\n", static_cast<int>(::GetLastError()), name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
// Creates or opens a named file mapping object for a specified file.
|
||||
else {
|
||||
h = ::CreateFileMapping(INVALID_HANDLE_VALUE, detail::get_sa(), PAGE_READWRITE | SEC_COMMIT,
|
||||
0, static_cast<DWORD>(size), fmt_name.c_str());
|
||||
DWORD err = ::GetLastError();
|
||||
// If the object exists before the function call, the function returns a handle to the existing object
|
||||
// (with its current size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS.
|
||||
if ((mode == create) && (err == ERROR_ALREADY_EXISTS)) {
|
||||
if (h != NULL) ::CloseHandle(h);
|
||||
h = NULL;
|
||||
}
|
||||
if (h == NULL) {
|
||||
ipc::error("fail CreateFileMapping[%d]: %s\n", static_cast<int>(err), name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
auto ii = mem::alloc<id_info_t>();
|
||||
ii->h_ = h;
|
||||
ii->size_ = size;
|
||||
return ii;
|
||||
}
|
||||
|
||||
std::int32_t get_ref(id_t) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sub_ref(id_t) {
|
||||
// Do Nothing.
|
||||
}
|
||||
|
||||
void * get_mem(id_t id, std::size_t * size) {
|
||||
if (id == nullptr) {
|
||||
ipc::error("fail get_mem: invalid id (null)\n");
|
||||
return nullptr;
|
||||
}
|
||||
auto ii = static_cast<id_info_t*>(id);
|
||||
if (ii->mem_ != nullptr) {
|
||||
if (size != nullptr) *size = ii->size_;
|
||||
return ii->mem_;
|
||||
}
|
||||
if (ii->h_ == NULL) {
|
||||
ipc::error("fail to_mem: invalid id (h = null)\n");
|
||||
return nullptr;
|
||||
}
|
||||
LPVOID mem = ::MapViewOfFile(ii->h_, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
if (mem == NULL) {
|
||||
ipc::error("fail MapViewOfFile[%d]\n", static_cast<int>(::GetLastError()));
|
||||
return nullptr;
|
||||
}
|
||||
MEMORY_BASIC_INFORMATION mem_info;
|
||||
if (::VirtualQuery(mem, &mem_info, sizeof(mem_info)) == 0) {
|
||||
ipc::error("fail VirtualQuery[%d]\n", static_cast<int>(::GetLastError()));
|
||||
return nullptr;
|
||||
}
|
||||
ii->mem_ = mem;
|
||||
ii->size_ = static_cast<std::size_t>(mem_info.RegionSize);
|
||||
if (size != nullptr) *size = ii->size_;
|
||||
return static_cast<void *>(mem);
|
||||
}
|
||||
|
||||
std::int32_t release(id_t id) {
|
||||
if (id == nullptr) {
|
||||
ipc::error("fail release: invalid id (null)\n");
|
||||
return -1;
|
||||
}
|
||||
auto ii = static_cast<id_info_t*>(id);
|
||||
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
||||
ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_);
|
||||
}
|
||||
else ::UnmapViewOfFile(static_cast<LPCVOID>(ii->mem_));
|
||||
if (ii->h_ == NULL) {
|
||||
ipc::error("fail release: invalid id (h = null)\n");
|
||||
}
|
||||
else ::CloseHandle(ii->h_);
|
||||
mem::free(ii);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void remove(id_t id) {
|
||||
if (id == nullptr) {
|
||||
ipc::error("fail release: invalid id (null)\n");
|
||||
return;
|
||||
}
|
||||
release(id);
|
||||
}
|
||||
|
||||
void remove(char const * name) {
|
||||
if (!is_valid_string(name)) {
|
||||
ipc::error("fail remove: name is empty\n");
|
||||
return;
|
||||
}
|
||||
// Do Nothing.
|
||||
}
|
||||
|
||||
} // namespace shm
|
||||
} // namespace ipc
|
74
human_render/Ipc/libipc/platform/win/to_tchar.h
Normal file
74
human_render/Ipc/libipc/platform/win/to_tchar.h
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <cstddef>
|
||||
|
||||
#include "libipc/utility/concept.h"
|
||||
#include "libipc/memory/resource.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
|
||||
struct has_value_type_ {
|
||||
template <typename T> static std::true_type check(typename T::value_type *);
|
||||
template <typename T> static std::false_type check(...);
|
||||
};
|
||||
|
||||
template <typename T, typename U, typename = decltype(has_value_type_::check<U>(nullptr))>
|
||||
struct is_same_char : std::is_same<T, U> {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_same_char<T, U, std::true_type> : std::is_same<T, typename U::value_type> {};
|
||||
|
||||
template <typename T, typename S, typename R = S>
|
||||
using IsSameChar = ipc::require<is_same_char<T, S>::value, R>;
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// to_tchar implementation
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T = TCHAR>
|
||||
constexpr auto to_tchar(ipc::string &&str) -> IsSameChar<T, ipc::string, ipc::string &&> {
|
||||
return std::move(str); // noconv
|
||||
}
|
||||
|
||||
/**
|
||||
* \remarks codecvt_utf8_utf16/std::wstring_convert is deprecated
|
||||
* \see https://codingtidbit.com/2020/02/09/c17-codecvt_utf8-is-deprecated/
|
||||
* https://stackoverflow.com/questions/42946335/deprecated-header-codecvt-replacement
|
||||
* https://en.cppreference.com/w/cpp/locale/codecvt/in
|
||||
* https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
|
||||
*/
|
||||
template <typename T = TCHAR>
|
||||
auto to_tchar(ipc::string &&external) -> IsSameChar<T, ipc::wstring> {
|
||||
if (external.empty()) {
|
||||
return {}; // noconv
|
||||
}
|
||||
/**
|
||||
* CP_ACP : The system default Windows ANSI code page.
|
||||
* CP_MACCP : The current system Macintosh code page.
|
||||
* CP_OEMCP : The current system OEM code page.
|
||||
* CP_SYMBOL : Symbol code page (42).
|
||||
* CP_THREAD_ACP: The Windows ANSI code page for the current thread.
|
||||
* CP_UTF7 : UTF-7. Use this value only when forced by a 7-bit transport mechanism. Use of UTF-8 is preferred.
|
||||
* CP_UTF8 : UTF-8.
|
||||
*/
|
||||
int size_needed = ::MultiByteToWideChar(CP_UTF8, 0, &external[0], (int)external.size(), NULL, 0);
|
||||
if (size_needed <= 0) {
|
||||
return {};
|
||||
}
|
||||
ipc::wstring internal(size_needed, L'\0');
|
||||
::MultiByteToWideChar(CP_UTF8, 0, &external[0], (int)external.size(), &internal[0], size_needed);
|
||||
return internal;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace ipc
|
25
human_render/Ipc/libipc/policy.h
Normal file
25
human_render/Ipc/libipc/policy.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/prod_cons.h"
|
||||
|
||||
#include "libipc/circ/elem_array.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace policy {
|
||||
|
||||
template <template <typename, std::size_t...> class Elems, typename Flag>
|
||||
struct choose;
|
||||
|
||||
template <typename Flag>
|
||||
struct choose<circ::elem_array, Flag> {
|
||||
using flag_t = Flag;
|
||||
|
||||
template <std::size_t DataSize, std::size_t AlignSize>
|
||||
using elems_t = circ::elem_array<ipc::prod_cons_impl<flag_t>, DataSize, AlignSize>;
|
||||
};
|
||||
|
||||
} // namespace policy
|
||||
} // namespace ipc
|
17
human_render/Ipc/libipc/pool_alloc.cpp
Normal file
17
human_render/Ipc/libipc/pool_alloc.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "libipc/pool_alloc.h"
|
||||
|
||||
#include "libipc/memory/resource.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace mem {
|
||||
|
||||
void* pool_alloc::alloc(std::size_t size) {
|
||||
return async_pool_alloc::alloc(size);
|
||||
}
|
||||
|
||||
void pool_alloc::free(void* p, std::size_t size) {
|
||||
async_pool_alloc::free(p, size);
|
||||
}
|
||||
|
||||
} // namespace mem
|
||||
} // namespace ipc
|
102
human_render/Ipc/libipc/pool_alloc.h
Normal file
102
human_render/Ipc/libipc/pool_alloc.h
Normal file
@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
#include "libipc/def.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace mem {
|
||||
|
||||
class pool_alloc {
|
||||
public:
|
||||
static void* alloc(std::size_t size);
|
||||
static void free (void* p, std::size_t size);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// construct/destruct an object
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
struct impl {
|
||||
template <typename... P>
|
||||
static T* construct(T* p, P&&... params) {
|
||||
::new (p) T(std::forward<P>(params)...);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void destruct(T* p) {
|
||||
reinterpret_cast<T*>(p)->~T();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, size_t N>
|
||||
struct impl<T[N]> {
|
||||
using type = T[N];
|
||||
|
||||
template <typename... P>
|
||||
static type* construct(type* p, P&&... params) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
impl<T>::construct(&((*p)[i]), std::forward<P>(params)...);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void destruct(type* p) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
impl<T>::destruct(&((*p)[i]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename... P>
|
||||
T* construct(T* p, P&&... params) {
|
||||
return detail::impl<T>::construct(p, std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... P>
|
||||
T* construct(void* p, P&&... params) {
|
||||
return construct(static_cast<T*>(p), std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void destruct(T* p) {
|
||||
return detail::impl<T>::destruct(p);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void destruct(void* p) {
|
||||
destruct(static_cast<T*>(p));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// general alloc/free
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
inline void* alloc(std::size_t size) {
|
||||
return pool_alloc::alloc(size);
|
||||
}
|
||||
|
||||
template <typename T, typename... P>
|
||||
T* alloc(P&&... params) {
|
||||
return construct<T>(pool_alloc::alloc(sizeof(T)), std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
inline void free(void* p, std::size_t size) {
|
||||
pool_alloc::free(p, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void free(T* p) {
|
||||
if (p == nullptr) return;
|
||||
destruct(p);
|
||||
pool_alloc::free(p, sizeof(T));
|
||||
}
|
||||
|
||||
} // namespace mem
|
||||
} // namespace ipc
|
433
human_render/Ipc/libipc/prod_cons.h
Normal file
433
human_render/Ipc/libipc/prod_cons.h
Normal file
@ -0,0 +1,433 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include <cstdint>
|
||||
|
||||
#include "libipc/def.h"
|
||||
|
||||
#include "libipc/platform/detail.h"
|
||||
#include "libipc/circ/elem_def.h"
|
||||
#include "libipc/utility/log.h"
|
||||
#include "libipc/utility/utility.h"
|
||||
|
||||
namespace ipc {
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// producer-consumer implementation
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Flag>
|
||||
struct prod_cons_impl;
|
||||
|
||||
template <>
|
||||
struct prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
|
||||
|
||||
template <std::size_t DataSize, std::size_t AlignSize>
|
||||
struct elem_t {
|
||||
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
||||
};
|
||||
|
||||
alignas(cache_line_size) std::atomic<circ::u2_t> rd_; // read index
|
||||
alignas(cache_line_size) std::atomic<circ::u2_t> wt_; // write index
|
||||
|
||||
constexpr circ::u2_t cursor() const noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename E>
|
||||
bool push(W* /*wrapper*/, F&& f, E* elems) {
|
||||
auto cur_wt = circ::index_of(wt_.load(std::memory_order_relaxed));
|
||||
if (cur_wt == circ::index_of(rd_.load(std::memory_order_acquire) - 1)) {
|
||||
return false; // full
|
||||
}
|
||||
std::forward<F>(f)(&(elems[cur_wt].data_));
|
||||
wt_.fetch_add(1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* In single-single-unicast, 'force_push' means 'no reader' or 'the only one reader is dead'.
|
||||
* So we could just disconnect all connections of receiver, and return false.
|
||||
*/
|
||||
template <typename W, typename F, typename E>
|
||||
bool force_push(W* wrapper, F&&, E*) {
|
||||
wrapper->elems()->disconnect_receiver(~static_cast<circ::cc_t>(0u));
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename R, typename E>
|
||||
bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, R&& out, E* elems) {
|
||||
auto cur_rd = circ::index_of(rd_.load(std::memory_order_relaxed));
|
||||
if (cur_rd == circ::index_of(wt_.load(std::memory_order_acquire))) {
|
||||
return false; // empty
|
||||
}
|
||||
std::forward<F>(f)(&(elems[cur_rd].data_));
|
||||
std::forward<R>(out)(true);
|
||||
rd_.fetch_add(1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct prod_cons_impl<wr<relat::single, relat::multi , trans::unicast>>
|
||||
: prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
|
||||
|
||||
template <typename W, typename F, typename E>
|
||||
bool force_push(W* wrapper, F&&, E*) {
|
||||
wrapper->elems()->disconnect_receiver(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename R,
|
||||
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, R&& out, E<DS, AS>* elems) {
|
||||
byte_t buff[DS];
|
||||
for (unsigned k = 0;;) {
|
||||
auto cur_rd = rd_.load(std::memory_order_relaxed);
|
||||
if (circ::index_of(cur_rd) ==
|
||||
circ::index_of(wt_.load(std::memory_order_acquire))) {
|
||||
return false; // empty
|
||||
}
|
||||
std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
|
||||
if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
|
||||
std::forward<F>(f)(buff);
|
||||
std::forward<R>(out)(true);
|
||||
return true;
|
||||
}
|
||||
ipc::yield(k);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct prod_cons_impl<wr<relat::multi , relat::multi, trans::unicast>>
|
||||
: prod_cons_impl<wr<relat::single, relat::multi, trans::unicast>> {
|
||||
|
||||
using flag_t = std::uint64_t;
|
||||
|
||||
template <std::size_t DataSize, std::size_t AlignSize>
|
||||
struct elem_t {
|
||||
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
||||
std::atomic<flag_t> f_ct_ { 0 }; // commit flag
|
||||
};
|
||||
|
||||
alignas(cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
|
||||
|
||||
template <typename W, typename F, typename E>
|
||||
bool push(W* /*wrapper*/, F&& f, E* elems) {
|
||||
circ::u2_t cur_ct, nxt_ct;
|
||||
for (unsigned k = 0;;) {
|
||||
cur_ct = ct_.load(std::memory_order_relaxed);
|
||||
if (circ::index_of(nxt_ct = cur_ct + 1) ==
|
||||
circ::index_of(rd_.load(std::memory_order_acquire))) {
|
||||
return false; // full
|
||||
}
|
||||
if (ct_.compare_exchange_weak(cur_ct, nxt_ct, std::memory_order_acq_rel)) {
|
||||
break;
|
||||
}
|
||||
ipc::yield(k);
|
||||
}
|
||||
auto* el = elems + circ::index_of(cur_ct);
|
||||
std::forward<F>(f)(&(el->data_));
|
||||
// set flag & try update wt
|
||||
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
|
||||
while (1) {
|
||||
auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
|
||||
if (cur_ct != wt_.load(std::memory_order_relaxed)) {
|
||||
return true;
|
||||
}
|
||||
if ((~cac_ct) != cur_ct) {
|
||||
return true;
|
||||
}
|
||||
if (!el->f_ct_.compare_exchange_strong(cac_ct, 0, std::memory_order_relaxed)) {
|
||||
return true;
|
||||
}
|
||||
wt_.store(nxt_ct, std::memory_order_release);
|
||||
cur_ct = nxt_ct;
|
||||
nxt_ct = cur_ct + 1;
|
||||
el = elems + circ::index_of(cur_ct);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename E>
|
||||
bool force_push(W* wrapper, F&&, E*) {
|
||||
wrapper->elems()->disconnect_receiver(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename R,
|
||||
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, R&& out, E<DS, AS>* elems) {
|
||||
byte_t buff[DS];
|
||||
for (unsigned k = 0;;) {
|
||||
auto cur_rd = rd_.load(std::memory_order_relaxed);
|
||||
auto cur_wt = wt_.load(std::memory_order_acquire);
|
||||
auto id_rd = circ::index_of(cur_rd);
|
||||
auto id_wt = circ::index_of(cur_wt);
|
||||
if (id_rd == id_wt) {
|
||||
auto* el = elems + id_wt;
|
||||
auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
|
||||
if ((~cac_ct) != cur_wt) {
|
||||
return false; // empty
|
||||
}
|
||||
if (el->f_ct_.compare_exchange_weak(cac_ct, 0, std::memory_order_relaxed)) {
|
||||
wt_.store(cur_wt + 1, std::memory_order_release);
|
||||
}
|
||||
k = 0;
|
||||
}
|
||||
else {
|
||||
std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
|
||||
if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
|
||||
std::forward<F>(f)(buff);
|
||||
std::forward<R>(out)(true);
|
||||
return true;
|
||||
}
|
||||
ipc::yield(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct prod_cons_impl<wr<relat::single, relat::multi, trans::broadcast>> {
|
||||
|
||||
using rc_t = std::uint64_t;
|
||||
|
||||
enum : rc_t {
|
||||
ep_mask = 0x00000000ffffffffull,
|
||||
ep_incr = 0x0000000100000000ull
|
||||
};
|
||||
|
||||
template <std::size_t DataSize, std::size_t AlignSize>
|
||||
struct elem_t {
|
||||
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
||||
std::atomic<rc_t> rc_ { 0 }; // read-counter
|
||||
};
|
||||
|
||||
alignas(cache_line_size) std::atomic<circ::u2_t> wt_; // write index
|
||||
alignas(cache_line_size) rc_t epoch_ { 0 }; // only one writer
|
||||
|
||||
circ::u2_t cursor() const noexcept {
|
||||
return wt_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename E>
|
||||
bool push(W* wrapper, F&& f, E* elems) {
|
||||
E* el;
|
||||
for (unsigned k = 0;;) {
|
||||
circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
|
||||
if (cc == 0) return false; // no reader
|
||||
el = elems + circ::index_of(wt_.load(std::memory_order_relaxed));
|
||||
// check all consumers have finished reading this element
|
||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||
circ::cc_t rem_cc = cur_rc & ep_mask;
|
||||
if ((cc & rem_cc) && ((cur_rc & ~ep_mask) == epoch_)) {
|
||||
return false; // has not finished yet
|
||||
}
|
||||
// consider rem_cc to be 0 here
|
||||
if (el->rc_.compare_exchange_weak(
|
||||
cur_rc, epoch_ | static_cast<rc_t>(cc), std::memory_order_release)) {
|
||||
break;
|
||||
}
|
||||
ipc::yield(k);
|
||||
}
|
||||
std::forward<F>(f)(&(el->data_));
|
||||
wt_.fetch_add(1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename E>
|
||||
bool force_push(W* wrapper, F&& f, E* elems) {
|
||||
E* el;
|
||||
epoch_ += ep_incr;
|
||||
for (unsigned k = 0;;) {
|
||||
circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
|
||||
if (cc == 0) return false; // no reader
|
||||
el = elems + circ::index_of(wt_.load(std::memory_order_relaxed));
|
||||
// check all consumers have finished reading this element
|
||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||
circ::cc_t rem_cc = cur_rc & ep_mask;
|
||||
if (cc & rem_cc) {
|
||||
ipc::log("force_push: k = %u, cc = %u, rem_cc = %u\n", k, cc, rem_cc);
|
||||
cc = wrapper->elems()->disconnect_receiver(rem_cc); // disconnect all invalid readers
|
||||
if (cc == 0) return false; // no reader
|
||||
}
|
||||
// just compare & exchange
|
||||
if (el->rc_.compare_exchange_weak(
|
||||
cur_rc, epoch_ | static_cast<rc_t>(cc), std::memory_order_release)) {
|
||||
break;
|
||||
}
|
||||
ipc::yield(k);
|
||||
}
|
||||
std::forward<F>(f)(&(el->data_));
|
||||
wt_.fetch_add(1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename R, typename E>
|
||||
bool pop(W* wrapper, circ::u2_t& cur, F&& f, R&& out, E* elems) {
|
||||
if (cur == cursor()) return false; // acquire
|
||||
auto* el = elems + circ::index_of(cur++);
|
||||
std::forward<F>(f)(&(el->data_));
|
||||
for (unsigned k = 0;;) {
|
||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||
if ((cur_rc & ep_mask) == 0) {
|
||||
std::forward<R>(out)(true);
|
||||
return true;
|
||||
}
|
||||
auto nxt_rc = cur_rc & ~static_cast<rc_t>(wrapper->connected_id());
|
||||
if (el->rc_.compare_exchange_weak(cur_rc, nxt_rc, std::memory_order_release)) {
|
||||
std::forward<R>(out)((nxt_rc & ep_mask) == 0);
|
||||
return true;
|
||||
}
|
||||
ipc::yield(k);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct prod_cons_impl<wr<relat::multi, relat::multi, trans::broadcast>> {
|
||||
|
||||
using rc_t = std::uint64_t;
|
||||
using flag_t = std::uint64_t;
|
||||
|
||||
enum : rc_t {
|
||||
rc_mask = 0x00000000ffffffffull,
|
||||
ep_mask = 0x00ffffffffffffffull,
|
||||
ep_incr = 0x0100000000000000ull,
|
||||
ic_mask = 0xff000000ffffffffull,
|
||||
ic_incr = 0x0000000100000000ull
|
||||
};
|
||||
|
||||
template <std::size_t DataSize, std::size_t AlignSize>
|
||||
struct elem_t {
|
||||
std::aligned_storage_t<DataSize, AlignSize> data_ {};
|
||||
std::atomic<rc_t > rc_ { 0 }; // read-counter
|
||||
std::atomic<flag_t> f_ct_ { 0 }; // commit flag
|
||||
};
|
||||
|
||||
alignas(cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
|
||||
alignas(cache_line_size) std::atomic<rc_t> epoch_ { 0 };
|
||||
|
||||
circ::u2_t cursor() const noexcept {
|
||||
return ct_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
constexpr static rc_t inc_rc(rc_t rc) noexcept {
|
||||
return (rc & ic_mask) | ((rc + ic_incr) & ~ic_mask);
|
||||
}
|
||||
|
||||
constexpr static rc_t inc_mask(rc_t rc) noexcept {
|
||||
return inc_rc(rc) & ~rc_mask;
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename E>
|
||||
bool push(W* wrapper, F&& f, E* elems) {
|
||||
E* el;
|
||||
circ::u2_t cur_ct;
|
||||
rc_t epoch = epoch_.load(std::memory_order_acquire);
|
||||
for (unsigned k = 0;;) {
|
||||
circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
|
||||
if (cc == 0) return false; // no reader
|
||||
el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
|
||||
// check all consumers have finished reading this element
|
||||
auto cur_rc = el->rc_.load(std::memory_order_relaxed);
|
||||
circ::cc_t rem_cc = cur_rc & rc_mask;
|
||||
if ((cc & rem_cc) && ((cur_rc & ~ep_mask) == epoch)) {
|
||||
return false; // has not finished yet
|
||||
}
|
||||
else if (!rem_cc) {
|
||||
auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
|
||||
if ((cur_fl != cur_ct) && cur_fl) {
|
||||
return false; // full
|
||||
}
|
||||
}
|
||||
// consider rem_cc to be 0 here
|
||||
if (el->rc_.compare_exchange_weak(
|
||||
cur_rc, inc_mask(epoch | (cur_rc & ep_mask)) | static_cast<rc_t>(cc), std::memory_order_relaxed) &&
|
||||
epoch_.compare_exchange_weak(epoch, epoch, std::memory_order_acq_rel)) {
|
||||
break;
|
||||
}
|
||||
ipc::yield(k);
|
||||
}
|
||||
// only one thread/process would touch here at one time
|
||||
ct_.store(cur_ct + 1, std::memory_order_release);
|
||||
std::forward<F>(f)(&(el->data_));
|
||||
// set flag & try update wt
|
||||
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename E>
|
||||
bool force_push(W* wrapper, F&& f, E* elems) {
|
||||
E* el;
|
||||
circ::u2_t cur_ct;
|
||||
rc_t epoch = epoch_.fetch_add(ep_incr, std::memory_order_release) + ep_incr;
|
||||
for (unsigned k = 0;;) {
|
||||
circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
|
||||
if (cc == 0) return false; // no reader
|
||||
el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
|
||||
// check all consumers have finished reading this element
|
||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||
circ::cc_t rem_cc = cur_rc & rc_mask;
|
||||
if (cc & rem_cc) {
|
||||
ipc::log("force_push: k = %u, cc = %u, rem_cc = %u\n", k, cc, rem_cc);
|
||||
cc = wrapper->elems()->disconnect_receiver(rem_cc); // disconnect all invalid readers
|
||||
if (cc == 0) return false; // no reader
|
||||
}
|
||||
// just compare & exchange
|
||||
if (el->rc_.compare_exchange_weak(
|
||||
cur_rc, inc_mask(epoch | (cur_rc & ep_mask)) | static_cast<rc_t>(cc), std::memory_order_relaxed)) {
|
||||
if (epoch == epoch_.load(std::memory_order_acquire)) {
|
||||
break;
|
||||
}
|
||||
else if (push(wrapper, std::forward<F>(f), elems)) {
|
||||
return true;
|
||||
}
|
||||
epoch = epoch_.fetch_add(ep_incr, std::memory_order_release) + ep_incr;
|
||||
}
|
||||
ipc::yield(k);
|
||||
}
|
||||
// only one thread/process would touch here at one time
|
||||
ct_.store(cur_ct + 1, std::memory_order_release);
|
||||
std::forward<F>(f)(&(el->data_));
|
||||
// set flag & try update wt
|
||||
el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename W, typename F, typename R, typename E, std::size_t N>
|
||||
bool pop(W* wrapper, circ::u2_t& cur, F&& f, R&& out, E(& elems)[N]) {
|
||||
auto* el = elems + circ::index_of(cur);
|
||||
auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
|
||||
if (cur_fl != ~static_cast<flag_t>(cur)) {
|
||||
return false; // empty
|
||||
}
|
||||
++cur;
|
||||
std::forward<F>(f)(&(el->data_));
|
||||
for (unsigned k = 0;;) {
|
||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||
if ((cur_rc & rc_mask) == 0) {
|
||||
std::forward<R>(out)(true);
|
||||
el->f_ct_.store(cur + N - 1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
auto nxt_rc = inc_rc(cur_rc) & ~static_cast<rc_t>(wrapper->connected_id());
|
||||
bool last_one = false;
|
||||
if ((last_one = (nxt_rc & rc_mask) == 0)) {
|
||||
el->f_ct_.store(cur + N - 1, std::memory_order_release);
|
||||
}
|
||||
if (el->rc_.compare_exchange_weak(cur_rc, nxt_rc, std::memory_order_release)) {
|
||||
std::forward<R>(out)(last_one);
|
||||
return true;
|
||||
}
|
||||
ipc::yield(k);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ipc
|
223
human_render/Ipc/libipc/queue.h
Normal file
223
human_render/Ipc/libipc/queue.h
Normal file
@ -0,0 +1,223 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <new>
|
||||
#include <utility> // [[since C++14]]: std::exchange
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <tuple>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <cassert> // assert
|
||||
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/shm.h"
|
||||
#include "libipc/rw_lock.h"
|
||||
|
||||
#include "libipc/utility/log.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
#include "libipc/circ/elem_def.h"
|
||||
#include "libipc/memory/resource.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
|
||||
class queue_conn {
|
||||
protected:
|
||||
circ::cc_t connected_ = 0;
|
||||
shm::handle elems_h_;
|
||||
|
||||
template <typename Elems>
|
||||
Elems* open(char const * name) {
|
||||
if (!is_valid_string(name)) {
|
||||
ipc::error("fail open waiter: name is empty!\n");
|
||||
return nullptr;
|
||||
}
|
||||
if (!elems_h_.acquire(name, sizeof(Elems))) {
|
||||
return nullptr;
|
||||
}
|
||||
auto elems = static_cast<Elems*>(elems_h_.get());
|
||||
if (elems == nullptr) {
|
||||
ipc::error("fail acquire elems: %s\n", name);
|
||||
return nullptr;
|
||||
}
|
||||
elems->init();
|
||||
return elems;
|
||||
}
|
||||
|
||||
void close() {
|
||||
elems_h_.release();
|
||||
}
|
||||
|
||||
public:
|
||||
queue_conn() = default;
|
||||
queue_conn(const queue_conn&) = delete;
|
||||
queue_conn& operator=(const queue_conn&) = delete;
|
||||
|
||||
bool connected() const noexcept {
|
||||
return connected_ != 0;
|
||||
}
|
||||
|
||||
circ::cc_t connected_id() const noexcept {
|
||||
return connected_;
|
||||
}
|
||||
|
||||
template <typename Elems>
|
||||
auto connect(Elems* elems) noexcept
|
||||
/*needs 'optional' here*/
|
||||
-> std::tuple<bool, bool, decltype(std::declval<Elems>().cursor())> {
|
||||
if (elems == nullptr) return {};
|
||||
// if it's already connected, just return
|
||||
if (connected()) return {connected(), false, 0};
|
||||
connected_ = elems->connect_receiver();
|
||||
return {connected(), true, elems->cursor()};
|
||||
}
|
||||
|
||||
template <typename Elems>
|
||||
bool disconnect(Elems* elems) noexcept {
|
||||
if (elems == nullptr) return false;
|
||||
// if it's already disconnected, just return false
|
||||
if (!connected()) return false;
|
||||
elems->disconnect_receiver(std::exchange(connected_, 0));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Elems>
|
||||
class queue_base : public queue_conn {
|
||||
using base_t = queue_conn;
|
||||
|
||||
public:
|
||||
using elems_t = Elems;
|
||||
using policy_t = typename elems_t::policy_t;
|
||||
|
||||
protected:
|
||||
elems_t * elems_ = nullptr;
|
||||
decltype(std::declval<elems_t>().cursor()) cursor_ = 0;
|
||||
bool sender_flag_ = false;
|
||||
|
||||
public:
|
||||
using base_t::base_t;
|
||||
|
||||
queue_base() = default;
|
||||
|
||||
explicit queue_base(char const * name)
|
||||
: queue_base{} {
|
||||
elems_ = queue_conn::template open<elems_t>(name);
|
||||
}
|
||||
|
||||
explicit queue_base(elems_t * elems) noexcept
|
||||
: queue_base{} {
|
||||
assert(elems != nullptr);
|
||||
elems_ = elems;
|
||||
}
|
||||
|
||||
/* not virtual */ ~queue_base() {
|
||||
base_t::close();
|
||||
}
|
||||
|
||||
bool open(char const * name) noexcept {
|
||||
base_t::close();
|
||||
elems_ = queue_conn::template open<elems_t>(name);
|
||||
return elems_ != nullptr;
|
||||
}
|
||||
|
||||
elems_t * elems() noexcept { return elems_; }
|
||||
elems_t const * elems() const noexcept { return elems_; }
|
||||
|
||||
bool ready_sending() noexcept {
|
||||
if (elems_ == nullptr) return false;
|
||||
return sender_flag_ || (sender_flag_ = elems_->connect_sender());
|
||||
}
|
||||
|
||||
void shut_sending() noexcept {
|
||||
if (elems_ == nullptr) return;
|
||||
if (!sender_flag_) return;
|
||||
elems_->disconnect_sender();
|
||||
}
|
||||
|
||||
bool connect() noexcept {
|
||||
auto tp = base_t::connect(elems_);
|
||||
if (std::get<0>(tp) && std::get<1>(tp)) {
|
||||
cursor_ = std::get<2>(tp);
|
||||
return true;
|
||||
}
|
||||
return std::get<0>(tp);
|
||||
}
|
||||
|
||||
bool disconnect() noexcept {
|
||||
return base_t::disconnect(elems_);
|
||||
}
|
||||
|
||||
std::size_t conn_count() const noexcept {
|
||||
return (elems_ == nullptr) ? static_cast<std::size_t>(invalid_value) : elems_->conn_count();
|
||||
}
|
||||
|
||||
bool valid() const noexcept {
|
||||
return elems_ != nullptr;
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return !valid() || (cursor_ == elems_->cursor());
|
||||
}
|
||||
|
||||
template <typename T, typename F, typename... P>
|
||||
bool push(F&& prep, P&&... params) {
|
||||
if (elems_ == nullptr) return false;
|
||||
return elems_->push(this, [&](void* p) {
|
||||
if (prep(p)) ::new (p) T(std::forward<P>(params)...);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T, typename F, typename... P>
|
||||
bool force_push(F&& prep, P&&... params) {
|
||||
if (elems_ == nullptr) return false;
|
||||
return elems_->force_push(this, [&](void* p) {
|
||||
if (prep(p)) ::new (p) T(std::forward<P>(params)...);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
bool pop(T& item, F&& out) {
|
||||
if (elems_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return elems_->pop(this, &(this->cursor_), [&item](void* p) {
|
||||
::new (&item) T(std::move(*static_cast<T*>(p)));
|
||||
}, std::forward<F>(out));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename Policy>
|
||||
class queue final : public detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>> {
|
||||
using base_t = detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>>;
|
||||
|
||||
public:
|
||||
using value_t = T;
|
||||
|
||||
using base_t::base_t;
|
||||
|
||||
template <typename... P>
|
||||
bool push(P&&... params) {
|
||||
return base_t::template push<T>(std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
template <typename... P>
|
||||
bool force_push(P&&... params) {
|
||||
return base_t::template force_push<T>(std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
bool pop(T& item) {
|
||||
return base_t::pop(item, [](bool) {});
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
bool pop(T& item, F&& out) {
|
||||
return base_t::pop(item, std::forward<F>(out));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ipc
|
171
human_render/Ipc/libipc/rw_lock.h
Normal file
171
human_render/Ipc/libipc/rw_lock.h
Normal file
@ -0,0 +1,171 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Gives hint to processor that improves performance of spin-wait loops.
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma push_macro("IPC_LOCK_PAUSE_")
|
||||
#undef IPC_LOCK_PAUSE_
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <windows.h> // YieldProcessor
|
||||
/*
|
||||
See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms687419(v=vs.85).aspx
|
||||
Not for intel c++ compiler, so ignore http://software.intel.com/en-us/forums/topic/296168
|
||||
*/
|
||||
# define IPC_LOCK_PAUSE_() YieldProcessor()
|
||||
#elif defined(__GNUC__)
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
/*
|
||||
See: Intel(R) 64 and IA-32 Architectures Software Developer's Manual V2
|
||||
PAUSE-Spin Loop Hint, 4-57
|
||||
http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.html?wapkw=instruction+set+reference
|
||||
*/
|
||||
# define IPC_LOCK_PAUSE_() __asm__ __volatile__("pause")
|
||||
#elif defined(__ia64__) || defined(__ia64)
|
||||
/*
|
||||
See: Intel(R) Itanium(R) Architecture Developer's Manual, Vol.3
|
||||
hint - Performance Hint, 3:145
|
||||
http://www.intel.com/content/www/us/en/processors/itanium/itanium-architecture-vol-3-manual.html
|
||||
*/
|
||||
# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("hint @pause")
|
||||
#elif defined(__arm__)
|
||||
/*
|
||||
See: ARM Architecture Reference Manuals (YIELD)
|
||||
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.architecture.reference/index.html
|
||||
*/
|
||||
# define IPC_LOCK_PAUSE_() __asm__ __volatile__ ("yield")
|
||||
#endif
|
||||
#endif/*compilers*/
|
||||
|
||||
#if !defined(IPC_LOCK_PAUSE_)
|
||||
/*
|
||||
Just use a compiler fence, prevent compiler from optimizing loop
|
||||
*/
|
||||
# define IPC_LOCK_PAUSE_() std::atomic_signal_fence(std::memory_order_seq_cst)
|
||||
#endif/*!defined(IPC_LOCK_PAUSE_)*/
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Yield to other threads
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace ipc {
|
||||
|
||||
template <typename K>
|
||||
inline void yield(K& k) noexcept {
|
||||
if (k < 4) { /* Do nothing */ }
|
||||
else
|
||||
if (k < 16) { IPC_LOCK_PAUSE_(); }
|
||||
else
|
||||
if (k < 32) { std::this_thread::yield(); }
|
||||
else {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
return;
|
||||
}
|
||||
++k;
|
||||
}
|
||||
|
||||
template <std::size_t N = 32, typename K, typename F>
|
||||
inline void sleep(K& k, F&& f) {
|
||||
if (k < static_cast<K>(N)) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
else {
|
||||
static_cast<void>(std::forward<F>(f)());
|
||||
return;
|
||||
}
|
||||
++k;
|
||||
}
|
||||
|
||||
template <std::size_t N = 32, typename K>
|
||||
inline void sleep(K& k) {
|
||||
sleep<N>(k, [] {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
|
||||
#pragma pop_macro("IPC_LOCK_PAUSE_")
|
||||
|
||||
namespace ipc {
|
||||
|
||||
class spin_lock {
|
||||
std::atomic<unsigned> lc_ { 0 };
|
||||
|
||||
public:
|
||||
void lock(void) noexcept {
|
||||
for (unsigned k = 0;
|
||||
lc_.exchange(1, std::memory_order_acquire);
|
||||
yield(k)) ;
|
||||
}
|
||||
|
||||
void unlock(void) noexcept {
|
||||
lc_.store(0, std::memory_order_release);
|
||||
}
|
||||
};
|
||||
|
||||
class rw_lock {
|
||||
using lc_ui_t = unsigned;
|
||||
|
||||
std::atomic<lc_ui_t> lc_ { 0 };
|
||||
|
||||
enum : lc_ui_t {
|
||||
w_mask = (std::numeric_limits<std::make_signed_t<lc_ui_t>>::max)(), // b 0111 1111
|
||||
w_flag = w_mask + 1 // b 1000 0000
|
||||
};
|
||||
|
||||
public:
|
||||
rw_lock() = default;
|
||||
|
||||
rw_lock(const rw_lock&) = delete;
|
||||
rw_lock& operator=(const rw_lock&) = delete;
|
||||
rw_lock(rw_lock&&) = delete;
|
||||
rw_lock& operator=(rw_lock&&) = delete;
|
||||
|
||||
void lock() noexcept {
|
||||
for (unsigned k = 0;;) {
|
||||
auto old = lc_.fetch_or(w_flag, std::memory_order_acq_rel);
|
||||
if (!old) return; // got w-lock
|
||||
if (!(old & w_flag)) break; // other thread having r-lock
|
||||
yield(k); // other thread having w-lock
|
||||
}
|
||||
// wait for reading finished
|
||||
for (unsigned k = 0;
|
||||
lc_.load(std::memory_order_acquire) & w_mask;
|
||||
yield(k)) ;
|
||||
}
|
||||
|
||||
void unlock() noexcept {
|
||||
lc_.store(0, std::memory_order_release);
|
||||
}
|
||||
|
||||
void lock_shared() noexcept {
|
||||
auto old = lc_.load(std::memory_order_acquire);
|
||||
for (unsigned k = 0;;) {
|
||||
// if w_flag set, just continue
|
||||
if (old & w_flag) {
|
||||
yield(k);
|
||||
old = lc_.load(std::memory_order_acquire);
|
||||
}
|
||||
// otherwise try cas lc + 1 (set r-lock)
|
||||
else if (lc_.compare_exchange_weak(old, old + 1, std::memory_order_release)) {
|
||||
return;
|
||||
}
|
||||
// set r-lock failed, old has been updated
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared() noexcept {
|
||||
lc_.fetch_sub(1, std::memory_order_release);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ipc
|
36
human_render/Ipc/libipc/semaphore.h
Normal file
36
human_render/Ipc/libipc/semaphore.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // std::uint64_t
|
||||
|
||||
#include "libipc/def.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace sync {
|
||||
|
||||
class semaphore {
|
||||
semaphore(semaphore const &) = delete;
|
||||
semaphore &operator=(semaphore const &) = delete;
|
||||
|
||||
public:
|
||||
semaphore();
|
||||
explicit semaphore(char const *name, std::uint32_t count = 0);
|
||||
~semaphore();
|
||||
|
||||
void const *native() const noexcept;
|
||||
void *native() noexcept;
|
||||
|
||||
bool valid() const noexcept;
|
||||
|
||||
bool open(char const *name, std::uint32_t count = 0) noexcept;
|
||||
void close() noexcept;
|
||||
|
||||
bool wait(std::uint64_t tm = ipc::invalid_value) noexcept;
|
||||
bool post(std::uint32_t count = 1) noexcept;
|
||||
|
||||
private:
|
||||
class semaphore_;
|
||||
semaphore_* p_;
|
||||
};
|
||||
|
||||
} // namespace sync
|
||||
} // namespace ipc
|
113
human_render/Ipc/libipc/shm.cpp
Normal file
113
human_render/Ipc/libipc/shm.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "libipc/shm.h"
|
||||
|
||||
#include "libipc/utility/pimpl.h"
|
||||
#include "libipc/utility/log.h"
|
||||
#include "libipc/memory/resource.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace shm {
|
||||
|
||||
class handle::handle_ : public pimpl<handle_> {
|
||||
public:
|
||||
shm::id_t id_ = nullptr;
|
||||
void* m_ = nullptr;
|
||||
|
||||
ipc::string n_;
|
||||
std::size_t s_ = 0;
|
||||
};
|
||||
|
||||
handle::handle()
|
||||
: p_(p_->make()) {
|
||||
}
|
||||
|
||||
handle::handle(char const * name, std::size_t size, unsigned mode)
|
||||
: handle() {
|
||||
acquire(name, size, mode);
|
||||
}
|
||||
|
||||
handle::handle(handle&& rhs)
|
||||
: handle() {
|
||||
swap(rhs);
|
||||
}
|
||||
|
||||
handle::~handle() {
|
||||
release();
|
||||
p_->clear();
|
||||
}
|
||||
|
||||
void handle::swap(handle& rhs) {
|
||||
std::swap(p_, rhs.p_);
|
||||
}
|
||||
|
||||
handle& handle::operator=(handle rhs) {
|
||||
swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool handle::valid() const noexcept {
|
||||
return impl(p_)->m_ != nullptr;
|
||||
}
|
||||
|
||||
std::size_t handle::size() const noexcept {
|
||||
return impl(p_)->s_;
|
||||
}
|
||||
|
||||
char const * handle::name() const noexcept {
|
||||
return impl(p_)->n_.c_str();
|
||||
}
|
||||
|
||||
std::int32_t handle::ref() const noexcept {
|
||||
return shm::get_ref(impl(p_)->id_);
|
||||
}
|
||||
|
||||
void handle::sub_ref() noexcept {
|
||||
shm::sub_ref(impl(p_)->id_);
|
||||
}
|
||||
|
||||
bool handle::acquire(char const * name, std::size_t size, unsigned mode) {
|
||||
if (!is_valid_string(name)) {
|
||||
ipc::error("fail acquire: name is empty\n");
|
||||
return false;
|
||||
}
|
||||
if (size == 0) {
|
||||
ipc::error("fail acquire: size is 0\n");
|
||||
return false;
|
||||
}
|
||||
release();
|
||||
impl(p_)->n_ = name;
|
||||
impl(p_)->id_ = shm::acquire(name, size, mode);
|
||||
impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_));
|
||||
return valid();
|
||||
}
|
||||
|
||||
std::int32_t handle::release() {
|
||||
if (impl(p_)->id_ == nullptr) return -1;
|
||||
return shm::release(detach());
|
||||
}
|
||||
|
||||
void* handle::get() const {
|
||||
return impl(p_)->m_;
|
||||
}
|
||||
|
||||
void handle::attach(id_t id) {
|
||||
if (id == nullptr) return;
|
||||
release();
|
||||
impl(p_)->id_ = id;
|
||||
impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_));
|
||||
}
|
||||
|
||||
id_t handle::detach() {
|
||||
auto old = impl(p_)->id_;
|
||||
impl(p_)->id_ = nullptr;
|
||||
impl(p_)->m_ = nullptr;
|
||||
impl(p_)->s_ = 0;
|
||||
impl(p_)->n_.clear();
|
||||
return old;
|
||||
}
|
||||
|
||||
} // namespace shm
|
||||
} // namespace ipc
|
58
human_render/Ipc/libipc/shm.h
Normal file
58
human_render/Ipc/libipc/shm.h
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace ipc {
|
||||
namespace shm {
|
||||
|
||||
using id_t = void*;
|
||||
|
||||
enum : unsigned {
|
||||
create = 0x01,
|
||||
open = 0x02
|
||||
};
|
||||
|
||||
extern id_t acquire(char const * name, std::size_t size, unsigned mode = create | open);
|
||||
extern void * get_mem(id_t id, std::size_t * size);
|
||||
extern std::int32_t release(id_t id);
|
||||
extern void remove (id_t id);
|
||||
extern void remove (char const * name);
|
||||
|
||||
extern std::int32_t get_ref(id_t id);
|
||||
extern void sub_ref(id_t id);
|
||||
|
||||
class handle {
|
||||
public:
|
||||
handle();
|
||||
handle(char const * name, std::size_t size, unsigned mode = create | open);
|
||||
handle(handle&& rhs);
|
||||
|
||||
~handle();
|
||||
|
||||
void swap(handle& rhs);
|
||||
handle& operator=(handle rhs);
|
||||
|
||||
bool valid() const noexcept;
|
||||
std::size_t size () const noexcept;
|
||||
char const * name () const noexcept;
|
||||
|
||||
std::int32_t ref() const noexcept;
|
||||
void sub_ref() noexcept;
|
||||
|
||||
bool acquire(char const * name, std::size_t size, unsigned mode = create | open);
|
||||
std::int32_t release();
|
||||
|
||||
void* get() const;
|
||||
|
||||
void attach(id_t);
|
||||
id_t detach();
|
||||
|
||||
private:
|
||||
class handle_;
|
||||
handle_* p_;
|
||||
};
|
||||
|
||||
} // namespace shm
|
||||
} // namespace ipc
|
77
human_render/Ipc/libipc/sync/condition.cpp
Normal file
77
human_render/Ipc/libipc/sync/condition.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
#include "libipc/condition.h"
|
||||
|
||||
#include "libipc/utility/pimpl.h"
|
||||
#include "libipc/utility/log.h"
|
||||
#include "libipc/memory/resource.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
#if defined(IPC_OS_WINDOWS_)
|
||||
#include "libipc/platform/win/condition.h"
|
||||
#elif defined(IPC_OS_LINUX_)
|
||||
#include "libipc/platform/linux/condition.h"
|
||||
#elif defined(IPC_OS_QNX_)
|
||||
#include "libipc/platform/posix/condition.h"
|
||||
#else/*IPC_OS*/
|
||||
# error "Unsupported platform."
|
||||
#endif
|
||||
|
||||
namespace ipc {
|
||||
namespace sync {
|
||||
|
||||
class condition::condition_ : public ipc::pimpl<condition_> {
|
||||
public:
|
||||
ipc::detail::sync::condition cond_;
|
||||
};
|
||||
|
||||
condition::condition()
|
||||
: p_(p_->make()) {
|
||||
}
|
||||
|
||||
condition::condition(char const * name)
|
||||
: condition() {
|
||||
open(name);
|
||||
}
|
||||
|
||||
condition::~condition() {
|
||||
close();
|
||||
p_->clear();
|
||||
}
|
||||
|
||||
void const *condition::native() const noexcept {
|
||||
return impl(p_)->cond_.native();
|
||||
}
|
||||
|
||||
void *condition::native() noexcept {
|
||||
return impl(p_)->cond_.native();
|
||||
}
|
||||
|
||||
bool condition::valid() const noexcept {
|
||||
return impl(p_)->cond_.valid();
|
||||
}
|
||||
|
||||
bool condition::open(char const *name) noexcept {
|
||||
if (!is_valid_string(name)) {
|
||||
ipc::error("fail condition open: name is empty\n");
|
||||
return false;
|
||||
}
|
||||
return impl(p_)->cond_.open(name);
|
||||
}
|
||||
|
||||
void condition::close() noexcept {
|
||||
impl(p_)->cond_.close();
|
||||
}
|
||||
|
||||
bool condition::wait(ipc::sync::mutex &mtx, std::uint64_t tm) noexcept {
|
||||
return impl(p_)->cond_.wait(mtx, tm);
|
||||
}
|
||||
|
||||
bool condition::notify(ipc::sync::mutex &mtx) noexcept {
|
||||
return impl(p_)->cond_.notify(mtx);
|
||||
}
|
||||
|
||||
bool condition::broadcast(ipc::sync::mutex &mtx) noexcept {
|
||||
return impl(p_)->cond_.broadcast(mtx);
|
||||
}
|
||||
|
||||
} // namespace sync
|
||||
} // namespace ipc
|
77
human_render/Ipc/libipc/sync/mutex.cpp
Normal file
77
human_render/Ipc/libipc/sync/mutex.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
#include "libipc/mutex.h"
|
||||
|
||||
#include "libipc/utility/pimpl.h"
|
||||
#include "libipc/utility/log.h"
|
||||
#include "libipc/memory/resource.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
#if defined(IPC_OS_WINDOWS_)
|
||||
#include "libipc/platform/win/mutex.h"
|
||||
#elif defined(IPC_OS_LINUX_)
|
||||
#include "libipc/platform/linux/mutex.h"
|
||||
#elif defined(IPC_OS_QNX_)
|
||||
#include "libipc/platform/posix/mutex.h"
|
||||
#else/*IPC_OS*/
|
||||
# error "Unsupported platform."
|
||||
#endif
|
||||
|
||||
namespace ipc {
|
||||
namespace sync {
|
||||
|
||||
class mutex::mutex_ : public ipc::pimpl<mutex_> {
|
||||
public:
|
||||
ipc::detail::sync::mutex lock_;
|
||||
};
|
||||
|
||||
mutex::mutex()
|
||||
: p_(p_->make()) {
|
||||
}
|
||||
|
||||
mutex::mutex(char const * name)
|
||||
: mutex() {
|
||||
open(name);
|
||||
}
|
||||
|
||||
mutex::~mutex() {
|
||||
close();
|
||||
p_->clear();
|
||||
}
|
||||
|
||||
void const *mutex::native() const noexcept {
|
||||
return impl(p_)->lock_.native();
|
||||
}
|
||||
|
||||
void *mutex::native() noexcept {
|
||||
return impl(p_)->lock_.native();
|
||||
}
|
||||
|
||||
bool mutex::valid() const noexcept {
|
||||
return impl(p_)->lock_.valid();
|
||||
}
|
||||
|
||||
bool mutex::open(char const *name) noexcept {
|
||||
if (!is_valid_string(name)) {
|
||||
ipc::error("fail mutex open: name is empty\n");
|
||||
return false;
|
||||
}
|
||||
return impl(p_)->lock_.open(name);
|
||||
}
|
||||
|
||||
void mutex::close() noexcept {
|
||||
impl(p_)->lock_.close();
|
||||
}
|
||||
|
||||
bool mutex::lock(std::uint64_t tm) noexcept {
|
||||
return impl(p_)->lock_.lock(tm);
|
||||
}
|
||||
|
||||
bool mutex::try_lock() noexcept(false) {
|
||||
return impl(p_)->lock_.try_lock();
|
||||
}
|
||||
|
||||
bool mutex::unlock() noexcept {
|
||||
return impl(p_)->lock_.unlock();
|
||||
}
|
||||
|
||||
} // namespace sync
|
||||
} // namespace ipc
|
71
human_render/Ipc/libipc/sync/semaphore.cpp
Normal file
71
human_render/Ipc/libipc/sync/semaphore.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
|
||||
#include "libipc/semaphore.h"
|
||||
|
||||
#include "libipc/utility/pimpl.h"
|
||||
#include "libipc/utility/log.h"
|
||||
#include "libipc/memory/resource.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
#if defined(IPC_OS_WINDOWS_)
|
||||
#include "libipc/platform/win/semaphore.h"
|
||||
#elif defined(IPC_OS_LINUX_) || defined(IPC_OS_QNX_)
|
||||
#include "libipc/platform/posix/semaphore_impl.h"
|
||||
#else/*IPC_OS*/
|
||||
# error "Unsupported platform."
|
||||
#endif
|
||||
|
||||
namespace ipc {
|
||||
namespace sync {
|
||||
|
||||
class semaphore::semaphore_ : public ipc::pimpl<semaphore_> {
|
||||
public:
|
||||
ipc::detail::sync::semaphore sem_;
|
||||
};
|
||||
|
||||
semaphore::semaphore()
|
||||
: p_(p_->make()) {
|
||||
}
|
||||
|
||||
semaphore::semaphore(char const * name, std::uint32_t count)
|
||||
: semaphore() {
|
||||
open(name, count);
|
||||
}
|
||||
|
||||
semaphore::~semaphore() {
|
||||
close();
|
||||
p_->clear();
|
||||
}
|
||||
|
||||
void const *semaphore::native() const noexcept {
|
||||
return impl(p_)->sem_.native();
|
||||
}
|
||||
|
||||
void *semaphore::native() noexcept {
|
||||
return impl(p_)->sem_.native();
|
||||
}
|
||||
|
||||
bool semaphore::valid() const noexcept {
|
||||
return impl(p_)->sem_.valid();
|
||||
}
|
||||
|
||||
bool semaphore::open(char const *name, std::uint32_t count) noexcept {
|
||||
if (!is_valid_string(name)) {
|
||||
ipc::error("fail semaphore open: name is empty\n");
|
||||
return false;
|
||||
}
|
||||
return impl(p_)->sem_.open(name, count);
|
||||
}
|
||||
|
||||
void semaphore::close() noexcept {
|
||||
impl(p_)->sem_.close();
|
||||
}
|
||||
|
||||
bool semaphore::wait(std::uint64_t tm) noexcept {
|
||||
return impl(p_)->sem_.wait(tm);
|
||||
}
|
||||
|
||||
bool semaphore::post(std::uint32_t count) noexcept {
|
||||
return impl(p_)->sem_.post(count);
|
||||
}
|
||||
|
||||
} // namespace sync
|
||||
} // namespace ipc
|
22
human_render/Ipc/libipc/sync/waiter.cpp
Normal file
22
human_render/Ipc/libipc/sync/waiter.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "libipc/waiter.h"
|
||||
|
||||
#include "libipc/platform/detail.h"
|
||||
#if defined(IPC_OS_WINDOWS_)
|
||||
#include "libipc/platform/win/mutex.h"
|
||||
#elif defined(IPC_OS_LINUX_)
|
||||
#include "libipc/platform/linux/mutex.h"
|
||||
#elif defined(IPC_OS_QNX_)
|
||||
#include "libipc/platform/posix/mutex.h"
|
||||
#else/*IPC_OS*/
|
||||
# error "Unsupported platform."
|
||||
#endif
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
|
||||
void waiter::init() {
|
||||
ipc::detail::sync::mutex::init();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace ipc
|
29
human_render/Ipc/libipc/utility/concept.h
Normal file
29
human_render/Ipc/libipc/utility/concept.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits> // std::enable_if
|
||||
|
||||
namespace ipc {
|
||||
|
||||
// concept helpers
|
||||
|
||||
template <bool Cond, typename R = void>
|
||||
using require = typename std::enable_if<Cond, R>::type;
|
||||
|
||||
#ifdef IPC_CONCEPT_
|
||||
# error "IPC_CONCEPT_ has been defined."
|
||||
#endif
|
||||
|
||||
#define IPC_CONCEPT_(NAME, WHAT) \
|
||||
template <typename T> \
|
||||
class NAME { \
|
||||
private: \
|
||||
template <typename Type> \
|
||||
static std::true_type check(decltype(std::declval<Type>().WHAT)*); \
|
||||
template <typename Type> \
|
||||
static std::false_type check(...); \
|
||||
public: \
|
||||
using type = decltype(check<T>(nullptr)); \
|
||||
constexpr static auto value = type::value; \
|
||||
}
|
||||
|
||||
} // namespace ipc
|
103
human_render/Ipc/libipc/utility/id_pool.h
Normal file
103
human_render/Ipc/libipc/utility/id_pool.h
Normal file
@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits> // std::aligned_storage_t
|
||||
#include <cstring> // std::memcmp
|
||||
#include <cstdint>
|
||||
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
|
||||
using storage_id_t = std::int32_t;
|
||||
|
||||
template <std::size_t DataSize, std::size_t AlignSize>
|
||||
struct id_type;
|
||||
|
||||
template <std::size_t AlignSize>
|
||||
struct id_type<0, AlignSize> {
|
||||
uint_t<8> id_;
|
||||
|
||||
id_type& operator=(storage_id_t val) {
|
||||
id_ = static_cast<uint_t<8>>(val);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
operator uint_t<8>() const {
|
||||
return id_;
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t DataSize, std::size_t AlignSize>
|
||||
struct id_type : id_type<0, AlignSize> {
|
||||
std::aligned_storage_t<DataSize, AlignSize> data_;
|
||||
};
|
||||
|
||||
template <std::size_t DataSize = 0,
|
||||
std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))>
|
||||
class id_pool {
|
||||
|
||||
static constexpr std::size_t limited_max_count() {
|
||||
return ipc::detail::min<std::size_t>(large_msg_cache, (std::numeric_limits<uint_t<8>>::max)());
|
||||
}
|
||||
|
||||
public:
|
||||
enum : std::size_t {
|
||||
/* eliminate error: taking address of temporary */
|
||||
max_count = limited_max_count()
|
||||
};
|
||||
|
||||
private:
|
||||
id_type<DataSize, AlignSize> next_[max_count];
|
||||
uint_t<8> cursor_ = 0;
|
||||
bool prepared_ = false;
|
||||
|
||||
public:
|
||||
void prepare() {
|
||||
if (!prepared_ && this->invalid()) this->init();
|
||||
prepared_ = true;
|
||||
}
|
||||
|
||||
void init() {
|
||||
for (storage_id_t i = 0; i < max_count;) {
|
||||
i = next_[i] = (i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool invalid() const {
|
||||
static id_pool inv;
|
||||
return std::memcmp(this, &inv, sizeof(id_pool)) == 0;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return cursor_ == max_count;
|
||||
}
|
||||
|
||||
storage_id_t acquire() {
|
||||
if (empty()) return -1;
|
||||
storage_id_t id = cursor_;
|
||||
cursor_ = next_[id]; // point to next
|
||||
return id;
|
||||
}
|
||||
|
||||
bool release(storage_id_t id) {
|
||||
if (id < 0) return false;
|
||||
next_[id] = cursor_;
|
||||
cursor_ = static_cast<uint_t<8>>(id); // put it back
|
||||
return true;
|
||||
}
|
||||
|
||||
void * at(storage_id_t id) { return &(next_[id].data_); }
|
||||
void const * at(storage_id_t id) const { return &(next_[id].data_); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class obj_pool : public id_pool<sizeof(T), alignof(T)> {
|
||||
using base_t = id_pool<sizeof(T), alignof(T)>;
|
||||
|
||||
public:
|
||||
T * at(storage_id_t id) { return reinterpret_cast<T *>(base_t::at(id)); }
|
||||
T const * at(storage_id_t id) const { return reinterpret_cast<T const *>(base_t::at(id)); }
|
||||
};
|
||||
|
||||
} // namespace ipc
|
39
human_render/Ipc/libipc/utility/log.h
Normal file
39
human_render/Ipc/libipc/utility/log.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <utility>
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
|
||||
template <typename O>
|
||||
void print(O out, char const * str) {
|
||||
std::fprintf(out, "%s", str);
|
||||
}
|
||||
|
||||
template <typename O, typename P1, typename... P>
|
||||
void print(O out, char const * fmt, P1&& p1, P&&... params) {
|
||||
std::fprintf(out, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline void log(char const * fmt) {
|
||||
ipc::detail::print(stdout, fmt);
|
||||
}
|
||||
|
||||
template <typename P1, typename... P>
|
||||
void log(char const * fmt, P1&& p1, P&&... params) {
|
||||
ipc::detail::print(stdout, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
inline void error(char const * str) {
|
||||
ipc::detail::print(stderr, str);
|
||||
}
|
||||
|
||||
template <typename P1, typename... P>
|
||||
void error(char const * fmt, P1&& p1, P&&... params) {
|
||||
ipc::detail::print(stderr, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
} // namespace ipc
|
64
human_render/Ipc/libipc/utility/pimpl.h
Normal file
64
human_render/Ipc/libipc/utility/pimpl.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
#include "libipc/platform/detail.h"
|
||||
#include "libipc/utility/concept.h"
|
||||
#include "libipc/pool_alloc.h"
|
||||
|
||||
namespace ipc {
|
||||
|
||||
// pimpl small object optimization helpers
|
||||
|
||||
template <typename T, typename R = T*>
|
||||
using IsImplComfortable = ipc::require<(sizeof(T) <= sizeof(T*)), R>;
|
||||
|
||||
template <typename T, typename R = T*>
|
||||
using IsImplUncomfortable = ipc::require<(sizeof(T) > sizeof(T*)), R>;
|
||||
|
||||
template <typename T, typename... P>
|
||||
IPC_CONSTEXPR_ auto make_impl(P&&... params) -> IsImplComfortable<T> {
|
||||
T* buf {};
|
||||
::new (&buf) T { std::forward<P>(params)... };
|
||||
return buf;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IPC_CONSTEXPR_ auto impl(T* const (& p)) -> IsImplComfortable<T> {
|
||||
return reinterpret_cast<T*>(&const_cast<char &>(reinterpret_cast<char const &>(p)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplComfortable<T, void> {
|
||||
if (p != nullptr) impl(p)->~T();
|
||||
}
|
||||
|
||||
template <typename T, typename... P>
|
||||
IPC_CONSTEXPR_ auto make_impl(P&&... params) -> IsImplUncomfortable<T> {
|
||||
return mem::alloc<T>(std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplUncomfortable<T, void> {
|
||||
mem::free(p);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IPC_CONSTEXPR_ auto impl(T* const (& p)) -> IsImplUncomfortable<T> {
|
||||
return p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct pimpl {
|
||||
template <typename... P>
|
||||
IPC_CONSTEXPR_ static T* make(P&&... params) {
|
||||
return make_impl<T>(std::forward<P>(params)...);
|
||||
}
|
||||
|
||||
IPC_CONSTEXPR_ void clear() {
|
||||
clear_impl(static_cast<T*>(const_cast<pimpl*>(this)));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ipc
|
64
human_render/Ipc/libipc/utility/scope_guard.h
Normal file
64
human_render/Ipc/libipc/utility/scope_guard.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility> // std::forward, std::move
|
||||
#include <algorithm> // std::swap
|
||||
#include <type_traits> // std::decay
|
||||
|
||||
namespace ipc {
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Execute guard function when the enclosing scope exits
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename F>
|
||||
class scope_guard {
|
||||
F destructor_;
|
||||
mutable bool dismiss_;
|
||||
|
||||
public:
|
||||
template <typename D>
|
||||
scope_guard(D && destructor)
|
||||
: destructor_(std::forward<D>(destructor))
|
||||
, dismiss_(false) {
|
||||
}
|
||||
|
||||
scope_guard(scope_guard&& rhs)
|
||||
: destructor_(std::move(rhs.destructor_))
|
||||
, dismiss_(true) /* dismiss rhs */ {
|
||||
std::swap(dismiss_, rhs.dismiss_);
|
||||
}
|
||||
|
||||
~scope_guard() {
|
||||
try { do_exit(); }
|
||||
/**
|
||||
* In the realm of exceptions, it is fundamental that you can do nothing
|
||||
* if your "undo/recover" action fails.
|
||||
*/
|
||||
catch (...) { /* Do nothing */ }
|
||||
}
|
||||
|
||||
void swap(scope_guard & rhs) {
|
||||
std::swap(destructor_, rhs.destructor_);
|
||||
std::swap(dismiss_ , rhs.dismiss_);
|
||||
}
|
||||
|
||||
void dismiss() const noexcept {
|
||||
dismiss_ = true;
|
||||
}
|
||||
|
||||
void do_exit() {
|
||||
if (!dismiss_) {
|
||||
dismiss_ = true;
|
||||
destructor_();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename D>
|
||||
constexpr auto guard(D && destructor) noexcept {
|
||||
return scope_guard<std::decay_t<D>> {
|
||||
std::forward<D>(destructor)
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace ipc
|
64
human_render/Ipc/libipc/utility/utility.h
Normal file
64
human_render/Ipc/libipc/utility/utility.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility> // std::forward, std::integer_sequence
|
||||
#include <cstddef> // std::size_t
|
||||
#include <new> // std::hardware_destructive_interference_size
|
||||
#include <type_traits> // std::is_trivially_copyable
|
||||
|
||||
#include "libipc/platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
|
||||
template <typename F, typename D>
|
||||
constexpr decltype(auto) static_switch(std::size_t /*i*/, std::index_sequence<>, F&& /*f*/, D&& def) {
|
||||
return std::forward<D>(def)();
|
||||
}
|
||||
|
||||
template <typename F, typename D, std::size_t N, std::size_t...I>
|
||||
constexpr decltype(auto) static_switch(std::size_t i, std::index_sequence<N, I...>, F&& f, D&& def) {
|
||||
return (i == N) ? std::forward<F>(f)(std::integral_constant<std::size_t, N>{}) :
|
||||
static_switch(i, std::index_sequence<I...>{}, std::forward<F>(f), std::forward<D>(def));
|
||||
}
|
||||
|
||||
template <std::size_t N, typename F, typename D>
|
||||
constexpr decltype(auto) static_switch(std::size_t i, F&& f, D&& def) {
|
||||
return static_switch(i, std::make_index_sequence<N>{}, std::forward<F>(f), std::forward<D>(def));
|
||||
}
|
||||
|
||||
template <typename F, std::size_t...I>
|
||||
IPC_CONSTEXPR_ void static_for(std::index_sequence<I...>, F&& f) {
|
||||
IPC_UNUSED_ auto expand = { (std::forward<F>(f)(std::integral_constant<std::size_t, I>{}), 0)... };
|
||||
}
|
||||
|
||||
template <std::size_t N, typename F>
|
||||
IPC_CONSTEXPR_ void static_for(F&& f) {
|
||||
static_for(std::make_index_sequence<N>{}, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// Minimum offset between two objects to avoid false sharing.
|
||||
enum {
|
||||
// #if __cplusplus >= 201703L
|
||||
// cache_line_size = std::hardware_destructive_interference_size
|
||||
// #else /*__cplusplus < 201703L*/
|
||||
cache_line_size = 64
|
||||
// #endif/*__cplusplus < 201703L*/
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
auto horrible_cast(U rhs) noexcept
|
||||
-> typename std::enable_if<std::is_trivially_copyable<T>::value
|
||||
&& std::is_trivially_copyable<U>::value, T>::type {
|
||||
union {
|
||||
T t;
|
||||
U u;
|
||||
} r = {};
|
||||
r.u = rhs;
|
||||
return r.t;
|
||||
}
|
||||
|
||||
IPC_CONSTEXPR_ std::size_t make_align(std::size_t align, std::size_t size) {
|
||||
// align must be 2^n
|
||||
return (size + align - 1) & ~(align - 1);
|
||||
}
|
||||
|
||||
} // namespace ipc
|
87
human_render/Ipc/libipc/waiter.h
Normal file
87
human_render/Ipc/libipc/waiter.h
Normal file
@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
#include "libipc/def.h"
|
||||
#include "libipc/mutex.h"
|
||||
#include "libipc/condition.h"
|
||||
#include "libipc/platform/detail.h"
|
||||
|
||||
namespace ipc {
|
||||
namespace detail {
|
||||
|
||||
class waiter {
|
||||
ipc::sync::condition cond_;
|
||||
ipc::sync::mutex lock_;
|
||||
std::atomic<bool> quit_ {false};
|
||||
|
||||
public:
|
||||
static void init();
|
||||
|
||||
waiter() = default;
|
||||
waiter(char const *name) {
|
||||
open(name);
|
||||
}
|
||||
|
||||
~waiter() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool valid() const noexcept {
|
||||
return cond_.valid() && lock_.valid();
|
||||
}
|
||||
|
||||
bool open(char const *name) noexcept {
|
||||
quit_.store(false, std::memory_order_relaxed);
|
||||
if (!cond_.open((std::string{name} + "_WAITER_COND_").c_str())) {
|
||||
return false;
|
||||
}
|
||||
if (!lock_.open((std::string{name} + "_WAITER_LOCK_").c_str())) {
|
||||
cond_.close();
|
||||
return false;
|
||||
}
|
||||
return valid();
|
||||
}
|
||||
|
||||
void close() noexcept {
|
||||
cond_.close();
|
||||
lock_.close();
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
bool wait_if(F &&pred, std::uint64_t tm = ipc::invalid_value) noexcept {
|
||||
IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> guard {lock_};
|
||||
while ([this, &pred] {
|
||||
return !quit_.load(std::memory_order_relaxed)
|
||||
&& std::forward<F>(pred)();
|
||||
}()) {
|
||||
if (!cond_.wait(lock_, tm)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool notify() noexcept {
|
||||
{
|
||||
IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> barrier{lock_}; // barrier
|
||||
}
|
||||
return cond_.notify(lock_);
|
||||
}
|
||||
|
||||
bool broadcast() noexcept {
|
||||
{
|
||||
IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> barrier{lock_}; // barrier
|
||||
}
|
||||
return cond_.broadcast(lock_);
|
||||
}
|
||||
|
||||
bool quit_waiting() {
|
||||
quit_.store(true, std::memory_order_release);
|
||||
return broadcast();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace ipc
|
Loading…
Reference in New Issue
Block a user