174 lines
4.7 KiB
C++
174 lines
4.7 KiB
C++
export module utils:worker;
|
|
import :lockfree;
|
|
import :logger;
|
|
import std;
|
|
|
|
export namespace ee_utils
|
|
{
|
|
struct WorkerCommand
|
|
{
|
|
std::uint8_t type = 0;
|
|
};
|
|
template <typename T> concept IWorkerCommand = std::is_base_of_v<WorkerCommand, T>;
|
|
|
|
template <IWorkerCommand TCommand, std::size_t MaxCmdTypeId, bool IdleDetection = false>
|
|
class Worker
|
|
{
|
|
public:
|
|
void Request(const TCommand &command)
|
|
{
|
|
commands.push(command); //TODO: filled queue case
|
|
}
|
|
|
|
void Request(const std::uint8_t type)
|
|
{
|
|
commands.push(TCommand {.type = type}); //TODO: filled queue case
|
|
}
|
|
|
|
void ProcessQueue(TCommand &buffer) requires !IdleDetection
|
|
{
|
|
while (this->commands.pop(buffer))
|
|
{
|
|
ProcessCommand(buffer);
|
|
}
|
|
}
|
|
|
|
std::uint32_t ProcessQueue(TCommand &buffer) requires IdleDetection
|
|
{
|
|
thread_local std::uint32_t idleCounter = 0;
|
|
if (!this->commands.pop(buffer))
|
|
{
|
|
idleCounter++;
|
|
return idleCounter;
|
|
}
|
|
idleCounter = 0;
|
|
|
|
do
|
|
{
|
|
ProcessCommand(buffer);
|
|
} while (this->commands.pop(buffer));
|
|
|
|
return 0;
|
|
}
|
|
|
|
protected:
|
|
// TODO: replace std::function with more optimized ref
|
|
std::array<std::function<void(TCommand &command)>, MaxCmdTypeId + 1> commandsMapping;
|
|
// TODO: maybe I should use lockfree::queue ? Otherwise make consumer thread explicit for safety
|
|
boost::lockfree::spsc_queue<TCommand, boost::lockfree::capacity<512>> commands;
|
|
|
|
Worker() {};
|
|
|
|
void ProcessCommand(TCommand &command)
|
|
{
|
|
if (command.type > MaxCmdTypeId || !commandsMapping[command.type])
|
|
{
|
|
LOG(WARNING, "Unhandled command. Type id: %d", command.type);
|
|
command.type = MaxCmdTypeId;
|
|
return;
|
|
}
|
|
|
|
commandsMapping[command.type](command);
|
|
}
|
|
};
|
|
|
|
class ThreadedWorkerBase {
|
|
public:
|
|
std::atomic<bool> isRunning;
|
|
|
|
virtual ~ThreadedWorkerBase() {}
|
|
|
|
void Start()
|
|
{
|
|
if (isRunning.load())
|
|
{
|
|
LOG(CRITICAL, "Thread can be started only once before stop");
|
|
throw std::logic_error("Thread can be started only once before stop");
|
|
}
|
|
|
|
isRunning.store(true);
|
|
thread = std::jthread([this](std::stop_token stop) {
|
|
this->stoken = &stop;
|
|
Threading();
|
|
});
|
|
}
|
|
|
|
void Shutdown()
|
|
{
|
|
if (isRunning.load())
|
|
thread.request_stop();
|
|
else LOG(WARNING, "Tried to shutdown stopped thread");
|
|
}
|
|
|
|
protected:
|
|
std::stop_token *stoken{};
|
|
std::jthread thread;
|
|
|
|
virtual void Threading() = 0;
|
|
};
|
|
template<typename T> concept IThreadedWorker = std::is_base_of_v<ThreadedWorkerBase, T>;
|
|
|
|
enum class IdleBackoffMode : std::uint8_t
|
|
{
|
|
DISABLED,
|
|
YIELD
|
|
};
|
|
|
|
template <IWorkerCommand TCommand, std::size_t MaxCmdType, IdleBackoffMode IdleBackoffMode = IdleBackoffMode::DISABLED>
|
|
class ThreadedWorker : public Worker<TCommand, MaxCmdType, static_cast<bool>(IdleBackoffMode)>, public ThreadedWorkerBase
|
|
{
|
|
protected:
|
|
const wchar_t *threadName;
|
|
|
|
explicit ThreadedWorker(const wchar_t *threadName, std::uint32_t yieldIdleTime = 0)
|
|
{
|
|
this->threadName = threadName;
|
|
this->yieldIdleTime = yieldIdleTime;
|
|
}
|
|
|
|
void Threading()
|
|
{
|
|
Logger::RegisterThread(threadName);
|
|
LOG(INFO, "Thread started");
|
|
|
|
Loop();
|
|
|
|
ReleaseStop();
|
|
}
|
|
|
|
void Loop()
|
|
{
|
|
TCommand commandBuffer{};
|
|
while (!stoken->stop_requested())
|
|
{
|
|
if constexpr (IdleBackoffMode == IdleBackoffMode::DISABLED)
|
|
{
|
|
this->ProcessQueue(commandBuffer);
|
|
}
|
|
else
|
|
{
|
|
const std::uint32_t idleCounter = this->ProcessQueue(commandBuffer);
|
|
|
|
if (idleCounter > yieldIdleTime)
|
|
std::this_thread::yield();
|
|
}
|
|
|
|
Update();
|
|
}
|
|
}
|
|
|
|
virtual void Update() = 0;
|
|
virtual void Release() = 0;
|
|
|
|
private:
|
|
std::uint32_t yieldIdleTime;
|
|
|
|
void ReleaseStop()
|
|
{
|
|
Release();
|
|
LOG(INFO, "Thread is released");
|
|
Logger::UnregisterThread();
|
|
isRunning.store(false);
|
|
}
|
|
};
|
|
} |