Add class 'BufferUtility'.

This commit is contained in:
luocai 2023-07-25 10:40:14 +08:00
parent e1558d53a2
commit 75929a843e
11 changed files with 848 additions and 1 deletions

View File

@ -1,3 +1,5 @@
find_package(Boost COMPONENTS url REQUIRED)
add_library(HttpProxy add_library(HttpProxy
NetworkUtility.h NetworkUtility.cpp NetworkUtility.h NetworkUtility.cpp
ProxyHttpSession.h ProxyHttpSession.cpp ProxyHttpSession.h ProxyHttpSession.cpp
@ -11,8 +13,11 @@ add_library(HttpProxy
target_include_directories(HttpProxy target_include_directories(HttpProxy
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${OPENSSL_INCLUDE_DIR}
) )
target_link_libraries(HttpProxy target_link_libraries(HttpProxy
PUBLIC ${Boost_LIBRARIES}
PUBLIC Universal PUBLIC Universal
PRIVATE ${OpenSSL_LIBRARY}
) )

View File

@ -0,0 +1,98 @@
#include "BufferUtility.h"
#include <cstring>
#include <filesystem>
#include <fstream>
namespace Amass {
const char CRLF[] = "\r\n";
const char CRLFCRLF[] = "\r\n\r\n";
Buffer::Buffer(size_t initialSize) : m_buffer(initialSize) {
}
void Buffer::swap(Buffer &buffer) {
m_buffer.swap(buffer.m_buffer);
std::swap(m_readerIndex, buffer.m_readerIndex);
std::swap(m_writerIndex, buffer.m_writerIndex);
}
int Buffer::lastError() {
return m_lastError;
}
void Buffer::append(const char *data, size_t len) {
if (writableBytes() < len + 1)
makeSpace(len + 1); // 剩下的可写空间如果小于需要的空间len+1则增加len+1长度个空间,[len+1]用来加上\0进行截断
std::memcpy(writableAddress(), data, len);
std::copy(data, data + len, writableAddress()); // 其实相当于把已有数据往前挪动
m_writerIndex += len;
// 当Buffer以readableAddress()开始直接作为字符串直接使用时,在此加上\0截断,否则随机会打印出writableAddress()后的内容
m_buffer[m_writerIndex] = '\0';
}
void Buffer::appendFileContent(const std::string_view &path) {
if (path.empty() || !std::filesystem::exists(path)) return;
std::ifstream ifs(path.data(), std::ifstream::binary);
char buffer[512];
while (ifs.read(buffer, sizeof(buffer))) {
append(buffer, ifs.gcount());
}
auto size = ifs.gcount();
if (size > 0) append(buffer, size);
}
void Buffer::makeSpace(size_t needSize) {
if ((writableBytes() + m_readerIndex) < needSize) { // 即使数据前移,空间还是不足
auto remainSize = readableBytes();
auto newSize = remainSize + needSize + 1024;
std::vector<char> newBuffer(newSize);
memcpy(newBuffer.data(), readableAddress(), remainSize);
m_buffer.swap(newBuffer);
m_readerIndex = 0;
m_writerIndex = remainSize;
} else { // 将数据前移,空间满足要求
auto remainSize = readableBytes();
auto dst = m_buffer.data();
auto src = readableAddress();
for (size_t i = 0; i < remainSize; i++) {
*dst++ = *src++;
}
m_readerIndex = 0;
m_writerIndex = remainSize;
}
}
const char *Buffer::findCRLF() const {
const char *crlf = std::search(readableAddress(), writableAddress(), CRLF, CRLF + 2);
return crlf == writableAddress() ? nullptr : crlf;
}
const char *Buffer::findCRLFCRLF() const {
auto crlf = std::search(readableAddress(), writableAddress(), CRLFCRLF, CRLFCRLF + 4);
return crlf == writableAddress() ? nullptr : crlf;
}
const char *Buffer::findEOL(const char *start) const {
if (start == nullptr) start = readableAddress();
if ((readableAddress() > start) || (start >= writableAddress())) return nullptr;
const void *eol = memchr(start, '\n', static_cast<size_t>(writableAddress() - start));
return static_cast<const char *>(eol);
}
bool Buffer::retrieve(size_t len) {
if (len > readableBytes()) return false;
if (len < readableBytes()) {
m_readerIndex += len;
} else {
retrieveAll();
}
return true;
}
bool Buffer::retrieveUntil(const char *end) {
if (((uint64_t)readableAddress() > (uint64_t)end) || ((uint64_t)end > (uint64_t)writableAddress())) return false;
return retrieve(static_cast<size_t>(end - readableAddress()));
}
} // namespace Amass

213
Universal/BufferUtility.h Normal file
View File

@ -0,0 +1,213 @@
#ifndef __BUFFERUTILITY_H__
#define __BUFFERUTILITY_H__
#include <algorithm>
#include <cstdint>
#include <stdexcept>
#include <vector>
namespace Amass {
/**
* @brief The Buffer class A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer
* +------------------+------------------+
* | readable bytes | writable bytes |
* | (CONTENT) | |
* +------------------+------------------+
* | | |
* 0 <=readerIndex <= writerIndex <= size
*/
class Buffer {
public:
Buffer(size_t initialSize = 1024);
void swap(Buffer &buffer);
int lastError();
inline const char *readableAddress() const {
return m_buffer.data() + m_readerIndex;
}
inline size_t readableBytes() const {
return m_writerIndex - m_readerIndex;
}
void append(const char *data, size_t len);
inline void append(std::string_view data) {
append(data.data(), data.size());
}
void appendFileContent(const std::string_view &path);
/**
* @brief peek must POD struct
* @param fd
* @return [errno,size]
*/
template <typename T>
T peek() {
T t;
memcpy(&t, readableAddress(), sizeof(T));
return t;
}
inline std::string peek(size_t size, bool retrieved = false) {
std::string ret(readableAddress(), size);
if (retrieved) retrieve(size);
return ret;
}
/**
* @brief findCRLF
* @param start if(start == nullptr),find CRLF from readableAddress.
* @return
*/
const char *findCRLF() const;
const char *findCRLFCRLF() const;
template <typename T>
const char *find(const T &data, size_t startPos = 0) {
if (startPos >= readableBytes()) return nullptr;
const char *pos = nullptr;
if constexpr (!std::is_array_v<T>) {
if (startPos + sizeof(T) > readableBytes()) return nullptr;
pos = std::search(readableAddress() + startPos, (const char *)writableAddress(), (const char *)(&data),
(const char *)(&data));
} else {
if (startPos + sizeof(T) * std::extent_v < T >> readableBytes()) return nullptr;
pos = std::search(readableAddress() + startPos, (const char *)writableAddress(), (const char *)(&data[0]),
(const char *)(&data[std::extent_v<T>]));
}
return pos == writableAddress() ? nullptr : pos;
}
/**
* @brief findEOL
* @param start if(start == nullptr),find EOL from readableAddress.
* @return
*/
const char *findEOL(const char *start = nullptr) const;
/**
* @brief retrieve retrieve returns void, to prevent string str(retrieve(readableBytes()),
* readableBytes()); the evaluation of two functions are unspecified
* @param len
* @return
*/
bool retrieve(size_t len);
/**
* @brief retrieveUntil retrive [begin,end)
* @param end
* @return
*/
bool retrieveUntil(const char *end);
/**
* @brief retrieveAll
* @param Return[template parameter] void,std::string,std::vector<char>
* @return
*/
template <typename ReturnType = void>
typename std::conditional_t<std::is_same_v<ReturnType, void>, void, ReturnType> retrieveAll() {
if constexpr (std::is_same_v<ReturnType, std::string>) {
std::string data(m_buffer.cbegin() + static_cast<int64_t>(m_readerIndex),
m_buffer.cbegin() + static_cast<int64_t>(m_writerIndex));
m_readerIndex = 0;
m_writerIndex = 0;
m_buffer[m_readerIndex] = '\0';
return data;
} else if constexpr (std::is_same_v<ReturnType, std::vector<char>>) {
std::vector<char> data(m_buffer.cbegin() + static_cast<int64_t>(m_readerIndex),
m_buffer.cbegin() + static_cast<int64_t>(m_writerIndex));
m_readerIndex = 0;
m_writerIndex = 0;
m_buffer[m_readerIndex] = '\0';
return data;
} else if constexpr (std::is_same_v<ReturnType, void>) {
m_readerIndex = 0;
m_writerIndex = 0;
m_buffer[m_readerIndex] = '\0';
}
}
protected:
/**
* @brief makeSpace m_buffer的writeable区域容量
* @param len
*/
void makeSpace(size_t len);
inline size_t writableBytes() const {
return m_buffer.size() - m_writerIndex;
}
inline const char *writableAddress() const {
return m_buffer.data() + m_writerIndex;
}
inline char *writableAddress() {
return m_buffer.data() + m_writerIndex;
}
private:
std::vector<char> m_buffer;
size_t m_readerIndex = 0;
size_t m_writerIndex = 0;
int m_lastError;
};
template <typename T>
class VectorView {
public:
using size_type = std::size_t;
using const_reference = const T &;
VectorView(const T *ptr, size_type length) : m_data(ptr), m_length(length) {
}
constexpr size_type size() const noexcept {
return m_length;
}
constexpr size_type length() const noexcept {
return m_length;
}
constexpr const_reference front() const {
return m_data[0];
}
constexpr const_reference back() const {
return m_data[size() - 1];
}
constexpr const_reference at(size_type pos) const {
return m_data[pos];
}
private:
const T *m_data;
size_type m_length;
};
template <typename T>
class BufferView {
public:
BufferView() {
}
BufferView(const T *data, size_t size) : m_data(data), m_length(size), m_current(data) {
}
bool empty() const {
return m_data == nullptr || (m_current > (m_data + m_length));
}
size_t size() const {
return m_data + m_length - m_current;
}
void read(T *buffer, size_t size) {
std::copy(m_current, m_current + size, buffer);
m_current += size;
}
private:
const T *m_data = nullptr;
size_t m_length = 0;
const T *m_current = nullptr;
};
} // namespace Amass
#endif // __BUFFERUTILITY_H__

View File

@ -1,12 +1,16 @@
find_package(Boost REQUIRED COMPONENTS log log_setup) find_package(Boost REQUIRED COMPONENTS log log_setup program_options)
add_library(Universal add_library(Universal
BoostLog.h BoostLog.inl BoostLog.cpp BoostLog.h BoostLog.inl BoostLog.cpp
BufferUtility.h BufferUtility.cpp
CommandLineInterpreter.h CommandLineInterpreter.cpp
DateTime.h DateTime.cpp DateTime.h DateTime.cpp
FunctionTraits.h FunctionTraits.h
IoContext.h IoContext.cpp IoContext.h IoContext.cpp
MessageManager.h MessageManager.inl MessageManager.cpp MessageManager.h MessageManager.inl MessageManager.cpp
ProcessUtility.h ProcessUtility.inl ProcessUtility.cpp
Singleton.h Singleton.h
SingletonProcess.h SingletonProcess.cpp
StreamFormat.h StreamFormat.inl StreamFormat.cpp StreamFormat.h StreamFormat.inl StreamFormat.cpp
StringUtility.h StringUtility.cpp StringUtility.h StringUtility.cpp
) )

View File

@ -0,0 +1,101 @@
#include "CommandLineInterpreter.h"
#include "BoostLog.h"
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <iostream>
CommandLineInterpreter::CommandLineInterpreter(const DescriptionPointer &description, const std::string &prompt)
: m_description(description), m_prompt(prompt) {}
void CommandLineInterpreter::interpret(std::istream &inputStream) {
std::string command;
std::cout << m_prompt << std::flush;
while (std::getline(inputStream, command, '\n')) {
handleReadLine(command);
std::cout << m_prompt << std::flush;
}
}
void CommandLineInterpreter::handleReadLine(std::string line) {
using namespace boost::program_options;
if (m_description.expired()) {
LOG(error) << "description has expired.";
return;
}
auto description = m_description.lock();
std::vector<std::string> args;
// huu, ugly...
args = splitCommandLine(std::string("--") + line);
try {
variables_map vm;
store(command_line_parser(args).options(*description).run(), vm);
notify(vm);
} catch (boost::program_options::unknown_option &e) {
std::cerr << "error: " << e.what() << std::endl;
} catch (boost::program_options::invalid_command_line_syntax &e) {
std::cerr << "error: " << e.what() << std::endl;
} catch (boost::program_options::validation_error &e) {
std::cerr << "error: " << e.what() << std::endl;
}
}
std::vector<std::string> CommandLineInterpreter::splitCommandLine(const std::string &input) {
std::vector<std::string> result;
std::string::const_iterator i = input.begin(), e = input.end();
for (; i != e; ++i)
if (!isspace((unsigned char)*i)) break;
if (i != e) {
std::string current;
bool inside_quoted = false;
int backslash_count = 0;
for (; i != e; ++i) {
if (*i == '"') {
// '"' preceded by even number (n) of backslashes generates
// n/2 backslashes and is a quoted block delimiter
if (backslash_count % 2 == 0) {
current.append(backslash_count / 2, '\\');
inside_quoted = !inside_quoted;
// '"' preceded by odd number (n) of backslashes generates
// (n-1)/2 backslashes and is literal quote.
} else {
current.append(backslash_count / 2, '\\');
current += '"';
}
backslash_count = 0;
} else if (*i == '\\') {
++backslash_count;
} else {
// Not quote or backslash. All accumulated backslashes should be
// added
if (backslash_count) {
current.append(backslash_count, '\\');
backslash_count = 0;
}
if (isspace((unsigned char)*i) && !inside_quoted) {
// Space outside quoted section terminate the current argument
result.push_back(current);
current.resize(0);
for (; i != e && isspace((unsigned char)*i); ++i)
;
--i;
} else {
current += *i;
}
}
}
// If we have trailing backslashes, add them
if (backslash_count) current.append(backslash_count, '\\');
// If we have non-empty 'current' or we're still in quoted
// section (even if 'current' is empty), add the last token.
if (!current.empty() || inside_quoted) result.push_back(current);
}
return result;
}

View File

@ -0,0 +1,23 @@
#ifndef COMMANDLINEINTERPRETER_H
#define COMMANDLINEINTERPRETER_H
#include <boost/program_options/options_description.hpp>
#include <memory>
class CommandLineInterpreter {
public:
using DescriptionPointer = std::shared_ptr<boost::program_options::options_description>;
using DescriptionWeakPointer = std::weak_ptr<boost::program_options::options_description>;
CommandLineInterpreter(const DescriptionPointer &description, const std::string &prompt);
void interpret(std::istream &inputStream);
protected:
void handleReadLine(std::string line);
static std::vector<std::string> splitCommandLine(const std::string &input);
private:
const DescriptionWeakPointer m_description;
std::string m_prompt;
};
#endif // COMMANDLINEINTERPRETER_H

View File

@ -0,0 +1,200 @@
#include "ProcessUtility.h"
#include "BoostLog.h"
#include "boost/algorithm/string.hpp"
#include <regex>
#include <sstream>
#ifdef __linux__
#include <unistd.h>
#elif defined(_WIN32)
#include <boost/winapi/dll.hpp>
#endif
bool DockerUtility::supervisorStatus(std::string_view container, const std::string &module) {
using namespace boost::process;
boost::process::ipstream ips;
auto docker = search_path("docker");
auto ctl = boost::process::search_path("supervisorctl");
auto env = boost::this_process::environment();
if (env.count("USER")) {
auto user = env["USER"].to_string();
system(docker, args = {"exec", "-u", user, container.data(), "supervisorctl", "status", module}, std_out > ips);
std::string line;
if (!ips) return false;
std::getline(ips, line);
if (line.empty()) return false;
boost::algorithm::trim(line);
std::vector<std::string> s;
boost::algorithm::split(s, line, boost::algorithm::is_any_of(" "), boost::algorithm::token_compress_on);
if (s.size() < 3) return false;
return s.at(2) == "RUNNING";
} else {
LOG(error) << "this_process did not contain USER env value.";
return false;
}
}
bool DockerUtility::running(std::string_view container, std::error_code &error) {
#ifdef __linux__
using namespace boost::process;
boost::process::ipstream stream;
std::ostringstream oss;
oss << "docker ps -q --filter \"name=" << container << "\" --filter \"status=running\"";
system(oss.str(), boost::process::std_out > stream, error);
if (error) return false;
bool ret = false;
std::string line;
while (std::getline(stream, line) && !line.empty()) {
ret = true;
break;
}
return ret;
#else
return false;
#endif
}
std::string NativeUtility::currentExecutable() {
// win32 GetModuleFileNameA
#ifdef __linux__
char buffer[512] = {0};
auto status = readlink("/proc/self/exe", buffer, sizeof(buffer));
return status == -1 ? std::string() : std::string(buffer);
#else
char buffer[MAX_PATH];
boost::winapi::get_module_file_name(nullptr, buffer, MAX_PATH);
return buffer;
#endif
}
std::string NativeUtility::executableDirectory() {
auto path = currentExecutable();
#ifdef __linux__
auto slashPos = path.find_last_of("/");
#else
auto slashPos = path.find_last_of("\\");
#endif
return path.substr(0, slashPos);
}
std::string NativeUtility::applicationDataDirectory(bool create) {
std::string directory;
#ifdef WIN32
DWORD bufferSize = GetEnvironmentVariable("APPDATA", 0, 0);
char *buffer = new char[bufferSize + 2];
GetEnvironmentVariable("APPDATA", buffer, bufferSize);
directory.assign(buffer);
delete[] buffer;
#else
const char *result = std::getenv("XDG_CONFIG_HOME");
if (result && strlen(result)) {
directory = std::string(result);
} else {
directory = std::string(std::getenv("HOME")) + std::string("/.config/");
}
#endif
auto exePath = currentExecutable();
auto slashPos = exePath.find_last_of("\\");
auto dotPos = exePath.find_last_of(".");
auto appName = exePath.substr(slashPos + 1, dotPos - slashPos - 1);
std::ostringstream oss;
oss << directory << "/" << appName << "/";
directory = oss.str();
if (create) {
try {
std::filesystem::path path(std::filesystem::u8path(directory));
if (!std::filesystem::exists(path)) {
std::filesystem::create_directories(path);
}
} catch (...) {
}
}
return directory;
}
std::string NativeUtility::homeDirectory() {
std::string directory;
#ifdef WIN32
DWORD bufferSize = GetEnvironmentVariable("USERPROFILE", 0, 0);
char *buffer = new char[bufferSize + 2];
GetEnvironmentVariable("USERPROFILE", buffer, bufferSize);
directory.assign(buffer);
delete[] buffer;
#else
directory = std::string(std::getenv("HOME"));
#endif
return directory;
}
bool NativeUtility::processRunning(const std::string &nameFilter) {
boost::process::ipstream ips;
// for unknow reason,on gdb debug mode,the columns is very short.
boost::process::system("ps -eF --columns 999", boost::process::std_out > ips);
std::string line;
std::regex regex(nameFilter);
while (std::getline(ips, line)) {
if (line.empty()) continue;
// LOG(debug) << "processRunning: " << line;
if (std::regex_search(line, regex)) {
return true;
}
}
return false;
}
void DockerUtility::executeBashCommand(const std::string &command) {
std::ostringstream oss;
oss << "bash -c \"" << command << "\"";
boost::process::system("bash -c \" \"");
// ps -aux | grep rosbridge | awk '{print $2}' | xargs kill -s 9 2>/dev/null
}
void DockerUtility::execute(const std::string_view &container, const std::string_view &command) {
std::ostringstream oss;
auto env = boost::this_process::environment();
auto user = env["USER"].to_string();
oss << "docker exec -u " << user << " -d " << container << " " << command;
using namespace boost::process;
system(oss.str());
}
void DockerUtility::kill(const std::string &nameFilter) {
std::ostringstream oss;
oss << "ps -aux | grep " << nameFilter << " | awk '{print $2}' | xargs kill -s 9 2>/dev/null";
executeBashCommand(oss.str());
}
bool DockerUtility::containerExisted(const std::string_view &name) {
boost::process::ipstream ips;
std::ostringstream oss;
oss << "docker ps -aq --filter name=" << name;
boost::process::system(oss.str(), boost::process::std_out > ips);
std::string line;
while (std::getline(ips, line)) {
if (line.empty()) continue;
return true;
}
return false;
}
void DockerUtility::supervisorctl(const std::string_view &container, const std::string &module, bool isStart) {
using namespace boost::process;
auto docker = search_path("docker");
auto env = boost::this_process::environment();
auto user = env["USER"].to_string();
system(docker, args = {"exec", "-u", user, container.data(), "supervisorctl", isStart ? "start" : "stop", module});
}
void DockerUtility::stop(const std::string &nameFilter) {
using namespace boost::process;
ipstream ips;
std::ostringstream oss;
oss << "docker ps -q --filter \"name=" << nameFilter << "\" --filter \"status=running\" ";
system(oss.str(), std_out > ips);
std::string id;
auto docker = search_path("docker");
while (std::getline(ips, id) && !id.empty()) {
system(docker, "stop", args += {id});
}
}

View File

@ -0,0 +1,54 @@
#ifndef DOCKERUTILITY_H
#define DOCKERUTILITY_H
#include <optional>
#include <string>
#include <string_view>
#include <system_error>
/**
* @brief used when app run on the native machine
*/
class DockerUtility {
public:
void stop(const std::string &nameFilter);
static bool supervisorStatus(std::string_view container, const std::string &module);
template <typename... Args>
static void runScript(std::string_view container, const std::string &script, Args &&...scriptArgs);
static bool running(std::string_view container, std::error_code &error);
static void executeBashCommand(const std::string &command);
static void execute(const std::string_view &container, const std::string_view &command);
static void kill(const std::string &nameFilter);
static bool containerExisted(const std::string_view &name);
/**
* @brief supervisorctl
* @param module
* @param isStart true:start,false:stop
*/
static void supervisorctl(const std::string_view &container, const std::string &module, bool isStart);
private:
DockerUtility() = default;
};
class NativeUtility {
public:
using ShellCommand = std::true_type;
template <typename... Args>
static void runScript(const std::string &script, Args &&...scriptArgs);
static bool processRunning(const std::string &nameFilter);
static std::string currentExecutable();
static std::string executableDirectory();
static std::string applicationDataDirectory(bool create = true);
static std::string homeDirectory();
private:
NativeUtility() = default;
};
#include "ProcessUtility.inl"
#endif // DOCKERUTILITY_H

View File

@ -0,0 +1,44 @@
#ifndef __PROCESSUTILITY_INL__
#define __PROCESSUTILITY_INL__
#include "BoostLog.h"
#include "ProcessUtility.h"
#include <boost/algorithm/string/trim.hpp>
#include <boost/process.hpp>
#if (defined __arm__) || (defined __aarch64__)
#include <experimental/filesystem>
#else
#include <filesystem>
#endif
template <typename... Args>
void DockerUtility::runScript(std::string_view container, const std::string &script, Args &&...scriptArgs) {
using namespace boost::process;
auto env = boost::this_process::environment();
auto user = env["USER"].to_string();
auto docker = search_path("docker");
system(docker,
args = {"exec", "-d", "-u", user, container.data(), "bash", script, std::forward<Args>(scriptArgs)...});
}
template <typename... Args>
void NativeUtility::runScript(const std::string &script, Args &&...scriptArgs) {
#if (defined __arm__) || (defined __aarch64__)
using namespace std::experimental;
#else
using namespace std;
#endif
#if defined(WIN32) || defined(ANDROID)
LOG(info) << "DockerUtility::runScript() not supported on windows or android.";
#else
auto askpass = filesystem::current_path().parent_path() / "Askpass/Askpassd.AppImage";
auto env = boost::this_process::environment();
env["SUDO_ASKPASS"] = askpass;
auto bash = boost::process::search_path("bash");
boost::process::system(bash, boost::process::args = {script, std::forward<Args>(scriptArgs)...}, env);
#endif
}
#endif // __PROCESSUTILITY_INL__

View File

@ -0,0 +1,66 @@
#include "SingletonProcess.h"
#include "BoostLog.h"
#ifdef WIN32
#include "Windows.h"
#else
#include <sys/file.h>
#endif
SingletonProcess::SingletonProcess(const std::string_view &name) {
#ifdef WIN32
m_handle = CreateMutex(NULL, FALSE, name.data());
if (m_handle) {
if (ERROR_ALREADY_EXISTS == GetLastError()) {
m_alreadyRun = true;
}
}
#else
std::ostringstream oss;
oss << "/tmp/" << name;
int pidFile = open(oss.str().data(), O_CREAT | O_RDWR, 0666);
m_handle = flock(pidFile, LOCK_EX | LOCK_NB);
if (m_handle) {
if (EWOULDBLOCK == errno) m_alreadyRun = true;
}
#endif
if (!m_alreadyRun) {
boost::interprocess::message_queue::remove(name.data());
m_messageQueue = std::make_unique<boost::interprocess::message_queue>(boost::interprocess::create_only,
name.data(), 64, 1024);
m_thread = std::make_unique<std::thread>(&SingletonProcess::run, this);
} else {
m_messageQueue =
std::make_unique<boost::interprocess::message_queue>(boost::interprocess::open_only, name.data());
}
}
bool SingletonProcess::alreadyRun() const {
return m_alreadyRun;
}
SingletonProcess::~SingletonProcess() {
m_exit = true;
m_conditionVariable.notify_all();
if (m_thread && m_thread->joinable()) m_thread->join();
}
bool SingletonProcess::sendMessage(const char *data, size_t size) {
if (!m_alreadyRun) return false;
return m_messageQueue->try_send(data, size, 0);
}
void SingletonProcess::run() {
char buffer[1024];
while (!m_exit) {
unsigned int priority;
boost::interprocess::message_queue::size_type receivedSize;
bool status = m_messageQueue->try_receive(buffer, sizeof(buffer), receivedSize, priority);
if (status) {
messageHandler(buffer, receivedSize);
} else {
std::unique_lock locker(m_mutex);
m_conditionVariable.wait_for(locker, std::chrono::milliseconds(500));
}
}
}

View File

@ -0,0 +1,39 @@
#ifndef SINGLETONPROCESS_H
#define SINGLETONPROCESS_H
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/signals2/signal.hpp>
#include <condition_variable>
#include <string_view>
#include <thread>
class SingletonProcess
{
public:
using MessageHandler = boost::signals2::signal<void(const char *, size_t)>;
using MessageHandlerSlot = MessageHandler::slot_type;
SingletonProcess(const std::string_view &name);
bool alreadyRun() const;
MessageHandler messageHandler;
~SingletonProcess();
bool sendMessage(const char *data, size_t size);
protected:
void run();
private:
#if WIN32
void *m_handle{nullptr}; // windows HANDLE
#else
int m_handle{-1};
#endif
bool m_alreadyRun{false};
std::unique_ptr<boost::interprocess::message_queue> m_messageQueue;
std::unique_ptr<std::thread> m_thread;
std::condition_variable m_conditionVariable;
std::mutex m_mutex;
bool m_exit{false};
};
#endif // SINGLETONPROCESS_H