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:
2026-03-13 20:37:08 +08:00
parent 508ee0bdc8
commit 34c75e7129
42 changed files with 3370 additions and 1 deletions

View File

@@ -0,0 +1,23 @@
#pragma once
#include "Task.h"
namespace XCEngine {
namespace Threading {
template<typename Func>
class LambdaTask : public ITask {
public:
explicit LambdaTask(Func&& func, TaskPriority priority = TaskPriority::Normal)
: ITask(priority), m_func(std::move(func)) {}
void Execute() override {
m_func();
}
private:
Func m_func;
};
} // namespace Threading
} // namespace XCEngine

View File

@@ -0,0 +1,26 @@
#pragma once
#include <mutex>
namespace XCEngine {
namespace Threading {
class Mutex {
public:
Mutex() = default;
~Mutex() = default;
void Lock() { m_mutex.lock(); }
void Unlock() { m_mutex.unlock(); }
bool TryLock() { return m_mutex.try_lock(); }
void lock() { Lock(); }
void unlock() { Unlock(); }
bool try_lock() { return TryLock(); }
private:
std::mutex m_mutex;
};
} // namespace Threading
} // namespace XCEngine

View File

@@ -0,0 +1,53 @@
#pragma once
#include <mutex>
#include <condition_variable>
namespace XCEngine {
namespace Threading {
class ReadWriteLock {
public:
ReadWriteLock() = default;
~ReadWriteLock() = default;
void ReadLock() {
std::unique_lock<std::mutex> lock(m_mutex);
m_readCondition.wait(lock, [this] { return !m_writerActive && m_writersWaiting == 0; });
++m_readers;
}
void ReadUnlock() {
std::unique_lock<std::mutex> lock(m_mutex);
--m_readers;
if (m_readers == 0) {
m_writeCondition.notify_all();
}
}
void WriteLock() {
std::unique_lock<std::mutex> lock(m_mutex);
++m_writersWaiting;
m_writeCondition.wait(lock, [this] { return m_readers == 0 && !m_writerActive; });
--m_writersWaiting;
m_writerActive = true;
}
void WriteUnlock() {
std::unique_lock<std::mutex> lock(m_mutex);
m_writerActive = false;
m_readCondition.notify_all();
m_writeCondition.notify_one();
}
private:
std::mutex m_mutex;
std::condition_variable m_readCondition;
std::condition_variable m_writeCondition;
int32_t m_readers = 0;
int32_t m_writersWaiting = 0;
bool m_writerActive = false;
};
} // namespace Threading
} // namespace XCEngine

View File

@@ -0,0 +1,32 @@
#pragma once
#include <atomic>
namespace XCEngine {
namespace Threading {
class SpinLock {
public:
void Lock() {
while (m_flag.test_and_set(std::memory_order_acquire)) {
}
}
void Unlock() {
m_flag.clear(std::memory_order_release);
}
bool TryLock() {
return !m_flag.test_and_set(std::memory_order_acquire);
}
void lock() { Lock(); }
void unlock() { Unlock(); }
bool try_lock() { return TryLock(); }
private:
std::atomic_flag m_flag = ATOMIC_FLAG_INIT;
};
} // namespace Threading
} // namespace XCEngine

View File

@@ -0,0 +1,61 @@
#pragma once
#include <atomic>
#include <functional>
#include <cstdint>
namespace XCEngine {
namespace Threading {
enum class TaskPriority : uint8_t {
Critical = 0,
High = 1,
Normal = 2,
Low = 3,
Idle = 4
};
enum class TaskStatus : uint8_t {
Pending,
Scheduled,
Running,
Completed,
Failed,
Canceled
};
class ITask {
public:
virtual ~ITask() = default;
virtual void Execute() = 0;
virtual void OnComplete() {}
virtual void OnCancel() {}
TaskPriority GetPriority() const { return m_priority; }
TaskStatus GetStatus() const { return m_status.load(); }
uint64_t GetId() const { return m_id; }
void SetId(uint64_t id) { m_id = id; }
void SetPriority(TaskPriority priority) { m_priority = priority; }
void SetStatus(TaskStatus status) { m_status = status; }
void AddRef() { m_refCount.fetch_add(1); }
void Release() {
if (m_refCount.fetch_sub(1) == 1) {
delete this;
}
}
protected:
ITask() = default;
explicit ITask(TaskPriority priority) : m_priority(priority) {}
TaskPriority m_priority = TaskPriority::Normal;
std::atomic<TaskStatus> m_status{TaskStatus::Pending};
uint64_t m_id = 0;
std::atomic<uint32_t> m_refCount{1};
};
} // namespace Threading
} // namespace XCEngine

View File

@@ -0,0 +1,51 @@
#pragma once
#include "Task.h"
#include <vector>
#include <functional>
#include <mutex>
#include <atomic>
#include <condition_variable>
namespace XCEngine {
namespace Threading {
class TaskGroup {
public:
using Callback = std::function<void()>;
TaskGroup();
~TaskGroup();
uint64_t AddTask(std::unique_ptr<ITask> task);
uint64_t AddTask(Callback&& func, TaskPriority priority = TaskPriority::Normal);
void AddDependency(uint64_t taskId, uint64_t dependsOn);
void Wait();
bool WaitFor(std::chrono::milliseconds timeout);
void SetCompleteCallback(Callback&& callback);
bool IsComplete() const;
float GetProgress() const;
void Cancel();
private:
struct TaskNode {
ITask* task = nullptr;
std::vector<uint64_t> dependencies;
int pendingDepCount = 0;
bool completed = false;
};
std::vector<TaskNode> m_tasks;
std::atomic<int> m_pendingCount{0};
std::atomic<int> m_completedCount{0};
Callback m_completeCallback;
mutable std::mutex m_mutex;
std::condition_variable m_condition;
std::atomic<bool> m_canceled{false};
};
} // namespace Threading
} // namespace XCEngine

