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:
23
engine/include/XCEngine/Threading/LambdaTask.h
Normal file
23
engine/include/XCEngine/Threading/LambdaTask.h
Normal 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
|
||||
26
engine/include/XCEngine/Threading/Mutex.h
Normal file
26
engine/include/XCEngine/Threading/Mutex.h
Normal 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
|
||||
53
engine/include/XCEngine/Threading/ReadWriteLock.h
Normal file
53
engine/include/XCEngine/Threading/ReadWriteLock.h
Normal 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
|
||||
32
engine/include/XCEngine/Threading/SpinLock.h
Normal file
32
engine/include/XCEngine/Threading/SpinLock.h
Normal 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
|
||||
61
engine/include/XCEngine/Threading/Task.h
Normal file
61
engine/include/XCEngine/Threading/Task.h
Normal 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
|
||||
51
engine/include/XCEngine/Threading/TaskGroup.h
Normal file
51
engine/include/XCEngine/Threading/TaskGroup.h
Normal 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
|
||||
108
engine/include/XCEngine/Threading/TaskSystem.h
Normal file
108
engine/include/XCEngine/Threading/TaskSystem.h
Normal 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
|
||||
17
engine/include/XCEngine/Threading/TaskSystemConfig.h
Normal file
17
engine/include/XCEngine/Threading/TaskSystemConfig.h
Normal 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
|
||||
43
engine/include/XCEngine/Threading/Thread.h
Normal file
43
engine/include/XCEngine/Threading/Thread.h
Normal 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
|
||||
17
engine/include/XCEngine/Threading/Threading.h
Normal file
17
engine/include/XCEngine/Threading/Threading.h
Normal 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
|
||||
Reference in New Issue
Block a user