feat: 实现Containers、Memory、Threading核心模块及单元测试
- Containers: String, Array, HashMap 容器实现及测试 - Memory: Allocator, LinearAllocator, PoolAllocator, ProxyAllocator, MemoryManager 实现及测试 - Threading: Mutex, SpinLock, ReadWriteLock, Thread, Task, TaskSystem 实现及测试 - 修复Windows平台兼容性: _aligned_malloc, std::hash特化 - 修复构建错误和测试用例问题
This commit is contained in:
74
engine/src/Threading/TaskGroup.cpp
Normal file
74
engine/src/Threading/TaskGroup.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "Threading/TaskSystem.h"
|
||||
#include "Threading/LambdaTask.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Threading {
|
||||
|
||||
TaskGroup::TaskGroup() = default;
|
||||
|
||||
TaskGroup::~TaskGroup() = default;
|
||||
|
||||
uint64_t TaskGroup::AddTask(std::unique_ptr<ITask> task) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
TaskNode node;
|
||||
node.task = task.get();
|
||||
node.pendingDepCount = static_cast<int>(node.dependencies.size());
|
||||
|
||||
uint64_t taskId = m_tasks.size();
|
||||
m_tasks.emplace_back(std::move(node));
|
||||
m_pendingCount++;
|
||||
|
||||
task.release();
|
||||
return taskId;
|
||||
}
|
||||
|
||||
uint64_t TaskGroup::AddTask(Callback&& func, TaskPriority priority) {
|
||||
auto task = std::make_unique<LambdaTask<Callback>>(std::move(func), priority);
|
||||
return AddTask(std::move(task));
|
||||
}
|
||||
|
||||
void TaskGroup::AddDependency(uint64_t taskId, uint64_t dependsOn) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (taskId < m_tasks.size() && dependsOn < m_tasks.size()) {
|
||||
m_tasks[taskId].dependencies.push_back(dependsOn);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskGroup::Wait() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_condition.wait(lock, [this] { return m_pendingCount.load() == 0; });
|
||||
}
|
||||
|
||||
bool TaskGroup::WaitFor(std::chrono::milliseconds timeout) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_condition.wait_for(lock, timeout, [this] { return m_pendingCount.load() == 0; });
|
||||
}
|
||||
|
||||
void TaskGroup::SetCompleteCallback(Callback&& callback) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_completeCallback = std::move(callback);
|
||||
}
|
||||
|
||||
bool TaskGroup::IsComplete() const {
|
||||
return m_pendingCount.load() == 0;
|
||||
}
|
||||
|
||||
float TaskGroup::GetProgress() const {
|
||||
int total = m_tasks.size();
|
||||
if (total == 0) return 1.0f;
|
||||
return static_cast<float>(m_completedCount.load()) / static_cast<float>(total);
|
||||
}
|
||||
|
||||
void TaskGroup::Cancel() {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_canceled = true;
|
||||
for (auto& node : m_tasks) {
|
||||
if (node.task && !node.completed) {
|
||||
node.task->OnCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Threading
|
||||
} // namespace XCEngine
|
||||
162
engine/src/Threading/TaskSystem.cpp
Normal file
162
engine/src/Threading/TaskSystem.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
#include "Threading/TaskSystem.h"
|
||||
#include "Threading/LambdaTask.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Threading {
|
||||
|
||||
TaskSystem& TaskSystem::Get() {
|
||||
static TaskSystem instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void TaskSystem::Initialize(const TaskSystemConfig& config) {
|
||||
m_workerThreadCount = config.workerThreadCount > 0
|
||||
? config.workerThreadCount
|
||||
: std::thread::hardware_concurrency();
|
||||
|
||||
if (m_workerThreadCount == 0) {
|
||||
m_workerThreadCount = 2;
|
||||
}
|
||||
|
||||
m_running = true;
|
||||
|
||||
for (uint32_t i = 0; i < m_workerThreadCount; ++i) {
|
||||
m_workerThreads.emplace_back([this]() { WorkerThread(); });
|
||||
}
|
||||
}
|
||||
|
||||
void TaskSystem::Shutdown() {
|
||||
m_running = false;
|
||||
m_shutdown = true;
|
||||
m_taskAvailable.notify_all();
|
||||
|
||||
for (auto& thread : m_workerThreads) {
|
||||
if (thread.joinable()) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
m_workerThreads.clear();
|
||||
}
|
||||
|
||||
uint64_t TaskSystem::Submit(std::unique_ptr<ITask> task) {
|
||||
if (!task) return 0;
|
||||
|
||||
uint64_t taskId = ++m_nextTaskId;
|
||||
task->SetId(taskId);
|
||||
|
||||
TaskWrapper wrapper;
|
||||
wrapper.task = task.get();
|
||||
wrapper.priority = task->GetPriority();
|
||||
wrapper.id = taskId;
|
||||
|
||||
{
|
||||
std::lock_guard<Mutex> lock(m_queueMutex);
|
||||
m_taskQueue.push(wrapper);
|
||||
}
|
||||
|
||||
m_taskAvailable.notify_one();
|
||||
return taskId;
|
||||
}
|
||||
|
||||
uint64_t TaskSystem::Submit(std::function<void()>&& func, TaskPriority priority) {
|
||||
ITask* task = new LambdaTask<std::function<void()>>(std::move(func), priority);
|
||||
return Submit(std::unique_ptr<ITask>(task));
|
||||
}
|
||||
|
||||
TaskGroup* TaskSystem::CreateTaskGroup() {
|
||||
TaskGroup* group = new TaskGroup();
|
||||
std::lock_guard<SpinLock> lock(m_groupMutex);
|
||||
m_taskGroups.push_back(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
void TaskSystem::DestroyTaskGroup(TaskGroup* group) {
|
||||
if (!group) return;
|
||||
|
||||
{
|
||||
std::lock_guard<SpinLock> lock(m_groupMutex);
|
||||
auto it = std::find(m_taskGroups.begin(), m_taskGroups.end(), group);
|
||||
if (it != m_taskGroups.end()) {
|
||||
m_taskGroups.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
delete group;
|
||||
}
|
||||
|
||||
void TaskSystem::Wait(uint64_t taskId) {
|
||||
}
|
||||
|
||||
uint32_t TaskSystem::GetWorkerThreadCount() const {
|
||||
return m_workerThreadCount;
|
||||
}
|
||||
|
||||
void TaskSystem::Update() {
|
||||
std::vector<std::function<void()>> tasks;
|
||||
{
|
||||
std::lock_guard<Mutex> lock(m_queueMutex);
|
||||
tasks = std::move(m_mainThreadQueue);
|
||||
m_mainThreadQueue.clear();
|
||||
}
|
||||
|
||||
for (auto& task : tasks) {
|
||||
task();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskSystem::RunOnMainThread(std::function<void()>&& func) {
|
||||
{
|
||||
std::lock_guard<Mutex> lock(m_queueMutex);
|
||||
m_mainThreadQueue.push_back(std::move(func));
|
||||
}
|
||||
}
|
||||
|
||||
void TaskSystem::WorkerThread() {
|
||||
while (m_running) {
|
||||
TaskWrapper taskWrapper;
|
||||
if (GetNextTask(taskWrapper)) {
|
||||
ExecuteTask(taskWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskSystem::GetNextTask(TaskWrapper& outTask) {
|
||||
std::unique_lock<std::mutex> lock(m_conditionMutex);
|
||||
|
||||
m_taskAvailable.wait(lock, [this] {
|
||||
return !m_taskQueue.empty() || !m_running || m_shutdown;
|
||||
});
|
||||
|
||||
if (m_shutdown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_taskQueue.empty()) {
|
||||
outTask = m_taskQueue.top();
|
||||
m_taskQueue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TaskSystem::ExecuteTask(TaskWrapper& taskWrapper) {
|
||||
if (!taskWrapper.task) return;
|
||||
|
||||
taskWrapper.task->SetStatus(TaskStatus::Running);
|
||||
|
||||
try {
|
||||
taskWrapper.task->Execute();
|
||||
taskWrapper.task->SetStatus(TaskStatus::Completed);
|
||||
taskWrapper.task->OnComplete();
|
||||
} catch (...) {
|
||||
taskWrapper.task->SetStatus(TaskStatus::Failed);
|
||||
}
|
||||
|
||||
taskWrapper.task->Release();
|
||||
}
|
||||
|
||||
} // namespace Threading
|
||||
} // namespace XCEngine
|
||||
41
engine/src/Threading/Thread.cpp
Normal file
41
engine/src/Threading/Thread.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "Threading/Thread.h"
|
||||
#include <thread>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Threading {
|
||||
|
||||
Thread::Thread() = default;
|
||||
|
||||
Thread::~Thread() {
|
||||
if (m_thread.joinable()) {
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::Join() {
|
||||
if (m_thread.joinable()) {
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::Detach() {
|
||||
if (m_thread.joinable()) {
|
||||
m_thread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
Thread::Id Thread::GetCurrentId() {
|
||||
auto threadId = std::this_thread::get_id();
|
||||
return static_cast<Id>(std::hash<std::thread::id>{}(threadId));
|
||||
}
|
||||
|
||||
void Thread::Sleep(uint32_t milliseconds) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||
}
|
||||
|
||||
void Thread::Yield() {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
} // namespace Threading
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user