290 lines
6.7 KiB
C++
290 lines
6.7 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
#include <memory>
|
|
#include <functional>
|
|
#include <atomic>
|
|
#include <vector>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <condition_variable>
|
|
#include <queue>
|
|
#include <stop_token>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <chrono>
|
|
#include <type_traits>
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include <ranges>
|
|
#include <bit>
|
|
#include <syncstream>
|
|
#include <future>
|
|
|
|
namespace reactor
|
|
{
|
|
|
|
// Use compiler intrinsics for efficient byte swapping
|
|
#if defined(_MSC_VER)
|
|
#include <stdlib.h>
|
|
#define bswap_64(x) _byteswap_uint64(x)
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
#define bswap_64(x) __builtin_bswap64(x)
|
|
#else
|
|
// Generic fallback implementation
|
|
inline uint64_t bswap_64(uint64_t val)
|
|
{
|
|
uint64_t temp = val;
|
|
char* ptr = reinterpret_cast<char*>(&temp);
|
|
std::reverse(ptr, ptr + sizeof(uint64_t));
|
|
return temp;
|
|
}
|
|
#endif
|
|
|
|
// NonCopyable base class (unchanged, follows modern practice)
|
|
class NonCopyable
|
|
{
|
|
protected:
|
|
NonCopyable() = default;
|
|
~NonCopyable() = default;
|
|
NonCopyable(const NonCopyable&) = delete;
|
|
NonCopyable& operator=(const NonCopyable&) = delete;
|
|
NonCopyable(NonCopyable&&) noexcept = default;
|
|
NonCopyable& operator=(NonCopyable&&) noexcept = default;
|
|
};
|
|
|
|
// C++20 Network byte order utilities
|
|
inline uint64_t hton64(uint64_t host_uint64)
|
|
{
|
|
if constexpr (std::endian::native == std::endian::little) {
|
|
return bswap_64(host_uint64);
|
|
} else {
|
|
return host_uint64;
|
|
}
|
|
}
|
|
|
|
inline uint64_t ntoh64(uint64_t net_uint64)
|
|
{
|
|
return hton64(net_uint64);
|
|
}
|
|
|
|
|
|
// Object Pool (unchanged, this is a standard pattern)
|
|
template<typename T>
|
|
class ObjectPool : public NonCopyable, public std::enable_shared_from_this<ObjectPool<T>>
|
|
{
|
|
private:
|
|
std::vector<std::unique_ptr<T>> objects_;
|
|
std::mutex mutex_;
|
|
|
|
public:
|
|
std::shared_ptr<T> getObject()
|
|
{
|
|
static_assert(!std::is_pointer_v<T>, "ObjectPool type cannot be a pointer");
|
|
|
|
std::unique_ptr<T> p = nullptr;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
if (!objects_.empty()) {
|
|
p = std::move(objects_.back());
|
|
objects_.pop_back();
|
|
}
|
|
}
|
|
|
|
if (!p) {
|
|
p = std::make_unique<T>();
|
|
}
|
|
|
|
std::weak_ptr<ObjectPool<T>> weakPtr = this->shared_from_this();
|
|
return std::shared_ptr<T>(p.release(), [weakPtr](T* ptr) {
|
|
auto self = weakPtr.lock();
|
|
if (self) {
|
|
std::lock_guard<std::mutex> lock(self->mutex_);
|
|
self->objects_.push_back(std::unique_ptr<T>(ptr));
|
|
} else {
|
|
delete ptr;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
enum class LogLevel { TRACE, DEBUG, INFO, WARN, ERROR, FATAL };
|
|
|
|
class Logger : public NonCopyable
|
|
{
|
|
private:
|
|
static inline LogLevel level_ = LogLevel::INFO;
|
|
static inline std::unique_ptr<std::ofstream> file_;
|
|
|
|
std::ostringstream stream_;
|
|
LogLevel msgLevel_;
|
|
|
|
static const char* levelString(LogLevel level)
|
|
{
|
|
switch (level) {
|
|
case LogLevel::TRACE: return "TRACE";
|
|
case LogLevel::DEBUG: return "DEBUG";
|
|
case LogLevel::INFO: return "INFO ";
|
|
case LogLevel::WARN: return "WARN ";
|
|
case LogLevel::ERROR: return "ERROR";
|
|
case LogLevel::FATAL: return "FATAL";
|
|
}
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
static std::string timestamp()
|
|
{
|
|
auto now = std::chrono::system_clock::now();
|
|
auto time_t = std::chrono::system_clock::to_time_t(now);
|
|
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
now.time_since_epoch()) % 1000;
|
|
|
|
char buf[64];
|
|
std::strftime(buf, sizeof(buf), "%Y%m%d %H:%M:%S", std::localtime(&time_t));
|
|
return std::string(buf) + "." + std::to_string(ms.count());
|
|
}
|
|
|
|
public:
|
|
Logger(LogLevel level) : msgLevel_(level)
|
|
{
|
|
if (level >= level_) {
|
|
stream_ << timestamp() << " " << levelString(level) << " ";
|
|
}
|
|
}
|
|
|
|
~Logger()
|
|
{
|
|
if (msgLevel_ >= level_) {
|
|
stream_ << "\n";
|
|
// std::osyncstream handles synchronized, atomic writes
|
|
if (file_ && file_->is_open()) {
|
|
std::osyncstream(*file_) << stream_.str();
|
|
} else {
|
|
std::osyncstream(std::cout) << stream_.str();
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
Logger& operator<<(const T& value)
|
|
{
|
|
if (msgLevel_ >= level_) {
|
|
stream_ << value;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
static void setLevel(LogLevel level) { level_ = level; }
|
|
static void setLogFile(const std::string& filename)
|
|
{
|
|
file_ = std::make_unique<std::ofstream>(filename, std::ios::app);
|
|
}
|
|
};
|
|
|
|
// C++20 Concurrent Task Queue using jthread and stop_token
|
|
class ConcurrentTaskQueue : public NonCopyable
|
|
{
|
|
private:
|
|
std::vector<std::jthread> threads_;
|
|
std::queue<std::function<void()>> taskQueue_;
|
|
std::mutex mutex_;
|
|
std::condition_variable_any taskCond_;
|
|
std::stop_source stopSource_;
|
|
std::string name_;
|
|
|
|
void workerThread(std::stop_token token)
|
|
{
|
|
while (!token.stop_requested()) {
|
|
std::function<void()> task;
|
|
{
|
|
std::unique_lock<std::mutex> lock(mutex_);
|
|
taskCond_.wait(lock, token, [this] { return !taskQueue_.empty(); });
|
|
|
|
if (token.stop_requested() && taskQueue_.empty()) {
|
|
return;
|
|
}
|
|
|
|
task = std::move(taskQueue_.front());
|
|
taskQueue_.pop();
|
|
}
|
|
task();
|
|
}
|
|
}
|
|
|
|
public:
|
|
ConcurrentTaskQueue(size_t threadNum, const std::string& name = "ConcurrentTaskQueue")
|
|
: name_(name)
|
|
{
|
|
for (size_t i = 0; i < threadNum; ++i) {
|
|
threads_.emplace_back(&ConcurrentTaskQueue::workerThread, this, stopSource_.get_token());
|
|
}
|
|
}
|
|
|
|
~ConcurrentTaskQueue()
|
|
{
|
|
stopSource_.request_stop();
|
|
taskCond_.notify_all();
|
|
// std::jthread destructors automatically join
|
|
}
|
|
|
|
template<typename F>
|
|
void runTaskInQueue(F&& task)
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
if (stopSource_.stop_requested()) return;
|
|
taskQueue_.emplace(std::forward<F>(task));
|
|
}
|
|
taskCond_.notify_one();
|
|
}
|
|
|
|
template<typename F>
|
|
void syncTaskInQueue(F&& task)
|
|
{
|
|
auto promise = std::make_shared<std::promise<void>>();
|
|
auto future = promise->get_future();
|
|
runTaskInQueue([promise, task = std::forward<F>(task)]() mutable
|
|
{
|
|
task();
|
|
promise->set_value();
|
|
});
|
|
future.wait();
|
|
}
|
|
|
|
std::string getName() const { return name_; }
|
|
|
|
size_t getTaskCount()
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
return taskQueue_.size();
|
|
}
|
|
};
|
|
|
|
// Logging macros (unchanged)
|
|
#define LOG_TRACE reactor::Logger(reactor::LogLevel::TRACE)
|
|
#define LOG_DEBUG reactor::Logger(reactor::LogLevel::DEBUG)
|
|
#define LOG_INFO reactor::Logger(reactor::LogLevel::INFO)
|
|
#define LOG_WARN reactor::Logger(reactor::LogLevel::WARN)
|
|
#define LOG_ERROR reactor::Logger(reactor::LogLevel::ERROR)
|
|
#define LOG_FATAL reactor::Logger(reactor::LogLevel::FATAL)
|
|
|
|
// Utility functions
|
|
template<typename T>
|
|
void hashCombine(std::size_t& seed, const T& value)
|
|
{
|
|
std::hash<T> hasher;
|
|
seed ^= hasher(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
|
}
|
|
|
|
// C++20 string splitting using ranges
|
|
inline std::vector<std::string> splitString(const std::string& s, const std::string& delimiter)
|
|
{
|
|
std::vector<std::string> result;
|
|
for (const auto& range : std::views::split(s, delimiter)) {
|
|
result.emplace_back(range.begin(), range.end());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace reactor
|