347 lines
7.7 KiB
C++
347 lines
7.7 KiB
C++
#pragma once
|
|
|
|
#include <future>
|
|
#include <string>
|
|
#include <memory>
|
|
#include <functional>
|
|
#include <atomic>
|
|
#include <vector>
|
|
#include <queue>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <condition_variable>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <chrono>
|
|
#include <type_traits>
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <netinet/in.h>
|
|
#include <sstream>
|
|
|
|
namespace reactor {
|
|
|
|
// NonCopyable base class
|
|
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;
|
|
};
|
|
|
|
// Network byte order utilities
|
|
inline uint64_t hton64(uint64_t n)
|
|
{
|
|
static const int one = 1;
|
|
static const char sig = *(char*)&one;
|
|
if (sig == 0) return n;
|
|
char* ptr = reinterpret_cast<char*>(&n);
|
|
std::reverse(ptr, ptr + sizeof(uint64_t));
|
|
return n;
|
|
}
|
|
|
|
inline uint64_t ntoh64(uint64_t n) { return hton64(n); }
|
|
|
|
// Lock-free MPSC queue
|
|
template<typename T>
|
|
class LockFreeQueue : public NonCopyable
|
|
{
|
|
private:
|
|
struct Node
|
|
{
|
|
Node() = default;
|
|
Node(const T& data) : data_(std::make_unique<T>(data)) {}
|
|
Node(T&& data) : data_(std::make_unique<T>(std::move(data))) {}
|
|
std::unique_ptr<T> data_;
|
|
std::atomic<Node*> next_{nullptr};
|
|
};
|
|
|
|
std::atomic<Node*> head_;
|
|
std::atomic<Node*> tail_;
|
|
|
|
public:
|
|
LockFreeQueue() : head_(new Node), tail_(head_.load()) {}
|
|
|
|
~LockFreeQueue()
|
|
{
|
|
T output;
|
|
while (dequeue(output)) {}
|
|
delete head_.load();
|
|
}
|
|
|
|
void enqueue(T&& input)
|
|
{
|
|
Node* node = new Node(std::move(input));
|
|
Node* prevhead = head_.exchange(node, std::memory_order_acq_rel);
|
|
prevhead->next_.store(node, std::memory_order_release);
|
|
}
|
|
|
|
void enqueue(const T& input)
|
|
{
|
|
Node* node = new Node(input);
|
|
Node* prevhead = head_.exchange(node, std::memory_order_acq_rel);
|
|
prevhead->next_.store(node, std::memory_order_release);
|
|
}
|
|
|
|
bool dequeue(T& output)
|
|
{
|
|
Node* tail = tail_.load(std::memory_order_relaxed);
|
|
Node* next = tail->next_.load(std::memory_order_acquire);
|
|
|
|
if (next == nullptr) return false;
|
|
|
|
output = std::move(*next->data_);
|
|
tail_.store(next, std::memory_order_release);
|
|
delete tail;
|
|
return true;
|
|
}
|
|
|
|
bool empty()
|
|
{
|
|
Node* tail = tail_.load(std::memory_order_relaxed);
|
|
Node* next = tail->next_.load(std::memory_order_acquire);
|
|
return next == nullptr;
|
|
}
|
|
};
|
|
|
|
// Object Pool
|
|
template<typename T>
|
|
class ObjectPool : public NonCopyable, public std::enable_shared_from_this<ObjectPool<T>>
|
|
{
|
|
private:
|
|
std::vector<T*> objects_;
|
|
std::mutex mutex_;
|
|
|
|
public:
|
|
std::shared_ptr<T> getObject()
|
|
{
|
|
static_assert(!std::is_pointer_v<T>, "ObjectPool type cannot be pointer");
|
|
|
|
T* p = nullptr;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
if (!objects_.empty()) {
|
|
p = objects_.back();
|
|
objects_.pop_back();
|
|
}
|
|
}
|
|
|
|
if (!p) p = new T;
|
|
|
|
std::weak_ptr<ObjectPool<T>> weakPtr = this->shared_from_this();
|
|
return std::shared_ptr<T>(p, [weakPtr](T* ptr) {
|
|
auto self = weakPtr.lock();
|
|
if (self) {
|
|
std::lock_guard<std::mutex> lock(self->mutex_);
|
|
self->objects_.push_back(ptr);
|
|
} else {
|
|
delete ptr;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// Simple Logger
|
|
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_;
|
|
static inline std::mutex mutex_;
|
|
|
|
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::lock_guard<std::mutex> lock(mutex_);
|
|
if (file_ && file_->is_open()) {
|
|
*file_ << stream_.str();
|
|
file_->flush();
|
|
} else {
|
|
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)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
file_ = std::make_unique<std::ofstream>(filename, std::ios::app);
|
|
}
|
|
};
|
|
|
|
// Task Queue interface
|
|
class TaskQueue : public NonCopyable
|
|
{
|
|
public:
|
|
virtual ~TaskQueue() = default;
|
|
virtual void runTaskInQueue(const std::function<void()>& task) = 0;
|
|
virtual void runTaskInQueue(std::function<void()>&& task) = 0;
|
|
virtual std::string getName() const { return ""; }
|
|
|
|
void syncTaskInQueue(const std::function<void()>& task)
|
|
{
|
|
std::promise<void> promise;
|
|
auto future = promise.get_future();
|
|
runTaskInQueue([&]() {
|
|
task();
|
|
promise.set_value();
|
|
});
|
|
future.wait();
|
|
}
|
|
};
|
|
|
|
// Concurrent Task Queue
|
|
class ConcurrentTaskQueue : public TaskQueue
|
|
{
|
|
private:
|
|
std::vector<std::thread> threads_;
|
|
std::queue<std::function<void()>> taskQueue_;
|
|
std::mutex taskMutex_;
|
|
std::condition_variable taskCond_;
|
|
std::atomic<bool> stop_{false};
|
|
std::string name_;
|
|
|
|
void workerThread(int threadId)
|
|
{
|
|
while (!stop_) {
|
|
std::function<void()> task;
|
|
{
|
|
std::unique_lock<std::mutex> lock(taskMutex_);
|
|
taskCond_.wait(lock, [this]() { return stop_ || !taskQueue_.empty(); });
|
|
|
|
if (taskQueue_.empty()) continue;
|
|
|
|
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, i);
|
|
}
|
|
}
|
|
|
|
~ConcurrentTaskQueue()
|
|
{
|
|
stop_ = true;
|
|
taskCond_.notify_all();
|
|
for (auto& t : threads_) {
|
|
if (t.joinable()) t.join();
|
|
}
|
|
}
|
|
|
|
void runTaskInQueue(const std::function<void()>& task) override
|
|
{
|
|
std::lock_guard<std::mutex> lock(taskMutex_);
|
|
taskQueue_.push(task);
|
|
taskCond_.notify_one();
|
|
}
|
|
|
|
void runTaskInQueue(std::function<void()>&& task) override
|
|
{
|
|
std::lock_guard<std::mutex> lock(taskMutex_);
|
|
taskQueue_.push(std::move(task));
|
|
taskCond_.notify_one();
|
|
}
|
|
|
|
std::string getName() const override { return name_; }
|
|
|
|
size_t getTaskCount()
|
|
{
|
|
std::lock_guard<std::mutex> lock(taskMutex_);
|
|
return taskQueue_.size();
|
|
}
|
|
};
|
|
|
|
// Logging macros
|
|
#define LOG_TRACE Logger(LogLevel::TRACE)
|
|
#define LOG_DEBUG Logger(LogLevel::DEBUG)
|
|
#define LOG_INFO Logger(LogLevel::INFO)
|
|
#define LOG_WARN Logger(LogLevel::WARN)
|
|
#define LOG_ERROR Logger(LogLevel::ERROR)
|
|
#define LOG_FATAL Logger(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);
|
|
}
|
|
|
|
inline std::vector<std::string> splitString(const std::string& s, const std::string& delimiter)
|
|
{
|
|
std::vector<std::string> result;
|
|
size_t start = 0;
|
|
size_t end = s.find(delimiter);
|
|
|
|
while (end != std::string::npos) {
|
|
result.push_back(s.substr(start, end - start));
|
|
start = end + delimiter.length();
|
|
end = s.find(delimiter, start);
|
|
}
|
|
result.push_back(s.substr(start));
|
|
return result;
|
|
}
|
|
|
|
} // namespace reactor
|