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

@@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(XCEngine STATIC
# Math
include/XCEngine/Math/Math.h
include/XCEngine/Math/Vector2.h
include/XCEngine/Math/Vector3.h
@@ -28,6 +29,36 @@ add_library(XCEngine STATIC
src/Math/Color.cpp
src/Math/Geometry.cpp
src/Math/FrustumBounds.cpp
# Containers
include/XCEngine/Containers/Containers.h
include/XCEngine/Containers/Array.h
include/XCEngine/Containers/String.h
include/XCEngine/Containers/HashMap.h
src/Containers/String.cpp
# Memory
include/XCEngine/Memory/Allocator.h
include/XCEngine/Memory/LinearAllocator.h
include/XCEngine/Memory/PoolAllocator.h
include/XCEngine/Memory/ProxyAllocator.h
include/XCEngine/Memory/MemoryManager.h
src/Memory/Memory.cpp
# Threading
include/XCEngine/Threading/Threading.h
include/XCEngine/Threading/Mutex.h
include/XCEngine/Threading/SpinLock.h
include/XCEngine/Threading/ReadWriteLock.h
include/XCEngine/Threading/Thread.h
include/XCEngine/Threading/Task.h
include/XCEngine/Threading/LambdaTask.h
include/XCEngine/Threading/TaskGroup.h
include/XCEngine/Threading/TaskSystemConfig.h
include/XCEngine/Threading/TaskSystem.h
src/Threading/Thread.cpp
src/Threading/TaskGroup.cpp
src/Threading/TaskSystem.cpp
)
target_include_directories(XCEngine PUBLIC

View File

@@ -0,0 +1,292 @@
#pragma once
#include <cstddef>
#include <initializer_list>
#include <algorithm>
#include <stdexcept>
#include "../Memory/Allocator.h"
namespace XCEngine {
namespace Containers {
template<typename T>
class Array {
public:
using Iterator = T*;
using ConstIterator = const T*;
Array() = default;
explicit Array(size_t capacity);
Array(size_t count, const T& value);
Array(std::initializer_list<T> init);
~Array();
Array(const Array& other);
Array(Array&& other) noexcept;
Array& operator=(const Array& other);
Array& operator=(Array&& other) noexcept;
T& operator[](size_t index);
const T& operator[](size_t index) const;
T* Data() { return m_data; }
const T* Data() const { return m_data; }
size_t Size() const { return m_size; }
size_t Capacity() const { return m_capacity; }
bool Empty() const { return m_size == 0; }
void Clear();
void Reserve(size_t capacity);
void Resize(size_t newSize);
void Resize(size_t newSize, const T& value);
void PushBack(const T& value);
void PushBack(T&& value);
template<typename... Args>
T& EmplaceBack(Args&&... args);
void PopBack();
T& Front() { return m_data[0]; }
const T& Front() const { return m_data[0]; }
T& Back() { return m_data[m_size - 1]; }
const T& Back() const { return m_data[m_size - 1]; }
Iterator begin() { return m_data; }
Iterator end() { return m_data + m_size; }
ConstIterator begin() const { return m_data; }
ConstIterator end() const { return m_data + m_size; }
void SetAllocator(Memory::IAllocator* allocator) { m_allocator = allocator; }
private:
T* m_data = nullptr;
size_t m_size = 0;
size_t m_capacity = 0;
Memory::IAllocator* m_allocator = nullptr;
void Reallocate(size_t newCapacity);
void DestructRange(T* begin, T* end);
void CopyRange(const T* src, T* dst, size_t count);
};
template<typename T>
Array<T>::Array(size_t capacity) : m_capacity(capacity) {
if (capacity > 0) {
m_data = static_cast<T*>(::operator new(capacity * sizeof(T)));
}
}
template<typename T>
Array<T>::Array(size_t count, const T& value) : m_size(count), m_capacity(count) {
if (count > 0) {
m_data = static_cast<T*>(::operator new(count * sizeof(T)));
for (size_t i = 0; i < count; ++i) {
new (&m_data[i]) T(value);
}
}
}
template<typename T>
Array<T>::Array(std::initializer_list<T> init) : m_size(init.size()), m_capacity(init.size()) {
if (m_size > 0) {
m_data = static_cast<T*>(::operator new(m_size * sizeof(T)));
size_t i = 0;
for (const auto& item : init) {
new (&m_data[i++]) T(item);
}
}
}
template<typename T>
Array<T>::~Array() {
DestructRange(m_data, m_data + m_size);
::operator delete(m_data);
}
template<typename T>
Array<T>::Array(const Array& other) : m_size(other.m_size), m_capacity(other.m_size) {
if (m_size > 0) {
m_data = static_cast<T*>(::operator new(m_size * sizeof(T)));
CopyRange(other.m_data, m_data, m_size);
}
}
template<typename T>
Array<T>::Array(Array&& other) noexcept
: m_data(other.m_data), m_size(other.m_size), m_capacity(other.m_capacity), m_allocator(other.m_allocator) {
other.m_data = nullptr;
other.m_size = 0;
other.m_capacity = 0;
}
template<typename T>
Array<T>& Array<T>::operator=(const Array& other) {
if (this != &other) {
DestructRange(m_data, m_data + m_size);
::operator delete(m_data);
m_size = other.m_size;
m_capacity = other.m_size;
m_allocator = other.m_allocator;
if (m_size > 0) {
m_data = static_cast<T*>(::operator new(m_size * sizeof(T)));
CopyRange(other.m_data, m_data, m_size);
} else {
m_data = nullptr;
}
}
return *this;
}
template<typename T>
Array<T>& Array<T>::operator=(Array&& other) noexcept {
if (this != &other) {
DestructRange(m_data, m_data + m_size);
::operator delete(m_data);
m_data = other.m_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
m_allocator = other.m_allocator;
other.m_data = nullptr;
other.m_size = 0;
other.m_capacity = 0;
}
return *this;
}
template<typename T>
T& Array<T>::operator[](size_t index) {
return m_data[index];
}
template<typename T>
const T& Array<T>::operator[](size_t index) const {
return m_data[index];
}
template<typename T>
void Array<T>::Clear() {
DestructRange(m_data, m_data + m_size);
m_size = 0;
}
template<typename T>
void Array<T>::Reserve(size_t capacity) {
if (capacity > m_capacity) {
Reallocate(capacity);
}
}
template<typename T>
void Array<T>::Resize(size_t newSize) {
if (newSize > m_capacity) {
Reallocate(newSize);
}
if (newSize > m_size) {
for (size_t i = m_size; i < newSize; ++i) {
new (&m_data[i]) T();
}
} else if (newSize < m_size) {
DestructRange(m_data + newSize, m_data + m_size);
}
m_size = newSize;
}
template<typename T>
void Array<T>::Resize(size_t newSize, const T& value) {
if (newSize > m_capacity) {
Reallocate(newSize);
}
if (newSize > m_size) {
for (size_t i = m_size; i < newSize; ++i) {
new (&m_data[i]) T(value);
}
} else if (newSize < m_size) {
DestructRange(m_data + newSize, m_data + m_size);
}
m_size = newSize;
}
template<typename T>
void Array<T>::PushBack(const T& value) {
if (m_size >= m_capacity) {
size_t newCapacity = m_capacity == 0 ? 4 : m_capacity * 2;
Reallocate(newCapacity);
}
new (&m_data[m_size]) T(value);
++m_size;
}
template<typename T>
void Array<T>::PushBack(T&& value) {
if (m_size >= m_capacity) {
size_t newCapacity = m_capacity == 0 ? 4 : m_capacity * 2;
Reallocate(newCapacity);
}
new (&m_data[m_size]) T(std::move(value));
++m_size;
}
template<typename T>
template<typename... Args>
T& Array<T>::EmplaceBack(Args&&... args) {
if (m_size >= m_capacity) {
size_t newCapacity = m_capacity == 0 ? 4 : m_capacity * 2;
Reallocate(newCapacity);
}
new (&m_data[m_size]) T(std::forward<Args>(args)...);
++m_size;
return m_data[m_size - 1];
}
template<typename T>
void Array<T>::PopBack() {
if (m_size > 0) {
--m_size;
m_data[m_size].~T();
}
}
template<typename T>
void Array<T>::Reallocate(size_t newCapacity) {
T* newData = nullptr;
if (newCapacity > 0) {
newData = static_cast<T*>(::operator new(newCapacity * sizeof(T)));
size_t count = std::min(m_size, newCapacity);
if (count > 0) {
CopyRange(m_data, newData, count);
}
}
DestructRange(m_data, m_data + m_size);
::operator delete(m_data);
m_data = newData;
m_capacity = newCapacity;
}
template<typename T>
void Array<T>::DestructRange(T* begin, T* end) {
for (T* p = begin; p < end; ++p) {
p->~T();
}
}
template<typename T>
void Array<T>::CopyRange(const T* src, T* dst, size_t count) {
for (size_t i = 0; i < count; ++i) {
new (&dst[i]) T(src[i]);
}
}
} // namespace Containers
} // namespace XCEngine

View File

@@ -0,0 +1,11 @@
#pragma once
#include "Containers/Array.h"
#include "Containers/String.h"
#include "Containers/HashMap.h"
namespace XCEngine {
namespace Containers {
} // namespace Containers
} // namespace XCEngine

View File

@@ -0,0 +1,306 @@
#pragma once
#include <cstddef>
#include <utility>
#include <functional>
#include "../Memory/Allocator.h"
#include "Array.h"
namespace XCEngine {
namespace Containers {
template<typename Key, typename Value>
class HashMap {
public:
struct Pair {
Key first;
Value second;
};
HashMap() {
m_buckets.Resize(DefaultBucketCount);
}
explicit HashMap(size_t bucketCount, Memory::IAllocator* allocator = nullptr);
~HashMap();
HashMap(const HashMap& other);
HashMap(HashMap&& other) noexcept;
HashMap& operator=(const HashMap& other);
HashMap& operator=(HashMap&& other) noexcept;
Value& operator[](const Key& key);
Value* Find(const Key& key);
const Value* Find(const Key& key) const;
bool Contains(const Key& key) const;
bool Insert(const Key& key, const Value& value);
bool Insert(const Key& key, Value&& value);
bool Insert(Pair&& pair);
bool Erase(const Key& key);
void Clear();
size_t Size() const { return m_size; }
bool Empty() const { return m_size == 0; }
void SetAllocator(Memory::IAllocator* allocator) { m_allocator = allocator; }
private:
struct Bucket {
Array<Pair> pairs;
};
static constexpr size_t DefaultBucketCount = 16;
Array<Bucket> m_buckets;
size_t m_bucketCount = DefaultBucketCount;
size_t m_size = 0;
float m_loadFactor = 0.75f;
Memory::IAllocator* m_allocator = nullptr;
size_t GetBucketIndex(const Key& key) const;
void Resize();
typename Array<Pair>::Iterator FindInBucket(Bucket& bucket, const Key& key);
typename Array<Pair>::ConstIterator FindInBucket(const Bucket& bucket, const Key& key) const;
};
template<typename Key, typename Value>
HashMap<Key, Value>::HashMap(size_t bucketCount, Memory::IAllocator* allocator)
: m_bucketCount(bucketCount), m_allocator(allocator) {
if (m_bucketCount == 0) {
m_bucketCount = 16;
}
m_buckets.Resize(m_bucketCount);
}
template<typename Key, typename Value>
HashMap<Key, Value>::~HashMap() {
Clear();
}
template<typename Key, typename Value>
HashMap<Key, Value>::HashMap(const HashMap& other)
: m_bucketCount(other.m_bucketCount), m_size(other.m_size), m_loadFactor(other.m_loadFactor), m_allocator(other.m_allocator) {
m_buckets.Resize(m_bucketCount);
for (size_t i = 0; i < m_bucketCount; ++i) {
m_buckets[i].pairs = other.m_buckets[i].pairs;
}
}
template<typename Key, typename Value>
HashMap<Key, Value>::HashMap(HashMap&& other) noexcept
: m_buckets(std::move(other.m_buckets)),
m_bucketCount(other.m_bucketCount),
m_size(other.m_size),
m_loadFactor(other.m_loadFactor),
m_allocator(other.m_allocator) {
other.m_buckets.Clear();
other.m_bucketCount = 0;
other.m_size = 0;
}
template<typename Key, typename Value>
HashMap<Key, Value>& HashMap<Key, Value>::operator=(const HashMap& other) {
if (this != &other) {
Clear();
m_bucketCount = other.m_bucketCount;
m_size = other.m_size;
m_loadFactor = other.m_loadFactor;
m_allocator = other.m_allocator;
m_buckets.Resize(m_bucketCount);
for (size_t i = 0; i < m_bucketCount; ++i) {
m_buckets[i].pairs = other.m_buckets[i].pairs;
}
}
return *this;
}
template<typename Key, typename Value>
HashMap<Key, Value>& HashMap<Key, Value>::operator=(HashMap&& other) noexcept {
if (this != &other) {
Clear();
m_buckets = std::move(other.m_buckets);
m_bucketCount = other.m_bucketCount;
m_size = other.m_size;
m_loadFactor = other.m_loadFactor;
m_allocator = other.m_allocator;
other.m_buckets.Clear();
other.m_bucketCount = 0;
other.m_size = 0;
}
return *this;
}
template<typename Key, typename Value>
Value& HashMap<Key, Value>::operator[](const Key& key) {
size_t bucketIndex = GetBucketIndex(key);
Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
return it->second;
}
bucket.pairs.EmplaceBack(Pair{key, Value()});
++m_size;
if (static_cast<float>(m_size) / static_cast<float>(m_bucketCount) > m_loadFactor) {
Resize();
}
return bucket.pairs.Back().second;
}
template<typename Key, typename Value>
Value* HashMap<Key, Value>::Find(const Key& key) {
size_t bucketIndex = GetBucketIndex(key);
Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
return &it->second;
}
return nullptr;
}
template<typename Key, typename Value>
const Value* HashMap<Key, Value>::Find(const Key& key) const {
size_t bucketIndex = GetBucketIndex(key);
const Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
return &it->second;
}
return nullptr;
}
template<typename Key, typename Value>
bool HashMap<Key, Value>::Contains(const Key& key) const {
size_t bucketIndex = GetBucketIndex(key);
const Bucket& bucket = m_buckets[bucketIndex];
return FindInBucket(bucket, key) != bucket.pairs.end();
}
template<typename Key, typename Value>
bool HashMap<Key, Value>::Insert(const Key& key, const Value& value) {
size_t bucketIndex = GetBucketIndex(key);
Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
it->second = value;
return false;
}
bucket.pairs.EmplaceBack(Pair{key, value});
++m_size;
if (static_cast<float>(m_size) / static_cast<float>(m_bucketCount) > m_loadFactor) {
Resize();
}
return true;
}
template<typename Key, typename Value>
bool HashMap<Key, Value>::Insert(const Key& key, Value&& value) {
size_t bucketIndex = GetBucketIndex(key);
Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
it->second = std::move(value);
return false;
}
bucket.pairs.EmplaceBack(Pair{key, std::move(value)});
++m_size;
if (static_cast<float>(m_size) / static_cast<float>(m_bucketCount) > m_loadFactor) {
Resize();
}
return true;
}
template<typename Key, typename Value>
bool HashMap<Key, Value>::Insert(Pair&& pair) {
return Insert(std::move(pair.first), std::move(pair.second));
}
template<typename Key, typename Value>
bool HashMap<Key, Value>::Erase(const Key& key) {
size_t bucketIndex = GetBucketIndex(key);
Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
size_t index = it - bucket.pairs.begin();
bucket.pairs[index] = bucket.pairs.Back();
bucket.pairs.PopBack();
--m_size;
return true;
}
return false;
}
template<typename Key, typename Value>
void HashMap<Key, Value>::Clear() {
for (size_t i = 0; i < m_buckets.Size(); ++i) {
m_buckets[i].pairs.Clear();
}
m_size = 0;
}
template<typename Key, typename Value>
size_t HashMap<Key, Value>::GetBucketIndex(const Key& key) const {
if (m_bucketCount == 0) {
return 0;
}
std::hash<Key> hasher;
return hasher(key) % m_bucketCount;
}
template<typename Key, typename Value>
void HashMap<Key, Value>::Resize() {
size_t newBucketCount = m_bucketCount * 2;
Array<Bucket> newBuckets;
newBuckets.Resize(newBucketCount);
for (size_t i = 0; i < m_bucketCount; ++i) {
Bucket& oldBucket = m_buckets[i];
for (size_t j = 0; j < oldBucket.pairs.Size(); ++j) {
Pair& pair = oldBucket.pairs[j];
std::hash<Key> hasher;
size_t newIndex = hasher(pair.first) % newBucketCount;
newBuckets[newIndex].pairs.EmplaceBack(std::move(pair));
}
}
m_buckets = std::move(newBuckets);
m_bucketCount = newBucketCount;
}
template<typename Key, typename Value>
typename Array<typename HashMap<Key, Value>::Pair>::Iterator HashMap<Key, Value>::FindInBucket(Bucket& bucket, const Key& key) {
for (auto it = bucket.pairs.begin(); it != bucket.pairs.end(); ++it) {
if (it->first == key) {
return it;
}
}
return bucket.pairs.end();
}
template<typename Key, typename Value>
typename Array<typename HashMap<Key, Value>::Pair>::ConstIterator HashMap<Key, Value>::FindInBucket(const Bucket& bucket, const Key& key) const {
for (auto it = bucket.pairs.begin(); it != bucket.pairs.end(); ++it) {
if (it->first == key) {
return it;
}
}
return bucket.pairs.end();
}
} // namespace Containers
} // namespace XCEngine