View File

@@ -0,0 +1,108 @@
#pragma once
#include "TaskSystemConfig.h"
#include "Task.h"
#include "TaskGroup.h"
#include "Mutex.h"
#include "SpinLock.h"
#include <vector>
#include <queue>
#include <thread>
#include <atomic>
#include <condition_variable>
#include <functional>
namespace XCEngine {
namespace Threading {
class TaskSystem {
public:
static TaskSystem& Get();
void Initialize(const TaskSystemConfig& config);
void Shutdown();
uint64_t Submit(std::unique_ptr<ITask> task);
uint64_t Submit(std::function<void()>&& func, TaskPriority priority = TaskPriority::Normal);
TaskGroup* CreateTaskGroup();
void DestroyTaskGroup(TaskGroup* group);
void Wait(uint64_t taskId);
uint32_t GetWorkerThreadCount() const;
void Update();
template<typename Func>
void ParallelFor(int32_t start, int32_t end, Func&& func);
void RunOnMainThread(std::function<void()>&& func);
private:
TaskSystem() = default;
~TaskSystem() = default;
struct TaskWrapper {
ITask* task;
TaskPriority priority;
uint64_t id;
bool operator<(const TaskWrapper& other) const {
return priority > other.priority;
}
};
void WorkerThread();
bool GetNextTask(TaskWrapper& outTask);
void ExecuteTask(TaskWrapper& task);
std::vector<std::thread> m_workerThreads;
std::priority_queue<TaskWrapper> m_taskQueue;
std::vector<TaskGroup*> m_taskGroups;
std::vector<std::function<void()>> m_mainThreadQueue;
Mutex m_queueMutex;
std::mutex m_conditionMutex;
SpinLock m_groupMutex;
std::condition_variable m_taskAvailable;
std::condition_variable m_mainThreadCondition;
std::atomic<bool> m_running{false};
std::atomic<uint64_t> m_nextTaskId{0};
uint32_t m_workerThreadCount = 0;
bool m_shutdown = false;
};
template<typename Func>
void TaskSystem::ParallelFor(int32_t start, int32_t end, Func&& func) {
int32_t count = end - start;
if (count <= 0) return;
uint32_t numThreads = std::thread::hardware_concurrency();
if (numThreads == 0) numThreads = 2;
int32_t chunkSize = (count + numThreads - 1) / numThreads;
if (chunkSize < 1) chunkSize = 1;
auto parallelTask = [&func, start, chunkSize, count](int32_t threadIndex) {
int32_t begin = start + threadIndex * chunkSize;
int32_t endIndex = std::min(begin + chunkSize, start + count);
for (int32_t i = begin; i < endIndex; ++i) {
func(i);
}
};
std::vector<std::function<void()>> tasks;
tasks.reserve(numThreads);
for (uint32_t i = 0; i < numThreads; ++i) {
tasks.emplace_back([=]() { parallelTask(i); });
}
for (auto& task : tasks) {
Submit(std::make_unique<LambdaTask<std::function<void()>>>(std::move(task), TaskPriority::High));
}
}
} // namespace Threading
} // namespace XCEngine

View File

@@ -0,0 +1,17 @@
#pragma once
#include <cstdint>
namespace XCEngine {
namespace Threading {
struct TaskSystemConfig {
uint32_t workerThreadCount = 0;
bool enableTaskProfiling = true;
bool stealTasks = true;
uint32_t maxTaskQueueSize = 1024;
uint32_t threadStackSize = 0;
};
} // namespace Threading
} // namespace XCEngine

View File

@@ -0,0 +1,43 @@
#pragma once
#include <thread>
#include <atomic>
#include "Containers/String.h"
namespace XCEngine {
namespace Threading {
class Thread {
public:
using Id = uint64_t;
Thread();
~Thread();
template<typename Func>
void Start(Func&& func, const Containers::String& name = "Thread");
void Join();
void Detach();
Id GetId() const { return m_id; }
const Containers::String& GetName() const { return m_name; }
static Id GetCurrentId();
static void Sleep(uint32_t milliseconds);
static void Yield();
private:
Id m_id = 0;
Containers::String m_name;
std::thread m_thread;
};
template<typename Func>
void Thread::Start(Func&& func, const Containers::String& name) {
m_name = name;
m_thread = std::thread(std::forward<Func>(func));
m_id = static_cast<Id>(reinterpret_cast<uintptr_t>(m_thread.native_handle()));
}
} // namespace Threading
} // namespace XCEngine

View File

@@ -0,0 +1,17 @@
#pragma once
#include "Threading/Mutex.h"
#include "Threading/SpinLock.h"
#include "Threading/ReadWriteLock.h"
#include "Threading/Thread.h"
#include "Threading/Task.h"
#include "Threading/LambdaTask.h"
#include "Threading/TaskGroup.h"
#include "Threading/TaskSystemConfig.h"
#include "Threading/TaskSystem.h"
namespace XCEngine {
namespace Threading {
} // namespace Threading
} // namespace XCEngine