core: hle: kernel: Add KDynamicSlabHeap.
This commit is contained in:
parent
345b9e6a08
commit
9ec5f75f43
|
@ -191,6 +191,7 @@ add_library(core STATIC
|
||||||
hle/kernel/k_condition_variable.cpp
|
hle/kernel/k_condition_variable.cpp
|
||||||
hle/kernel/k_condition_variable.h
|
hle/kernel/k_condition_variable.h
|
||||||
hle/kernel/k_dynamic_page_manager.h
|
hle/kernel/k_dynamic_page_manager.h
|
||||||
|
hle/kernel/k_dynamic_slab_heap.h
|
||||||
hle/kernel/k_event.cpp
|
hle/kernel/k_event.cpp
|
||||||
hle/kernel/k_event.h
|
hle/kernel/k_event.h
|
||||||
hle/kernel/k_handle_table.cpp
|
hle/kernel/k_handle_table.cpp
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "core/hle/kernel/k_dynamic_page_manager.h"
|
||||||
|
#include "core/hle/kernel/k_slab_heap.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
template <typename T, bool ClearNode = false>
|
||||||
|
class KDynamicSlabHeap : protected impl::KSlabHeapImpl {
|
||||||
|
YUZU_NON_COPYABLE(KDynamicSlabHeap);
|
||||||
|
YUZU_NON_MOVEABLE(KDynamicSlabHeap);
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr KDynamicSlabHeap() = default;
|
||||||
|
|
||||||
|
constexpr VAddr GetAddress() const {
|
||||||
|
return m_address;
|
||||||
|
}
|
||||||
|
constexpr size_t GetSize() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
constexpr size_t GetUsed() const {
|
||||||
|
return m_used.load();
|
||||||
|
}
|
||||||
|
constexpr size_t GetPeak() const {
|
||||||
|
return m_peak.load();
|
||||||
|
}
|
||||||
|
constexpr size_t GetCount() const {
|
||||||
|
return m_count.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsInRange(VAddr addr) const {
|
||||||
|
return this->GetAddress() <= addr && addr <= this->GetAddress() + this->GetSize() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(KDynamicPageManager* page_allocator, size_t num_objects) {
|
||||||
|
ASSERT(page_allocator != nullptr);
|
||||||
|
|
||||||
|
// Initialize members.
|
||||||
|
m_address = page_allocator->GetAddress();
|
||||||
|
m_size = page_allocator->GetSize();
|
||||||
|
|
||||||
|
// Initialize the base allocator.
|
||||||
|
KSlabHeapImpl::Initialize();
|
||||||
|
|
||||||
|
// Allocate until we have the correct number of objects.
|
||||||
|
while (m_count.load() < num_objects) {
|
||||||
|
auto* allocated = reinterpret_cast<T*>(page_allocator->Allocate());
|
||||||
|
ASSERT(allocated != nullptr);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(PageBuffer) / sizeof(T); i++) {
|
||||||
|
KSlabHeapImpl::Free(allocated + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_count += sizeof(PageBuffer) / sizeof(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T* Allocate(KDynamicPageManager* page_allocator) {
|
||||||
|
T* allocated = static_cast<T*>(KSlabHeapImpl::Allocate());
|
||||||
|
|
||||||
|
// If we successfully allocated and we should clear the node, do so.
|
||||||
|
if constexpr (ClearNode) {
|
||||||
|
if (allocated != nullptr) [[likely]] {
|
||||||
|
reinterpret_cast<KSlabHeapImpl::Node*>(allocated)->next = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we fail to allocate, try to get a new page from our next allocator.
|
||||||
|
if (allocated == nullptr) [[unlikely]] {
|
||||||
|
if (page_allocator != nullptr) {
|
||||||
|
allocated = reinterpret_cast<T*>(page_allocator->Allocate());
|
||||||
|
if (allocated != nullptr) {
|
||||||
|
// If we succeeded in getting a page, free the rest to our slab.
|
||||||
|
for (size_t i = 1; i < sizeof(PageBuffer) / sizeof(T); i++) {
|
||||||
|
KSlabHeapImpl::Free(allocated + i);
|
||||||
|
}
|
||||||
|
m_count += sizeof(PageBuffer) / sizeof(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allocated != nullptr) [[likely]] {
|
||||||
|
// Construct the object.
|
||||||
|
std::construct_at(allocated);
|
||||||
|
|
||||||
|
// Update our tracking.
|
||||||
|
const size_t used = ++m_used;
|
||||||
|
size_t peak = m_peak.load();
|
||||||
|
while (peak < used) {
|
||||||
|
if (m_peak.compare_exchange_weak(peak, used, std::memory_order_relaxed)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allocated;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Free(T* t) {
|
||||||
|
KSlabHeapImpl::Free(t);
|
||||||
|
--m_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using PageBuffer = KDynamicPageManager::PageBuffer;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<size_t> m_used{};
|
||||||
|
std::atomic<size_t> m_peak{};
|
||||||
|
std::atomic<size_t> m_count{};
|
||||||
|
VAddr m_address{};
|
||||||
|
size_t m_size{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Kernel
|
Reference in New Issue