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
NetworkUtility.h NetworkUtility.cpp
ProxyHttpSession.h ProxyHttpSession.cpp
@ -11,8 +13,11 @@ add_library(HttpProxy
target_include_directories(HttpProxy
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${OPENSSL_INCLUDE_DIR}
)
target_link_libraries(HttpProxy
PUBLIC ${Boost_LIBRARIES}
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
BoostLog.h BoostLog.inl BoostLog.cpp
BufferUtility.h BufferUtility.cpp
CommandLineInterpreter.h CommandLineInterpreter.cpp
DateTime.h DateTime.cpp
FunctionTraits.h
IoContext.h IoContext.cpp
MessageManager.h MessageManager.inl MessageManager.cpp
ProcessUtility.h ProcessUtility.inl ProcessUtility.cpp
Singleton.h
SingletonProcess.h SingletonProcess.cpp
StreamFormat.h StreamFormat.inl StreamFormat.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