Files
tech5/source/shared/idlib/sys/sys_alloc.cpp
T
2026-08-08 13:54:26 -07:00

209 lines
7.3 KiB
C++

#include "sys_alloc.h"
#include <algorithm>
#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <malloc.h>
#include <Windows.h>
namespace {
struct allocationRecord_t {
allocationRecord_t* next;
void* pointer;
unsigned int bytes;
memTag_t tag;
heapType_t heap;
const char* location;
};
SRWLOCK allocationLock = SRWLOCK_INIT;
allocationRecord_t* allocations = nullptr;
std::atomic<unsigned long long> allocatedBytes(0);
std::atomic<idOutOfMemoryCallback> outOfMemoryCallback(nullptr);
thread_local heapType_t currentHeap = HEAP_SYSTEMHEAP;
thread_local heapType_t pushedHeap = HEAP_SYSTEMHEAP;
thread_local int heapStackDepth = 0;
const char* const tagNames[TAG_NUM_TAGS] = {
"UNSET", "STATIC_EXE", "RESOURCE_GRAPH", "DEBUG", "NEW", "IDLIST",
"TEMP", "STRING", "ATOMIC_STRING", "BITARRAY", "MATH", "LEXER",
"COMPILER", "COLLISION", "COLLISION_QUERY", "CLIPMODEL", "MORPH",
"MD6_MISC", "MD6_NODES", "MD6", "MD6_ANIMS", "MD6_LIPSYNC",
"MD6_JOINTCACHE", "MD6_MESHES", "MD6_JOINTBUFFERS", "MD6_BLENDSTACK",
"MD6_JOINTMODS", "MD6_COLLISION", "MD6_ANIMEVENTS", "MD6_PHASE_TRACK",
"ANIMATION", "ANIMATION_DEBUG", "DECL_ANIMWEB", "ANIMWEB", "IMAGE",
"DXIMAGE", "VIRTUALTEXTURE", "AAS", "SOUND", "SOUND_BSP", "SOUND_DATA",
"SOUND_STREAM", "SOUND_MULTISTREAM", "SOUND_SAMPLETABLES", "IDLIB",
"TRIANGLES", "DECL", "DECLTEXT", "FILE", "CVAR", "PAGEFILECACHE",
"IDCLASS", "PRESENTABLE", "FOLIAGE", "WATER", "MEMORY_MAPPED_FILE",
"RENDERPARM", "NETWORKING", "SCRIPT", "FXPHYSICS", "LWO", "RENDERWORLD",
"RENDERER", "AI_GAMESTATE", "EVENTS", "VOICEOVER", "VOICETRACK_EVENTS",
"VOICETRACK_FRAMEREFS", "VOICETRACK_PHONEMES", "VISEMESET_VISEMES",
"VISEMESET_PHONEMES", "AF", "SWF", "GUI", "GUI_MODEL", "FUNC_CALLBACK",
"MENU", "GAME", "HASHINDEX", "PARTICLE", "EFFECT_PARTICLE", "CLOTH",
"ANIMTAGS", "IK", "STATICMODEL", "RENDERMODEL", "DXBUFFER", "TOOLS",
"CLOUD", "AMQP", "RENDERPROG", "HASHTABLE", "AI_FSM", "AI_VISCACHE",
"AI_SEARCH", "AI_OBSTACLE", "JOBLIST", "TRANSPARENCY", "DETAIL",
"RESOURCE", "FILE_RESOURCE", "RESOURCE_BGL", "RESOURCE_BGL_RING",
"RESOURCE_BGL_OVERSIZE", "RESOURCE_MGR", "PVS", "DEFERRED_VIS", "FIBER",
"SUPERSCRIPT", "FX", "SAVEGAMES", "AI_TRANSITIONS", "AI_STATEDATA",
"AI_COMBATANIM", "TYPEINFO", "DAMAGEDECAL", "TABLE", "VIDEO", "NAVPOWER",
"LANGDICT", "SPLINE", "BINK", "FONTS", "EVENT_LISTENER", "PHYSICAL_BLOCK",
"DXOBJECT"
};
void AddRecord(void* pointer, const unsigned int bytes, const memTag_t tag,
const heapType_t heap, const char* location) {
allocationRecord_t* const record = static_cast<allocationRecord_t*>(
std::malloc(sizeof(allocationRecord_t))
);
if (record == nullptr) {
return;
}
record->pointer = pointer;
record->bytes = bytes;
record->tag = tag;
record->heap = heap;
record->location = location;
AcquireSRWLockExclusive(&allocationLock);
record->next = allocations;
allocations = record;
ReleaseSRWLockExclusive(&allocationLock);
allocatedBytes.fetch_add(bytes, std::memory_order_relaxed);
}
unsigned int RemoveRecord(void* pointer) {
unsigned int bytes = 0;
AcquireSRWLockExclusive(&allocationLock);
allocationRecord_t** link = &allocations;
while (*link != nullptr) {
if ((*link)->pointer == pointer) {
allocationRecord_t* const record = *link;
*link = record->next;
bytes = record->bytes;
std::free(record);
break;
}
link = &(*link)->next;
}
ReleaseSRWLockExclusive(&allocationLock);
if (bytes != 0) {
allocatedBytes.fetch_sub(bytes, std::memory_order_relaxed);
}
return bytes;
}
}
idMem mem;
idMemLocal memLocal;
const char* GetMemTagName(const int tag) {
return tag >= 0 && tag < TAG_NUM_TAGS ? tagNames[tag] : "<BAD TAG NAME>";
}
void* idMem::AllocWithLocation(const char* const location,
const unsigned int size, const memTag_t tag, const bool zeroBuffer,
const align_t alignment, const heapType_t requestedHeap) {
const std::size_t allocationSize = size == 0 ? 1u : size;
const std::size_t requestedAlignment = static_cast<std::size_t>(alignment);
const std::size_t safeAlignment = requestedAlignment < sizeof(void*)
? sizeof(void*) : requestedAlignment;
void* pointer = _aligned_malloc(allocationSize, safeAlignment);
if (pointer == nullptr) {
idOutOfMemoryCallback callback = outOfMemoryCallback.load(
std::memory_order_acquire
);
if (callback != nullptr && callback()) {
pointer = _aligned_malloc(allocationSize, safeAlignment);
}
}
if (pointer == nullptr) {
return nullptr;
}
if (zeroBuffer) {
std::memset(pointer, 0, allocationSize);
}
const heapType_t usedHeap = requestedHeap == HEAP_DEFAULTHEAP
? currentHeap : requestedHeap;
AddRecord(pointer, size, tag, usedHeap, location);
return pointer;
}
void idMem::Free(void* const pointer, const align_t) {
if (pointer == nullptr) {
return;
}
RemoveRecord(pointer);
_aligned_free(pointer);
}
int idMem::BytesCurrentlyAllocated() const {
const unsigned long long bytes = allocatedBytes.load(std::memory_order_relaxed);
return bytes > static_cast<unsigned long long>((std::numeric_limits<int>::max)())
? (std::numeric_limits<int>::max)() : static_cast<int>(bytes);
}
void idMem::InitMapHeap() {
}
void idMem::ResetMapHeap() {
// The PC port uses the process CRT heap for both logical heaps. Allocations
// remain individually tracked so stale map allocations can still be found.
}
void idMem::PushHeap(const heapType_t heapType) {
if (heapStackDepth++ == 0) {
pushedHeap = currentHeap;
currentHeap = heapType == HEAP_DEFAULTHEAP ? HEAP_MAPHEAP : heapType;
}
}
void idMem::PopHeap() {
if (heapStackDepth <= 0) {
heapStackDepth = 0;
return;
}
if (--heapStackDepth == 0) {
currentHeap = pushedHeap;
}
}
bool idMem::IsGlobalHeap() const {
return currentHeap == HEAP_SYSTEMHEAP;
}
void idMem::SetOutOfMemoryCallback(const idOutOfMemoryCallback callback) {
outOfMemoryCallback.store(callback, std::memory_order_release);
}
idOutOfMemoryCallback idMem::GetOutOfMemoryCallback() const {
return outOfMemoryCallback.load(std::memory_order_acquire);
}
void idMem::WriteMemoryReport(const char* const directory,
const char* const fileName) const {
char path[MAX_PATH];
const char* const safeDirectory = directory == nullptr ? "." : directory;
const char* const safeFile = fileName == nullptr ? "memory_report.txt" : fileName;
std::snprintf(path, sizeof(path), "%s\\%s", safeDirectory, safeFile);
FILE* report = nullptr;
if (fopen_s(&report, path, "w") != 0 || report == nullptr) {
return;
}
std::fprintf(report, "bytes currently allocated: %d\n", BytesCurrentlyAllocated());
AcquireSRWLockShared(&allocationLock);
for (allocationRecord_t* record = allocations; record != nullptr;
record = record->next) {
std::fprintf(report, "%p %10u %-24s heap=%d %s\n", record->pointer,
record->bytes, GetMemTagName(record->tag), record->heap,
record->location == nullptr ? "<unknown>" : record->location);
}
ReleaseSRWLockShared(&allocationLock);
std::fclose(report);
}