mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-17 12:39:27 -04:00
virtual memory and actor spawner
This commit is contained in:
@@ -10,6 +10,11 @@
|
||||
#include "JSystem/JUtility/JUTConsole.h"
|
||||
#include "JSystem/JUtility/JUTException.h"
|
||||
#include <cstdlib>
|
||||
#if TARGET_PC
|
||||
#include "dusk/vmem.h"
|
||||
#include <algorithm>
|
||||
#include "dusk/logging.h"
|
||||
#endif
|
||||
|
||||
JKRExpHeap* JKRExpHeap::createRoot(int maxHeaps, bool errorFlag) {
|
||||
JKRExpHeap* heap = NULL;
|
||||
@@ -71,21 +76,49 @@ JKRExpHeap* JKRExpHeap::create(u32 size, JKRHeap* parent, bool errorFlag) {
|
||||
|
||||
u32 alignedSize = ALIGN_PREV(size, 0x10);
|
||||
|
||||
if (alignedSize < expHeapSize + blockSize)
|
||||
if (alignedSize < expHeapSize + blockSize) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8* memory = (u8*)JKRAllocFromHeap(parent, alignedSize, 0x10);
|
||||
u8* dataPtr = (memory + expHeapSize);
|
||||
#if TARGET_PC
|
||||
u8* vmemBase = (u8*)dusk::vmem_arena_alloc(JKR_HEAP_VIRTUAL_RESERVE);
|
||||
if (!vmemBase) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const size_t pageSize = dusk::vmem_page_size();
|
||||
size_t commitSize = ALIGN_NEXT((size_t)alignedSize, pageSize);
|
||||
if (!dusk::vmem_commit(vmemBase, commitSize)) {
|
||||
dusk::vmem_arena_free(vmemBase, JKR_HEAP_VIRTUAL_RESERVE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8* memory = vmemBase;
|
||||
u8* dataPtr = memory + expHeapSize;
|
||||
|
||||
newHeap = JKR_NEW_ARGS(memory) JKRExpHeap(dataPtr, alignedSize - expHeapSize, parent, errorFlag);
|
||||
if (newHeap == NULL) {
|
||||
dusk::vmem_arena_free(vmemBase, JKR_HEAP_VIRTUAL_RESERVE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newHeap->mVmemBase = vmemBase;
|
||||
newHeap->mVmemCapacity = JKR_HEAP_VIRTUAL_RESERVE;
|
||||
newHeap->mVmemCommitted = commitSize;
|
||||
#else
|
||||
u8* memory = (u8*)JKRAllocFromHeap(parent, alignedSize, 0x10);
|
||||
u8* dataPtr = memory + expHeapSize;
|
||||
if (!memory) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newHeap = JKR_NEW_ARGS (memory) JKRExpHeap(dataPtr, alignedSize - expHeapSize, parent, errorFlag);
|
||||
|
||||
newHeap = JKR_NEW_ARGS(memory) JKRExpHeap(dataPtr, alignedSize - expHeapSize, parent, errorFlag);
|
||||
if (newHeap == NULL) {
|
||||
JKRFree(memory);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
if (newHeap) {
|
||||
u8* local_30 = dataPtr + sizeof(CMemBlock);
|
||||
@@ -102,9 +135,16 @@ JKRExpHeap* JKRExpHeap::create(u32 size, JKRHeap* parent, bool errorFlag) {
|
||||
JKRExpHeap* JKRExpHeap::create(void* ptr, u32 size, JKRHeap* parent, bool errorFlag) {
|
||||
JKRHeap* parent2;
|
||||
if (parent == NULL) {
|
||||
#if TARGET_PC
|
||||
// VM-backed heaps live outside the root heap's address range, so find() fails
|
||||
// findAllHeap() searches the full tree
|
||||
parent2 = getRootHeap()->findAllHeap(ptr);
|
||||
#else
|
||||
parent2 = getRootHeap()->find(ptr);
|
||||
if (!parent2)
|
||||
#endif
|
||||
if (!parent2) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
parent2 = parent;
|
||||
}
|
||||
@@ -136,6 +176,15 @@ JKRExpHeap* JKRExpHeap::create(void* ptr, u32 size, JKRHeap* parent, bool errorF
|
||||
}
|
||||
|
||||
void JKRExpHeap::do_destroy() {
|
||||
#if TARGET_PC
|
||||
if (mVmemBase) {
|
||||
void* vmemBase = mVmemBase;
|
||||
size_t vmemCapacity = mVmemCapacity;
|
||||
this->~JKRExpHeap();
|
||||
dusk::vmem_arena_free(vmemBase, vmemCapacity);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (!field_0x6e) {
|
||||
JKRHeap* heap = getParent();
|
||||
if (heap) {
|
||||
@@ -163,6 +212,11 @@ JKRExpHeap::JKRExpHeap(void* data, u32 size, JKRHeap* parent, bool errorFlag)
|
||||
mHeadFreeList->initiate(NULL, NULL, size - sizeof(CMemBlock), 0, 0);
|
||||
mHeadUsedList = NULL;
|
||||
mTailUsedList = NULL;
|
||||
#if TARGET_PC
|
||||
mVmemBase = nullptr;
|
||||
mVmemCapacity = 0;
|
||||
mVmemCommitted = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
JKRExpHeap::~JKRExpHeap() {
|
||||
@@ -214,6 +268,24 @@ void* JKRExpHeap::do_alloc(u32 size, int alignment) {
|
||||
#endif
|
||||
|
||||
#if TARGET_PC
|
||||
if (!ptr && mVmemBase) {
|
||||
// Heap is full, commit the next chunk of reserved VM and retry
|
||||
if (growHeap(size)) {
|
||||
if (alignment >= 0) {
|
||||
if (alignment <= 4) {
|
||||
ptr = allocFromHead(size);
|
||||
} else {
|
||||
ptr = allocFromHead(size, alignment);
|
||||
}
|
||||
} else {
|
||||
if (-alignment <= 4) {
|
||||
ptr = allocFromTail(size);
|
||||
} else {
|
||||
ptr = allocFromTail(size, -alignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ptr) {
|
||||
// Allocation failed.
|
||||
OSReport_Error(
|
||||
@@ -491,6 +563,49 @@ static void dummy() {
|
||||
OS_REPORT("newSize > 0");
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
bool JKRExpHeap::growHeap(u32 needed) {
|
||||
// Determine how much to commit
|
||||
// Always grow by at least JKR_HEAP_GROW_CHUNK
|
||||
const size_t pageSize = dusk::vmem_page_size();
|
||||
size_t wantBytes = (size_t)needed + sizeof(CMemBlock);
|
||||
size_t growAmount = std::max(wantBytes, JKR_HEAP_GROW_CHUNK);
|
||||
growAmount = ALIGN_NEXT(growAmount, pageSize);
|
||||
|
||||
size_t remaining = mVmemCapacity - mVmemCommitted;
|
||||
if (growAmount > remaining) {
|
||||
// Clamp to whatever reservation is left
|
||||
growAmount = ALIGN_PREV(remaining, pageSize);
|
||||
if (growAmount < wantBytes) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void* commitBase = (u8*)mVmemBase + mVmemCommitted;
|
||||
if (!dusk::vmem_commit(commitBase, growAmount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Splice the new committed region into the free list as a single block at mEnd
|
||||
CMemBlock* newBlock = (CMemBlock*)mEnd;
|
||||
newBlock->size = (u32)(growAmount - sizeof(CMemBlock));
|
||||
newBlock->mFlags = 0;
|
||||
|
||||
mEnd = (u8*)mEnd + growAmount;
|
||||
mSize += (u32)growAmount;
|
||||
mVmemCommitted += growAmount;
|
||||
|
||||
recycleFreeBlock(newBlock);
|
||||
|
||||
DuskLog.debug("[JKRExpHeap] '{}' grew by {} MB (committed: {} MB / reserved: {} MB)\n",
|
||||
getName(),
|
||||
growAmount / (1024 * 1024),
|
||||
mVmemCommitted / (1024 * 1024),
|
||||
mVmemCapacity / (1024 * 1024));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void JKRExpHeap::do_freeAll() {
|
||||
lock();
|
||||
JKRHeap::callAllDisposer();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "JSystem/JSystem.h" // IWYU pragma: keep
|
||||
#include "JSystem/JSystem.h" // IWYU pragma: keep
|
||||
|
||||
#include "JSystem/JKernel/JKRSolidHeap.h"
|
||||
#include "JSystem/JGadget/binary.h"
|
||||
@@ -7,6 +7,11 @@
|
||||
#include "global.h"
|
||||
#include <stdint.h>
|
||||
#include <cstdlib>
|
||||
#if TARGET_PC
|
||||
#include "dusk/vmem.h"
|
||||
#include <algorithm>
|
||||
#include "dusk/logging.h"
|
||||
#endif
|
||||
|
||||
JKRSolidHeap* JKRSolidHeap::create(u32 size, JKRHeap* heap, bool useErrorHandler) {
|
||||
if (!heap) {
|
||||
@@ -19,18 +24,56 @@ JKRSolidHeap* JKRSolidHeap::create(u32 size, JKRHeap* heap, bool useErrorHandler
|
||||
}
|
||||
|
||||
u32 alignedSize = ALIGN_PREV(size, 0x10);
|
||||
if (alignedSize < solidHeapSize)
|
||||
if (alignedSize < solidHeapSize) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
u8* vmemBase = (u8*)dusk::vmem_arena_alloc(JKR_HEAP_VIRTUAL_RESERVE);
|
||||
if (!vmemBase) {
|
||||
return NULL;
|
||||
}
|
||||
const size_t pageSize = dusk::vmem_page_size();
|
||||
size_t commitSize = ALIGN_NEXT((size_t)alignedSize, pageSize);
|
||||
if (!dusk::vmem_commit(vmemBase, commitSize)) {
|
||||
dusk::vmem_arena_free(vmemBase, JKR_HEAP_VIRTUAL_RESERVE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8* mem = vmemBase;
|
||||
void* dataPtr = mem + solidHeapSize;
|
||||
|
||||
JKRSolidHeap* newHeap = JKR_NEW_ARGS(mem) JKRSolidHeap(dataPtr, alignedSize - solidHeapSize, heap, useErrorHandler);
|
||||
if (newHeap == NULL) {
|
||||
dusk::vmem_arena_free(vmemBase, JKR_HEAP_VIRTUAL_RESERVE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newHeap->mVmemBase = vmemBase;
|
||||
newHeap->mVmemCapacity = JKR_HEAP_VIRTUAL_RESERVE;
|
||||
newHeap->mVmemCommitted = commitSize;
|
||||
return newHeap;
|
||||
#else
|
||||
u8* mem = (u8*)JKRAllocFromHeap(heap, alignedSize, 0x10);
|
||||
void* dataPtr = mem + solidHeapSize;
|
||||
if (!mem)
|
||||
if (!mem) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return JKR_NEW_ARGS (mem) JKRSolidHeap(dataPtr, alignedSize - solidHeapSize, heap, useErrorHandler);
|
||||
#endif
|
||||
}
|
||||
|
||||
void JKRSolidHeap::do_destroy(void) {
|
||||
#if TARGET_PC
|
||||
if (mVmemBase) {
|
||||
void* vmemBase = mVmemBase;
|
||||
size_t vmemCapacity = mVmemCapacity;
|
||||
this->~JKRSolidHeap();
|
||||
dusk::vmem_arena_free(vmemBase, vmemCapacity);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
JKRHeap* parent = getParent();
|
||||
if (parent) {
|
||||
this->~JKRSolidHeap();
|
||||
@@ -44,6 +87,11 @@ JKRSolidHeap::JKRSolidHeap(void* start, u32 size, JKRHeap* parent, bool useError
|
||||
mSolidHead = (u8*)mStart;
|
||||
mSolidTail = (u8*)mEnd;
|
||||
field_0x78 = NULL;
|
||||
#if TARGET_PC
|
||||
mVmemBase = nullptr;
|
||||
mVmemCapacity = 0;
|
||||
mVmemCommitted = 0;
|
||||
#endif
|
||||
#if DEBUG
|
||||
if (mDebugFill) {
|
||||
JKRFillMemory(mStart, mSize, JKRValue_DEBUGFILL_NOTUSE);
|
||||
@@ -59,6 +107,15 @@ s32 JKRSolidHeap::adjustSize(void) {
|
||||
int r25 = 0;
|
||||
JKRHeap* parent = getParent();
|
||||
if (parent) {
|
||||
#if TARGET_PC
|
||||
if (mVmemBase) {
|
||||
// VM-backed heap, can't resize in parent, but this is not a failure
|
||||
// Return what the trimmed size would have been so the caller doesn't log an error
|
||||
u32 thisSize = (uintptr_t)mStart - (uintptr_t)this;
|
||||
u32 newSize = ALIGN_NEXT(mSolidHead - mStart, 0x20);
|
||||
return (s32)(thisSize + newSize);
|
||||
}
|
||||
#endif
|
||||
lock();
|
||||
u32 thisSize = (uintptr_t)mStart - (uintptr_t)this;
|
||||
u32 newSize = ALIGN_NEXT(mSolidHead - mStart, 0x20);
|
||||
@@ -110,6 +167,11 @@ void* JKRSolidHeap::allocFromHead(u32 size, int alignment) {
|
||||
void* ptr = NULL;
|
||||
uintptr_t alignedStart = (alignment - 1 + (uintptr_t)mSolidHead) & ~(alignment - 1);
|
||||
u32 totalSize = size + (alignedStart - (uintptr_t)mSolidHead);
|
||||
#if TARGET_PC
|
||||
if (totalSize > mFreeSize && mVmemBase) {
|
||||
growHeap(totalSize);
|
||||
}
|
||||
#endif
|
||||
if (totalSize <= mFreeSize) {
|
||||
#if DEBUG
|
||||
if (mCheckMemoryFilled) {
|
||||
@@ -137,6 +199,15 @@ void* JKRSolidHeap::allocFromTail(u32 size, int alignment) {
|
||||
void* ptr = NULL;
|
||||
uintptr_t alignedStart = ALIGN_PREV((uintptr_t)mSolidTail - size, alignment);
|
||||
u32 totalSize = (uintptr_t)mSolidTail - (uintptr_t)alignedStart;
|
||||
#if TARGET_PC
|
||||
if (totalSize > mFreeSize && mVmemBase) {
|
||||
if (growHeap(totalSize)) {
|
||||
// mSolidTail moved to new mEnd; recompute from the new tail position
|
||||
alignedStart = ALIGN_PREV((uintptr_t)mSolidTail - size, alignment);
|
||||
totalSize = (uintptr_t)mSolidTail - (uintptr_t)alignedStart;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (totalSize <= mFreeSize) {
|
||||
ptr = (void*)alignedStart;
|
||||
mSolidTail -= totalSize;
|
||||
@@ -158,6 +229,47 @@ void* JKRSolidHeap::allocFromTail(u32 size, int alignment) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
bool JKRSolidHeap::growHeap(u32 needed) {
|
||||
// Growth is only safe when no tail allocations exist yet
|
||||
if (mSolidTail != mEnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t pageSize = dusk::vmem_page_size();
|
||||
size_t wantBytes = (size_t)needed;
|
||||
size_t growAmount = std::max(wantBytes, JKR_HEAP_GROW_CHUNK);
|
||||
growAmount = ALIGN_NEXT(growAmount, pageSize);
|
||||
|
||||
size_t remaining = mVmemCapacity - mVmemCommitted;
|
||||
if (growAmount > remaining) {
|
||||
growAmount = ALIGN_PREV(remaining, pageSize);
|
||||
if (growAmount < wantBytes) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void* commitBase = (u8*)mVmemBase + mVmemCommitted;
|
||||
if (!dusk::vmem_commit(commitBase, growAmount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extend the heap end and the tail pointer
|
||||
mEnd = (u8*)mEnd + growAmount;
|
||||
mSolidTail = mEnd;
|
||||
mFreeSize += (u32)growAmount;
|
||||
mSize += (u32)growAmount;
|
||||
mVmemCommitted += growAmount;
|
||||
|
||||
DuskLog.debug("[JKRSolidHeap] '{}' grew by {} MB (committed: {} MB / reserved: {} MB)\n",
|
||||
getName(),
|
||||
growAmount / (1024 * 1024),
|
||||
mVmemCommitted / (1024 * 1024),
|
||||
mVmemCapacity / (1024 * 1024));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void JKRSolidHeap::do_free(void* ptr) {
|
||||
JUTWarningConsole_f("free: cannot free memory block (%08x)\n", ptr);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user