mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-11 22:29:17 +00:00
kernel: Fix a bunch of bugs, kernel thread heap
This commit is contained in:
163
src/common/slab_heap.h
Normal file
163
src/common/slab_heap.h
Normal file
@@ -0,0 +1,163 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include "common/assert.h"
|
||||
#include "common/spin_lock.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
class SlabHeapImpl {
|
||||
public:
|
||||
struct Node {
|
||||
Node* next{};
|
||||
};
|
||||
|
||||
public:
|
||||
constexpr SlabHeapImpl() = default;
|
||||
|
||||
void Initialize() {
|
||||
ASSERT(m_head == nullptr);
|
||||
}
|
||||
|
||||
Node* GetHead() const {
|
||||
return m_head;
|
||||
}
|
||||
|
||||
void* Allocate() {
|
||||
m_lock.lock();
|
||||
|
||||
Node* ret = m_head;
|
||||
if (ret != nullptr) {
|
||||
m_head = ret->next;
|
||||
}
|
||||
|
||||
m_lock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Free(void* obj) {
|
||||
m_lock.lock();
|
||||
|
||||
Node* node = static_cast<Node*>(obj);
|
||||
node->next = m_head;
|
||||
m_head = node;
|
||||
|
||||
m_lock.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<Node*> m_head{};
|
||||
Common::SpinLock m_lock;
|
||||
};
|
||||
|
||||
class SlabHeapBase : protected SlabHeapImpl {
|
||||
private:
|
||||
size_t m_obj_size{};
|
||||
uintptr_t m_peak{};
|
||||
uintptr_t m_start{};
|
||||
uintptr_t m_end{};
|
||||
|
||||
public:
|
||||
constexpr SlabHeapBase() = default;
|
||||
|
||||
bool Contains(uintptr_t address) const {
|
||||
return m_start <= address && address < m_end;
|
||||
}
|
||||
|
||||
void Initialize(size_t obj_size, void* memory, size_t memory_size) {
|
||||
// Ensure we don't initialize a slab using null memory.
|
||||
ASSERT(memory != nullptr);
|
||||
|
||||
// Set our object size.
|
||||
m_obj_size = obj_size;
|
||||
|
||||
// Initialize the base allocator.
|
||||
SlabHeapImpl::Initialize();
|
||||
|
||||
// Set our tracking variables.
|
||||
const size_t num_obj = (memory_size / obj_size);
|
||||
m_start = reinterpret_cast<uintptr_t>(memory);
|
||||
m_end = m_start + num_obj * obj_size;
|
||||
m_peak = m_start;
|
||||
|
||||
// Free the objects.
|
||||
u8* cur = reinterpret_cast<u8*>(m_end);
|
||||
|
||||
for (size_t i = 0; i < num_obj; i++) {
|
||||
cur -= obj_size;
|
||||
SlabHeapImpl::Free(cur);
|
||||
}
|
||||
}
|
||||
|
||||
size_t GetSlabHeapSize() const {
|
||||
return (m_end - m_start) / this->GetObjectSize();
|
||||
}
|
||||
|
||||
size_t GetObjectSize() const {
|
||||
return m_obj_size;
|
||||
}
|
||||
|
||||
void* Allocate() {
|
||||
void* obj = SlabHeapImpl::Allocate();
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Free(void* obj) {
|
||||
// Don't allow freeing an object that wasn't allocated from this heap.
|
||||
const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
|
||||
ASSERT(contained);
|
||||
SlabHeapImpl::Free(obj);
|
||||
}
|
||||
|
||||
size_t GetObjectIndex(const void* obj) const {
|
||||
return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
|
||||
}
|
||||
|
||||
size_t GetPeakIndex() const {
|
||||
return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
|
||||
}
|
||||
|
||||
uintptr_t GetSlabHeapAddress() const {
|
||||
return m_start;
|
||||
}
|
||||
|
||||
size_t GetNumRemaining() const {
|
||||
// Only calculate the number of remaining objects under debug configuration.
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class SlabHeap final : public SlabHeapBase {
|
||||
private:
|
||||
using BaseHeap = SlabHeapBase;
|
||||
|
||||
public:
|
||||
constexpr SlabHeap() = default;
|
||||
|
||||
void Initialize(void* memory, size_t memory_size) {
|
||||
BaseHeap::Initialize(sizeof(T), memory, memory_size);
|
||||
}
|
||||
|
||||
T* Allocate() {
|
||||
T* obj = static_cast<T*>(BaseHeap::Allocate());
|
||||
|
||||
if (obj != nullptr) [[likely]] {
|
||||
std::construct_at(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Free(T* obj) {
|
||||
BaseHeap::Free(obj);
|
||||
}
|
||||
|
||||
size_t GetObjectIndex(const T* obj) const {
|
||||
return BaseHeap::GetObjectIndex(obj);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -3,10 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <compare>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "common/assert.h"
|
||||
|
||||
53
src/common/spin_lock.cpp
Executable file
53
src/common/spin_lock.cpp
Executable file
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/spin_lock.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#include <intrin.h>
|
||||
#if _M_AMD64
|
||||
#define __x86_64__ 1
|
||||
#endif
|
||||
#if _M_ARM64
|
||||
#define __aarch64__ 1
|
||||
#endif
|
||||
#else
|
||||
#if __x86_64__
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
void ThreadPause() {
|
||||
#if __x86_64__
|
||||
_mm_pause();
|
||||
#elif __aarch64__ && _MSC_VER
|
||||
__yield();
|
||||
#elif __aarch64__
|
||||
asm("yield");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
namespace Common {
|
||||
|
||||
void SpinLock::lock() {
|
||||
while (lck.test_and_set(std::memory_order_acquire)) {
|
||||
ThreadPause();
|
||||
}
|
||||
}
|
||||
|
||||
void SpinLock::unlock() {
|
||||
lck.clear(std::memory_order_release);
|
||||
}
|
||||
|
||||
bool SpinLock::try_lock() {
|
||||
if (lck.test_and_set(std::memory_order_acquire)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
33
src/common/spin_lock.h
Executable file
33
src/common/spin_lock.h
Executable file
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/**
|
||||
* SpinLock class
|
||||
* a lock similar to mutex that forces a thread to spin wait instead calling the
|
||||
* supervisor. Should be used on short sequences of code.
|
||||
*/
|
||||
class SpinLock {
|
||||
public:
|
||||
SpinLock() = default;
|
||||
|
||||
SpinLock(const SpinLock&) = delete;
|
||||
SpinLock& operator=(const SpinLock&) = delete;
|
||||
|
||||
SpinLock(SpinLock&&) = delete;
|
||||
SpinLock& operator=(SpinLock&&) = delete;
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
[[nodiscard]] bool try_lock();
|
||||
|
||||
private:
|
||||
std::atomic_flag lck = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
Reference in New Issue
Block a user