View File

@@ -0,0 +1,96 @@
#pragma once
#include <cstring>
#include <algorithm>
#include <initializer_list>
namespace XCEngine {
namespace Containers {
class String {
public:
using SizeType = size_t;
static constexpr SizeType npos = static_cast<SizeType>(-1);
String();
String(const char* str);
String(const char* str, SizeType len);
String(const String& other);
String(String&& other) noexcept;
~String();
String& operator=(const String& other);
String& operator=(String&& other) noexcept;
String& operator=(const char* str);
String& operator+=(const String& other);
String& operator+=(const char* str);
String& operator+=(char c);
String Substring(SizeType pos, SizeType len = npos) const;
String Trim() const;
String ToLower() const;
String ToUpper() const;
SizeType Find(const char* str, SizeType pos = 0) const;
bool StartsWith(const String& prefix) const;
bool StartsWith(const char* prefix) const;
bool EndsWith(const String& suffix) const;
bool EndsWith(const char* suffix) const;
const char* CStr() const { return m_data; }
SizeType Length() const { return m_length; }
SizeType Capacity() const { return m_capacity; }
bool Empty() const { return m_length == 0; }
char& operator[](SizeType index) { return m_data[index]; }
const char& operator[](SizeType index) const { return m_data[index]; }
void Clear();
void Reserve(SizeType capacity);
void Resize(SizeType newSize);
void Resize(SizeType newSize, char fillChar);
private:
char* m_data = nullptr;
SizeType m_length = 0;
SizeType m_capacity = 0;
void Allocate(SizeType capacity);
void Deallocate();
void CopyFrom(const char* str, SizeType len);
void MoveFrom(String&& other) noexcept;
};
inline String operator+(const String& lhs, const String& rhs) {
String result(lhs);
result += rhs;
return result;
}
inline bool operator==(const String& lhs, const String& rhs) {
return lhs.Length() == rhs.Length() &&
std::strncmp(lhs.CStr(), rhs.CStr(), lhs.Length()) == 0;
}
inline bool operator!=(const String& lhs, const String& rhs) {
return !(lhs == rhs);
}
} // namespace Containers
} // namespace XCEngine
namespace std {
template<>
struct hash<XCEngine::Containers::String> {
size_t operator()(const XCEngine::Containers::String& str) const noexcept {
size_t hash = 5381;
const char* s = str.CStr();
while (*s) {
hash = ((hash << 5) + hash) + *s++;
}
return hash;
}
};
}

View File

@@ -39,7 +39,7 @@ public:
m_pendingUnsubscribes.clear();
}
void Invoke(Args... args) const {
void Invoke(Args... args) {
std::vector<Listener> listenersCopy;
{
std::lock_guard<std::mutex> lock(m_mutex);

View File

@@ -0,0 +1,26 @@
#pragma once
#include <cstddef>
#include <cstdint>
namespace XCEngine {
namespace Memory {
class IAllocator {
public:
virtual ~IAllocator() = default;
virtual void* Allocate(size_t size, size_t alignment = 0) = 0;
virtual void Free(void* ptr) = 0;
virtual void* Reallocate(void* ptr, size_t newSize) = 0;
virtual size_t GetTotalAllocated() const = 0;
virtual size_t GetTotalFreed() const = 0;
virtual size_t GetPeakAllocated() const = 0;
virtual size_t GetAllocationCount() const = 0;
virtual const char* GetName() const = 0;
};
} // namespace Memory
} // namespace XCEngine

View File

@@ -0,0 +1,39 @@
#pragma once
#include "Allocator.h"
namespace XCEngine {
namespace Memory {
class LinearAllocator : public IAllocator {
public:
explicit LinearAllocator(size_t size, IAllocator* parent = nullptr);
~LinearAllocator() override;
void* Allocate(size_t size, size_t alignment = 8) override;
void Free(void* ptr) override;
void* Reallocate(void* ptr, size_t newSize) override;
void Clear();
void* GetMarker() const;
void SetMarker(void* marker);
size_t GetUsedSize() const { return m_offset; }
size_t GetCapacity() const { return m_capacity; }
const char* GetName() const override { return "LinearAllocator"; }
size_t GetTotalAllocated() const override { return m_offset; }
size_t GetTotalFreed() const override { return 0; }
size_t GetPeakAllocated() const override { return m_capacity; }
size_t GetAllocationCount() const override { return 0; }
private:
uint8_t* m_buffer = nullptr;
size_t m_capacity = 0;
size_t m_offset = 0;
IAllocator* m_parent = nullptr;
};
} // namespace Memory
} // namespace XCEngine

View File

@@ -0,0 +1,46 @@
#pragma once
#include <memory>
#include "LinearAllocator.h"
#include "PoolAllocator.h"
#include "ProxyAllocator.h"
namespace XCEngine {
namespace Memory {
class IAllocator;
class LinearAllocator;
class PoolAllocator;
class ProxyAllocator;
class MemoryManager {
public:
static MemoryManager& Get();
void Initialize();
void Shutdown();
IAllocator* GetSystemAllocator();
std::unique_ptr<LinearAllocator> CreateLinearAllocator(size_t size);
std::unique_ptr<PoolAllocator> CreatePoolAllocator(size_t blockSize, size_t count);
std::unique_ptr<ProxyAllocator> CreateProxyAllocator(const char* name);
void SetTrackAllocations(bool track);
void DumpMemoryLeaks();
void GenerateMemoryReport();
private:
MemoryManager() = default;
~MemoryManager() = default;
IAllocator* m_systemAllocator = nullptr;
bool m_initialized = false;
bool m_trackAllocations = true;
};
#define XE_ALLOC(allocator, size, ...) allocator->Allocate(size, ##__VA_ARGS__)
#define XE_FREE(allocator, ptr) allocator->Free(ptr)
} // namespace Memory
} // namespace XCEngine

View File

@@ -0,0 +1,43 @@
#pragma once
#include "Allocator.h"
namespace XCEngine {
namespace Memory {
class PoolAllocator : public IAllocator {
public:
PoolAllocator(size_t blockSize, size_t poolSize, size_t alignment = 8);
~PoolAllocator() override;
void* Allocate(size_t size, size_t alignment = 0) override;
void Free(void* ptr) override;
void* Reallocate(void* ptr, size_t newSize) override;
bool Contains(void* ptr) const;
size_t GetBlockSize() const { return m_blockSize; }
size_t GetFreeBlockCount() const;
size_t GetTotalBlockCount() const { return m_totalBlocks; }
const char* GetName() const override { return "PoolAllocator"; }
size_t GetTotalAllocated() const override { return (m_totalBlocks - m_freeBlocks) * m_blockSize; }
size_t GetTotalFreed() const override { return m_freeBlocks * m_blockSize; }
size_t GetPeakAllocated() const override { return m_totalBlocks * m_blockSize; }
size_t GetAllocationCount() const override { return m_totalBlocks - m_freeBlocks; }
private:
struct FreeNode {
FreeNode* next;
};
size_t m_blockSize = 0;
size_t m_alignment = 0;
void* m_memory = nullptr;
FreeNode* m_freeList = nullptr;
size_t m_totalBlocks = 0;
size_t m_freeBlocks = 0;
};
} // namespace Memory
} // namespace XCEngine

View File

@@ -0,0 +1,41 @@
#pragma once
#include "Allocator.h"
#include "Threading/Mutex.h"
namespace XCEngine {
namespace Memory {
class ProxyAllocator : public IAllocator {
public:
ProxyAllocator(IAllocator* underlying, const char* name);
void* Allocate(size_t size, size_t alignment = 0) override;
void Free(void* ptr) override;
void* Reallocate(void* ptr, size_t newSize) override;
size_t GetTotalAllocated() const override { return m_stats.totalAllocated; }
size_t GetTotalFreed() const override { return m_stats.totalFreed; }
size_t GetPeakAllocated() const override { return m_stats.peakAllocated; }
size_t GetAllocationCount() const override { return m_stats.allocationCount; }
struct Stats {
size_t totalAllocated = 0;
size_t totalFreed = 0;
size_t peakAllocated = 0;
size_t allocationCount = 0;
size_t memoryOverhead = 0;
};
const Stats& GetStats() const;
const char* GetName() const override { return m_name; }
private:
IAllocator* m_underlying = nullptr;
const char* m_name = nullptr;
Stats m_stats;
Threading::Mutex m_mutex;
};
} // namespace Memory
} // namespace XCEngine

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

View File

@@ -0,0 +1,327 @@
#include "Containers/String.h"
#include <cstdlib>
#include <cstdio>
namespace XCEngine {
namespace Containers {
String::String() : m_data(nullptr), m_length(0), m_capacity(0) {
m_data = new char[1];
m_data[0] = '\0';
m_capacity = 1;
}
String::String(const char* str) {
if (str) {
m_length = std::strlen(str);
m_capacity = m_length + 1;
m_data = new char[m_capacity];
std::memcpy(m_data, str, m_length + 1);
} else {
m_data = new char[1];
m_data[0] = '\0';
m_length = 0;
m_capacity = 1;
}
}
String::String(const char* str, SizeType len) {
if (str && len > 0) {
m_length = len;
m_capacity = m_length + 1;
m_data = new char[m_capacity];
std::memcpy(m_data, str, m_length);
m_data[m_length] = '\0';
} else {
m_data = new char[1];
m_data[0] = '\0';
m_length = 0;
m_capacity = 1;
}
}
String::String(const String& other) {
m_length = other.m_length;
m_capacity = m_length + 1;
m_data = new char[m_capacity];
std::memcpy(m_data, other.m_data, m_length + 1);
}
String::String(String&& other) noexcept {
m_data = other.m_data;
m_length = other.m_length;
m_capacity = other.m_capacity;
other.m_data = nullptr;
other.m_length = 0;
other.m_capacity = 0;
}
String::~String() {
Deallocate();
}
String& String::operator=(const String& other) {
if (this != &other) {
if (other.m_length + 1 > m_capacity) {
Deallocate();
Allocate(other.m_length + 1);
}
m_length = other.m_length;
std::memcpy(m_data, other.m_data, m_length + 1);
}
return *this;
}
String& String::operator=(String&& other) noexcept {
if (this != &other) {
Deallocate();
m_data = other.m_data;
m_length = other.m_length;
m_capacity = other.m_capacity;
other.m_data = nullptr;
other.m_length = 0;
other.m_capacity = 0;
}
return *this;
}
String& String::operator=(const char* str) {
if (str) {
SizeType len = std::strlen(str);
if (len + 1 > m_capacity) {
Deallocate();
Allocate(len + 1);
}
m_length = len;
std::memcpy(m_data, str, m_length + 1);
} else {
Clear();
}
return *this;
}
String& String::operator+=(const String& other) {
if (other.m_length > 0) {
SizeType newLength = m_length + other.m_length;
if (newLength + 1 > m_capacity) {
char* newData = new char[newLength + 1];
std::memcpy(newData, m_data, m_length);
delete[] m_data;
m_data = newData;
m_capacity = newLength + 1;
}
std::memcpy(m_data + m_length, other.m_data, other.m_length + 1);
m_length = newLength;
}
return *this;
}
String& String::operator+=(const char* str) {
if (str) {
SizeType len = std::strlen(str);
if (len > 0) {
SizeType newLength = m_length + len;
if (newLength + 1 > m_capacity) {
char* newData = new char[newLength + 1];
std::memcpy(newData, m_data, m_length);
delete[] m_data;
m_data = newData;
m_capacity = newLength + 1;
}
std::memcpy(m_data + m_length, str, len + 1);
m_length = newLength;
}
}
return *this;
}
String& String::operator+=(char c) {
SizeType newLength = m_length + 1;
if (newLength + 1 > m_capacity) {
SizeType newCapacity = newLength * 2;
char* newData = new char[newCapacity];
std::memcpy(newData, m_data, m_length);
delete[] m_data;
m_data = newData;
m_capacity = newCapacity;
}
m_data[m_length] = c;
m_data[newLength] = '\0';
m_length = newLength;
return *this;
}
String String::Substring(SizeType pos, SizeType len) const {
if (pos >= m_length) {
return String();
}
SizeType actualLen = (len == npos || pos + len > m_length) ? (m_length - pos) : len;
return String(m_data + pos, actualLen);
}
String String::Trim() const {
if (m_length == 0) {
return String();
}
SizeType start = 0;
SizeType end = m_length - 1;
while (start <= end && (m_data[start] == ' ' || m_data[start] == '\t' ||
m_data[start] == '\n' || m_data[start] == '\r')) {
start++;
}
while (end >= start && (m_data[end] == ' ' || m_data[end] == '\t' ||
m_data[end] == '\n' || m_data[end] == '\r')) {
end--;
}
if (start > end) {
return String();
}
return String(m_data + start, end - start + 1);
}
String String::ToLower() const {
String result(*this);
for (SizeType i = 0; i < result.m_length; i++) {
if (result.m_data[i] >= 'A' && result.m_data[i] <= 'Z') {
result.m_data[i] = result.m_data[i] + 32;
}
}
return result;
}
String String::ToUpper() const {
String result(*this);
for (SizeType i = 0; i < result.m_length; i++) {
if (result.m_data[i] >= 'a' && result.m_data[i] <= 'z') {
result.m_data[i] = result.m_data[i] - 32;
}
}
return result;
}
String::SizeType String::Find(const char* str, String::SizeType pos) const {
if (!str || m_length == 0) {
return npos;
}
SizeType len = std::strlen(str);
if (len == 0 || pos >= m_length) {
return npos;
}
for (SizeType i = pos; i <= m_length - len; i++) {
if (std::memcmp(m_data + i, str, len) == 0) {
return i;
}
}
return npos;
}
bool String::StartsWith(const String& prefix) const {
return StartsWith(prefix.CStr());
}
bool String::StartsWith(const char* prefix) const {
if (!prefix) {
return false;
}
SizeType len = std::strlen(prefix);
if (len > m_length) {
return false;
}
return std::memcmp(m_data, prefix, len) == 0;
}
bool String::EndsWith(const String& suffix) const {
return EndsWith(suffix.CStr());
}
bool String::EndsWith(const char* suffix) const {
if (!suffix) {
return false;
}
SizeType len = std::strlen(suffix);
if (len > m_length) {
return false;
}
return std::memcmp(m_data + m_length - len, suffix, len) == 0;
}
void String::Clear() {
if (m_data) {
m_data[0] = '\0';
}
m_length = 0;
}
void String::Reserve(SizeType capacity) {
if (capacity > m_capacity) {
char* newData = new char[capacity];
std::memcpy(newData, m_data, m_length + 1);
delete[] m_data;
m_data = newData;
m_capacity = capacity;
}
}
void String::Resize(SizeType newSize) {
Resize(newSize, '\0');
}
void String::Resize(SizeType newSize, char fillChar) {
if (newSize + 1 > m_capacity) {
Reserve(newSize + 1);
}
if (newSize > m_length) {
for (SizeType i = m_length; i < newSize; i++) {
m_data[i] = fillChar;
}
}
m_data[newSize] = '\0';
m_length = newSize;
}
void String::Allocate(SizeType capacity) {
m_capacity = capacity;
m_data = new char[m_capacity];
m_data[0] = '\0';
}
void String::Deallocate() {
if (m_data) {
delete[] m_data;
m_data = nullptr;
}
m_length = 0;
m_capacity = 0;
}
void String::CopyFrom(const char* str, SizeType len) {
if (len + 1 > m_capacity) {
Deallocate();
Allocate(len + 1);
}
m_length = len;
std::memcpy(m_data, str, len);
m_data[len] = '\0';
}
void String::MoveFrom(String&& other) noexcept {
m_data = other.m_data;
m_length = other.m_length;
m_capacity = other.m_capacity;
other.m_data = nullptr;
other.m_length = 0;
other.m_capacity = 0;
}
} // namespace Containers
} // namespace XCEngine

View File

@@ -0,0 +1,272 @@
#include "Memory/LinearAllocator.h"
#include "Memory/PoolAllocator.h"
#include "Memory/ProxyAllocator.h"
#include "Memory/MemoryManager.h"
#include "Threading/Mutex.h"
#include <cstdlib>
#include <cstring>
#include <iostream>
namespace XCEngine {
namespace Memory {
class SystemAllocator : public IAllocator {
public:
void* Allocate(size_t size, size_t alignment) override {
if (alignment == 0) {
return std::malloc(size);
}
#ifdef _WIN32
return _aligned_malloc(size, alignment);
#else
return std::aligned_alloc(alignment, size);
#endif
}
void Free(void* ptr) override {
#ifdef _WIN32
_aligned_free(ptr);
#else
std::free(ptr);
#endif
}
void* Reallocate(void* ptr, size_t newSize) override {
return std::realloc(ptr, newSize);
}
size_t GetTotalAllocated() const override { return 0; }
size_t GetTotalFreed() const override { return 0; }
size_t GetPeakAllocated() const override { return 0; }
size_t GetAllocationCount() const override { return 0; }
const char* GetName() const override { return "SystemAllocator"; }
};
LinearAllocator::LinearAllocator(size_t size, IAllocator* parent)
: m_capacity(size), m_parent(parent) {
if (parent) {
m_buffer = static_cast<uint8_t*>(parent->Allocate(size, 8));
} else {
m_buffer = static_cast<uint8_t*>(_aligned_malloc(size, 8));
}
}
LinearAllocator::~LinearAllocator() {
if (m_parent) {
m_parent->Free(m_buffer);
} else {
#ifdef _WIN32
_aligned_free(m_buffer);
#else
std::free(m_buffer);
#endif
}
}
void* LinearAllocator::Allocate(size_t size, size_t alignment) {
if (size == 0) {
return nullptr;
}
uintptr_t address = reinterpret_cast<uintptr_t>(m_buffer) + m_offset;
if (alignment > 0) {
size_t misalignment = address % alignment;
if (misalignment != 0) {
size += alignment - misalignment;
}
}
if (m_offset + size > m_capacity) {
return nullptr;
}
void* ptr = &m_buffer[m_offset];
m_offset += size;
return ptr;
}
void LinearAllocator::Free(void* ptr) {
}
void* LinearAllocator::Reallocate(void* ptr, size_t newSize) {
return nullptr;
}
void LinearAllocator::Clear() {
m_offset = 0;
}
void* LinearAllocator::GetMarker() const {
return reinterpret_cast<void*>(m_offset);
}
void LinearAllocator::SetMarker(void* marker) {
m_offset = reinterpret_cast<size_t>(marker);
}
PoolAllocator::PoolAllocator(size_t blockSize, size_t poolSize, size_t alignment)
: m_blockSize(blockSize), m_alignment(alignment), m_totalBlocks(poolSize), m_freeBlocks(poolSize) {
size_t actualBlockSize = blockSize;
if (alignment > 0) {
actualBlockSize = (blockSize + alignment - 1) & ~(alignment - 1);
}
m_memory = std::malloc(actualBlockSize * poolSize);
uint8_t* memory = static_cast<uint8_t*>(m_memory);
m_freeList = reinterpret_cast<FreeNode*>(memory);
FreeNode* current = m_freeList;
for (size_t i = 1; i < poolSize; ++i) {
uint8_t* block = memory + (i * actualBlockSize);
current->next = reinterpret_cast<FreeNode*>(block);
current = current->next;
}
current->next = nullptr;
}
PoolAllocator::~PoolAllocator() {
std::free(m_memory);
}
void* PoolAllocator::Allocate(size_t size, size_t alignment) {
if (!m_freeList) {
return nullptr;
}
if (size > m_blockSize) {
return nullptr;
}
FreeNode* node = m_freeList;
m_freeList = m_freeList->next;
--m_freeBlocks;
return node;
}
void PoolAllocator::Free(void* ptr) {
if (!ptr) {
return;
}
FreeNode* node = static_cast<FreeNode*>(ptr);
node->next = m_freeList;
m_freeList = node;
++m_freeBlocks;
}
void* PoolAllocator::Reallocate(void* ptr, size_t newSize) {
return nullptr;
}
bool PoolAllocator::Contains(void* ptr) const {
if (!ptr || !m_memory) {
return false;
}
uint8_t* memory = static_cast<uint8_t*>(m_memory);
uint8_t* p = static_cast<uint8_t*>(ptr);
uintptr_t offset = p - memory;
size_t blockSize = m_blockSize;
if (m_alignment > 0) {
blockSize = (m_blockSize + m_alignment - 1) & ~(m_alignment - 1);
}
return offset < blockSize * m_totalBlocks;
}
size_t PoolAllocator::GetFreeBlockCount() const {
return m_freeBlocks;
}
ProxyAllocator::ProxyAllocator(IAllocator* underlying, const char* name)
: m_underlying(underlying), m_name(name) {
}
void* ProxyAllocator::Allocate(size_t size, size_t alignment) {
std::lock_guard<Threading::Mutex> lock(m_mutex);
void* ptr = m_underlying->Allocate(size, alignment);
if (ptr) {
m_stats.totalAllocated += size;
m_stats.allocationCount++;
if (m_stats.totalAllocated - m_stats.totalFreed > m_stats.peakAllocated) {
m_stats.peakAllocated = m_stats.totalAllocated - m_stats.totalFreed;
}
}
return ptr;
}
void ProxyAllocator::Free(void* ptr) {
std::lock_guard<Threading::Mutex> lock(m_mutex);
m_underlying->Free(ptr);
m_stats.totalFreed += m_stats.allocationCount;
m_stats.allocationCount--;
}
void* ProxyAllocator::Reallocate(void* ptr, size_t newSize) {
std::lock_guard<Threading::Mutex> lock(m_mutex);
void* newPtr = m_underlying->Reallocate(ptr, newSize);
return newPtr;
}
const ProxyAllocator::Stats& ProxyAllocator::GetStats() const {
return m_stats;
}
MemoryManager& MemoryManager::Get() {
static MemoryManager instance;
return instance;
}
void MemoryManager::Initialize() {
if (m_initialized) {
return;
}
m_systemAllocator = new SystemAllocator();
m_initialized = true;
}
void MemoryManager::Shutdown() {
if (!m_initialized) {
return;
}
delete static_cast<SystemAllocator*>(m_systemAllocator);
m_systemAllocator = nullptr;
m_initialized = false;
}
IAllocator* MemoryManager::GetSystemAllocator() {
return m_systemAllocator;
}
std::unique_ptr<LinearAllocator> MemoryManager::CreateLinearAllocator(size_t size) {
return std::make_unique<LinearAllocator>(size, m_systemAllocator);
}
std::unique_ptr<PoolAllocator> MemoryManager::CreatePoolAllocator(size_t blockSize, size_t count) {
return std::make_unique<PoolAllocator>(blockSize, count);
}
std::unique_ptr<ProxyAllocator> MemoryManager::CreateProxyAllocator(const char* name) {
return std::make_unique<ProxyAllocator>(m_systemAllocator, name);
}
void MemoryManager::SetTrackAllocations(bool track) {
m_trackAllocations = track;
}
void MemoryManager::DumpMemoryLeaks() {
std::cout << "Memory Leak Report:" << std::endl;
}
void MemoryManager::GenerateMemoryReport() {
std::cout << "Memory Report:" << std::endl;
}
} // namespace Memory
} // namespace XCEngine

View 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

View 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

View 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