Cleanup part 2

This commit is contained in:
Musa Mahmood 2025-11-23 04:08:11 -05:00
parent 5e3ce038b5
commit d42bf63d41
23 changed files with 201 additions and 315 deletions

View File

@ -1,50 +0,0 @@
[#TODO]
[ ] Base layer and OS abstraction for Windows. (Ryan's Win32 platform layer is ~2k lines of code.)
• ryan uses os_thread_launch (CreateThread) /join (WaitForSingleObject+CloseHandle)/detach (CloseHandle ?? what type?)
See: base_threads
just implement the stuff we need for:
He launches a bunch of async threads from `main_thread_base_entry_point`
• Creating / destroying threads.
• rdtsc for timing
• Thread primitives (Mutex, Semaphore, etc.)
• Thread Creation / Deletion
[ ] CreateWindow and other Windows-specific features for handling messaging.
[ ] Context should include links to other living thread data (+ save dead threads in debug mode)
[ ] push_scratch() macro. (basically an auto_release_temp)
[ ] Start removing irrelevant header files (for unity build system)
[ ] ... Mouse / Keyboard inputs
[*] This is the order I want to follow, because I need them in thread context.
- Entry point(s) `Entry_Point_Main.cpp`
-> We can have a lib_init() instead of a clear entry point for now.
-> Switch from library to application once I add an entry point
-> See how rjf abstracts his entry points for each platform with TCTX.
[Design Stuff]
[ ] "Primary Thread" <- Main entry point thread
[ ] "Secondary Thread" <- does all the heavy lifting for the first thread and synchronizes with the first.
[ ] Tertiary threads (launched by secondary thread and awaiting work as assigned by Primary and Secondary threads)
[ ] Multi-threaded by default with sync points
[ ] Need bindings for Semaphore and Mutex
[ ] Does calling SetProcessWorkingSetSize(.., -1, -1) cause the program to crash? Why tho.
[ ] Maybe we just shouldn't do this?
[ ] *our* program memory usage can be calculated by Stack Size + allocations from GPAllocator + allocations from arena.
[ ] I want to be able to create new Windows (instances) of the program that can communicate with one another.
I want one program to be the "Main" and always in-tray, and the others to be "child instances"
[TODO - Low priority]
[ ] Fix implementation so all source is just in .cpp files (including templates), and auto-generate forward declares like Vjekoslav does.
[ ] Implement Secure Arenas (with VirtualLock, wiping memory with RtlSecureZeroMemory)
[ ] Way to serialize and deserialize settings in Text (I think using binary is a mistake here?).
[Project Ideas] - order of operations:
[ ] Pushbullet replacement should make it easy to understand how data can be securely moved over the internet via a central server.
[ ] Signal replacement
[ ](Graphics): How do I create a basic overlay that shows the time, date, cpu/gpu temps, frequency, memory usage all the time and responds to global hotkey to show more info
[Documentation Notes]
Main inspirations for this codebase:
(1) jonathan blow & jai compiler project
(2) casey muratori and "handmade" approach to software
(3) ryan fleury and raddebugger codebase
Always contemplate your impulses

View File

@ -1,6 +1,3 @@
#include "Allocator.h"
#include "Base_Thread_Context.h"
void* internal_alloc (s64 size) {
Allocator allocator = get_context_allocator();
void* result = allocator.proc(Allocator_Mode::ALLOCATE, size, 0, nullptr, allocator.data);

View File

@ -2,8 +2,6 @@
#include "Base.h"
#include "error-codes.h"
enum class Allocator_Mode: s32 {
ALLOCATE = 0,
RESIZE = 1,
@ -103,3 +101,4 @@ template <typename T> T* copy_struct(T* src) {
T* dst = New<T>(false);
memcpy(dst, src, sizeof(T));
}

View File

@ -1,5 +1,3 @@
#pragma once
#include "Arena.h"
#include "Arena_Windows.cpp"

View File

@ -1,9 +1,5 @@
#pragma once
#include "Allocator.h"
#include "Base.h"
#include "Base_Thread_Context.h"
#if OS_WINDOWS
constexpr u32 ARENA_DEFAULT_COMMIT_PAGE_COUNT = 16; // 16 * 4k page = 64kB
constexpr s64 ARENA_DEFAULT_COMMIT_SIZE_BYTES = 65536;
@ -99,10 +95,6 @@ void extend_committed_pages (Arena* arena, u8* end);
void free_pages_down_to (Arena* arena, s64 pages_to_keep);
void arena_delete (Arena* arena);
// Scoped Macros/Functions for auto_reset and auto_release
// usage `Auto_Reset guard(arena);` within a scope.
#define auto_reset(x) \
Auto_Reset Concat(_auto_reset_guard_, __LINE__)(x)
struct Auto_Reset {
Arena* arena;
u8* starting_point;
@ -118,10 +110,6 @@ struct Auto_Reset {
}
};
#define auto_release_temp() auto_release(get_temp_allocator());
#define auto_release(x) \
Auto_Release Concat(_auto_release_guard_, __LINE__)(x)
struct Auto_Release {
Arena* arena;
u8* starting_point;
@ -138,8 +126,6 @@ struct Auto_Release {
}
};
#define push_alignment(x, y) \
Push_Alignment Concat(_push_align_guard_, __LINE__)(x, y)
struct Push_Alignment { // #rename to Arena_Push_Alignment?
Arena* arena;
u16 original_alignment;
@ -156,24 +142,6 @@ struct Push_Alignment { // #rename to Arena_Push_Alignment?
}
};
#define push_arena(x) \
Push_Arena Concat(_push_alloc_guard_, __LINE__)(x)
struct Push_Arena {
Thread_Context* context;
Allocator original_allocator;
Push_Arena(Arena* arena) {
Assert(is_valid(arena));
context = get_thread_context();
Assert(context != nullptr);
original_allocator = context->allocator;
context->allocator = get_allocator(arena);
}
~Push_Arena() {
context->allocator = original_allocator;
}
};
// Do this later:
// arena_lock_pages :: (using arena: *Arena, start_address: *u8, byte_count: s64)

View File

@ -1,8 +1,5 @@
#pragma once
#include "Allocator.h"
#include "Arena.h"
constexpr s64 ARRAY_ARENA_START_OFFSET = 64;
template <typename T>
@ -13,6 +10,8 @@ struct ArenaArray { // downcasts to an ArrayView.
s64 allocated;
Arena* arena; // We can probably assume arena is &Array-32
ArenaArray() {}
T& operator[] (s64 index) {
#if ARRAY_ENABLE_BOUNDS_CHECKING
if (index < 0 || index >= count) { debug_break(); } // index out of bounds
@ -124,32 +123,32 @@ template <typename T> force_inline void maybe_grow (ArenaArray<T>& array) {
}
template <typename T> force_inline void array_reserve (ArenaArray<T>& array, s64 desired_item_count) {
reserve_internal((ArenaArray<void>&)array, desired_item_count, sizeof(T));
reserve_internal((ArenaArray<u8>&)array, desired_item_count, sizeof(T));
}
template <typename T> void array_resize (ArenaArray<T>& array, s64 desired_item_count, bool initialize) {
s64 old_count = array.count;
reserve_internal((ArenaArray<void>&)array, desired_item_count, sizeof(T));
reserve_internal((ArenaArray<u8>&)array, desired_item_count, sizeof(T));
array.count = desired_item_count;
if (initialize) {
init_range(array.data, old_count, desired_item_count);
}
}
s64 max_array_size (ArenaArray<void>& array) {
return reserve_size(array.arena) - sizeof(Arena) - sizeof(ArenaArray<void>);
s64 max_array_size (ArenaArray<u8>& array) {
return reserve_size(array.arena) - sizeof(Arena) - sizeof(ArenaArray<u8>);
}
void array_arena_realloc (ArenaArray<void>& array, s64 new_size, s64 old_size) {
void array_arena_realloc (ArenaArray<u8>& array, s64 new_size, s64 old_size) {
Assert(new_size <= max_array_size(array));
void* array_begin = (void*)array.data;
void* result_end = array_begin + new_size;
u8* array_begin = (u8*)array.data;
void* result_end = (void*)(array_begin + new_size);
// Check if we need more pages:
if (result_end > array.arena.first_uncommitted_page) {
if (result_end > array.arena->first_uncommitted_page) {
// Critical error if we run out of address space!
if (result_end > address_limit(array.arena)) {
if (result_end > arena_address_limit(array.arena)) {
// #TODO Log error.
Assert(false); // Failed to allocate because Arena is full and cannot expand
return;
@ -159,7 +158,7 @@ void array_arena_realloc (ArenaArray<void>& array, s64 new_size, s64 old_size) {
}
}
void reserve_internal (ArenaArray<void>& array, s64 desired_item_count, s64 element_size) {
void reserve_internal (ArenaArray<u8>& array, s64 desired_item_count, s64 element_size) {
if (desired_item_count <= array.allocated) return;
array_arena_realloc(array, desired_item_count * element_size, array.allocated * element_size);
@ -251,20 +250,3 @@ template <typename T> ArrayView<T> array_copy_as_view (ArenaArray<T>& array) {
return view;
}
// [x] initialize_range
// [x] max_array_size
// [x] maybe_grow
// [x] resize
// [x] reserve
// [x] reserve_internal
// [x] array_arena_realloc
// [x] reset_keeping_memory
// [x] ordered_remove_by_index
// [x] unordered_remove_by_index
// [x] insert_at
// [x] pop
// [x] peek
// [x] peek_pointer
// [x] delete_range
// [x] array_copy

View File

@ -1,7 +1,4 @@
// API in Arena.h
#include "Arena.h"
#include "Array.h"
#include "General_Purpose_Allocator.h"
#include <mutex>
global std::mutex arena_table_mutex;

View File

@ -1,9 +1,6 @@
// Strongly influenced by Array.jai in Basic module.
#pragma once
#include "Base.h"
#include "Allocator.h"
// #TODO: Array.h
// [x] Set allocations to use context.allocator interface
// For now, I'm just disabling alignment:

View File

@ -1,3 +0,0 @@
#include "Base.h"
// #TODO Split these up into platform_base (e.g. Windows_Base, Unix_Base...)

View File

@ -175,3 +175,16 @@ force_inline s64 Next_Power_Of_Two(s64 v) {
#define ForArrayStartingAt(_it_, _array_, _start_) for (s64 _it_ = _start_; _it_ < (_array_).count; _it_ += 1)
#define ForUpTo(_it_, _end_) for (s64 _it_ = 0; _it_ < _end_; _it_ += 1)
// Scoped Macros/Functions for auto_reset and auto_release
// usage `Auto_Reset guard(arena);` within a scope.
#define auto_reset(x) \
Auto_Reset Concat(_auto_reset_guard_, __LINE__)(x)
#define push_allocator(x) Push_Allocator Concat(_push_alloc_guard_, __LINE__)(x)
#define push_alignment(x, y) \
Push_Alignment Concat(_push_align_guard_, __LINE__)(x, y)
#define push_arena(x) \
Push_Arena Concat(_push_alloc_guard_, __LINE__)(x)
#define auto_release_temp() auto_release(get_temp_allocator());
#define auto_release(x) \
Auto_Release Concat(_auto_release_guard_, __LINE__)(x)

View File

@ -1,44 +0,0 @@
#pragma once
#include "Base.h"
// Need to sort out how formatted strings and string builders are allocated
// Maybe just use context.allocator?
// What about temp strings? use context.temp?
struct string {
s64 count;
u8* data;
// Construct from a string literal or C-string
string () { // default constructor
count = 0;
data = nullptr;
}
string (char* cstr) {
count = strlen(cstr);
data = (u8*)cstr;
}
string (s64 _count, char* str) { count = _count; data = (u8*)str; }
string (s64 _count, u8* str) { count = _count; data = str; }
};
// ~ API ~ #TODO
string copy_string (string str);
bool strings_match(string first_string, string second_string);
// Unicode stuff
string wide_to_utf8 (u16* source, s32 length);
// string string_view(string n_string, int start_index, int view_count);
// string copy_string(char* c_string);
// void free(string& n_string);
bool is_valid(string n_string);
bool is_c_string(string n_string);
char* to_c_string(string n_string);
string format_string(char* format, ...);
string string_from_literal(char* literal);

View File

@ -1,4 +1,63 @@
#include "Base_Thread_Context.h"
// See Context_Base in jai, and TCTX in raddebugger:
struct Thread_Context {
Arena* temp; // Used for temporary allocations, scratch space.
Arena* arena; // general purpose local arena
Allocator allocator;
s32 thread_idx;
u16 _padding0;
u16 GPAllocator_alignment = 16;
// Logger logger;
// Stack_Trace* stack_trace;
// #TODO: other debug information
// #TODO:
// Array<Thread*> threads_created; // maybe should be linked-list?
// Thread* thread_that_created_me = nullptr; // so we can remove from above array
// Mutex thread_context_mutex;
string thread_name;
};
Thread_Context* get_thread_context();
struct Push_Arena {
Thread_Context* context;
Allocator original_allocator;
Push_Arena(Arena* arena) {
Assert(is_valid(arena));
context = get_thread_context();
Assert(context != nullptr);
original_allocator = context->allocator;
context->allocator = get_allocator(arena);
}
~Push_Arena() {
context->allocator = original_allocator;
}
};
struct Push_Allocator {
Thread_Context* context;
Allocator old_allocator;
Push_Allocator (Allocator new_allocator) {
context = get_thread_context();
old_allocator = context->allocator;
context->allocator = new_allocator;
}
~Push_Allocator () {
context->allocator = old_allocator;
}
};
// Thread-local allocators:
PROTOTYPING_API Allocator get_temp_allocator();
PROTOTYPING_API Allocator get_context_allocator();
// C_LINKAGE thread_static TCTX* tctx_thread_local;
thread_static Thread_Context* thread_local_context;

View File

@ -1,55 +0,0 @@
#pragma once
#include "Allocator.h"
#include "Base.h"
// #include "Arena.h"
struct Arena; // #TEMP - Base_Thread_Context and Arena rely on each other, so I have to figure out how to make this less messy (maybe with meta-generated forward declares?)
// See Context_Base in jai, and TCTX in raddebugger:
struct Thread_Context {
Arena* temp; // Used for temporary allocations, scratch space.
Arena* arena; // general purpose local arena
Allocator allocator;
s32 thread_idx;
u16 _padding0;
u16 GPAllocator_alignment = 16;
// Logger logger;
// Stack_Trace* stack_trace;
// #TODO: other debug information
// #TODO:
// Array<Thread*> threads_created; // maybe should be linked-list?
// Thread* thread_that_created_me = nullptr; // so we can remove from above array
// Mutex thread_context_mutex;
string thread_name;
};
Thread_Context* get_thread_context();
// maybe should append line number to guard?
// This is stupid, and maybe should be something generated by a metaprogram?
#define push_allocator(x) Push_Allocator Concat(_push_alloc_guard_, __LINE__)(x)
struct Push_Allocator {
Thread_Context* context;
Allocator old_allocator;
Push_Allocator (Allocator new_allocator) {
context = get_thread_context();
old_allocator = context->allocator;
context->allocator = new_allocator;
}
~Push_Allocator () {
context->allocator = old_allocator;
}
};
// Thread-local allocators:
PROTOTYPING_API Allocator get_temp_allocator();
PROTOTYPING_API Allocator get_context_allocator();
// PROTOTYPING_API void reset_temp_allocator();
// PROTOTYPING_API void free_temp_allocator();

View File

@ -1,4 +1,3 @@
#include "General_Purpose_Allocator.h"
#include "Basic.h"
#include <cmath>

View File

@ -1,8 +1,5 @@
#pragma once
#include "error-codes.h"
#include "Array.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -145,4 +142,4 @@ struct Complex32 { f32 real; f32 imag; };
Complex exponential (Complex cx);
Complex conjugate (Complex cx);
f64 fabs(Complex cx);
f64 fabs (Complex cx);

View File

@ -1,4 +1,57 @@
#include "error-codes.h"
enum ErrorSeverity: s32 {
SEVERITY_WARNING = 0,
SEVERITY_NON_FATAL = 1,
SEVERITY_FATAL = 2
};
// typedef struct string Native_Error;
// Note: Native_Error should down-cast to a string.
struct Native_Error {
s64 count;
u8* data;
ErrorSeverity severity = SEVERITY_WARNING;
};
#define Null_Pointer_Check(arg) \
if (arg == nullptr) { \
return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is a null pointer.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); \
}
#define Array_Check(arg) \
if (!is_valid(arg)) { \
return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is not a valid array.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); \
}
#define String_Check(arg) \
if (!Is_Valid(arg)) { return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is not a valid string.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); }
#define Error_Check(error) \
if (error != nullptr) { \
return error; \
}
// An error from which the program cannot continue (e.g. a segmentation fault)
#define New_Fatal_Error(message) \
New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s.", __FILE__, __LINE__, __FUNCTION__, message)
#define New_Error(message) \
New_Error_Internal("%s:%d\n[%s] Error: %s.", __FILE__, __LINE__, __FUNCTION__, message)
#define New_Warning(message) \
New_Warning_Internal("%s:%d\n[%s] Warning: %s.", __FILE__, __LINE__, __FUNCTION__, message)
Native_Error* New_Fatal_Error_Internal(char* raw_message, ...);
Native_Error* New_Error_Internal(char* raw_message, ...);
Native_Error* New_Warning_Internal(char* raw_message, ...);
Native_Error* Native_Error_Callstack(Native_Error* new_error, Native_Error* old_error, ErrorSeverity severity);
PROTOTYPING_API C_API Native_Error* Cleanup_Error(Native_Error* error);
PROTOTYPING_API C_API Native_Error* Native_Error_Test();
#include "General_Purpose_Allocator.h"
#include <stdio.h> // vsnprintf, printf
@ -12,7 +65,7 @@
Native_Error* Create_New_Native_Error_Internal(char* format, va_list args) {
constexpr s64 ERROR_BUFFER_COUNT = 512;
push_allocator(GPAllocator());
// push_allocator(GPAllocator());
auto error = New<Native_Error>(false);
error->data = (u8*)GPAllocator_New(ERROR_BUFFER_COUNT);
@ -44,7 +97,7 @@ Native_Error* New_Fatal_Error_Internal(char* format, ...) {
}
Native_Error* Native_Error_Callstack(Native_Error* new_error, Native_Error* old_error, ErrorSeverity severity) {
push_allocator(GPAllocator());
// push_allocator(GPAllocator());
auto error_message = format_string("%s\n > %s", new_error->data, old_error->data).data;

View File

@ -1,5 +1,3 @@
#include "General_Purpose_Allocator.h"
#include <string.h>
#if GP_ALLOCATOR_TRACK_ALLOCATIONS

View File

@ -1,8 +1,5 @@
#pragma once
#include "Base.h"
#include "Array.h"
#define GP_ALLOCATOR_TRACK_ALLOCATIONS BUILD_DEBUG
#define GP_ALLOCATOR_VERY_DEBUG 0

View File

@ -1,6 +1,46 @@
// #TODO: Integrate Allocator / context.allocator
#include "Base_String.h"
// Need to sort out how formatted strings and string builders are allocated
// Maybe just use context.allocator?
// What about temp strings? use context.temp?
struct string {
s64 count;
u8* data;
// Construct from a string literal or C-string
string () { // default constructor
count = 0;
data = nullptr;
}
string (char* cstr) {
count = strlen(cstr);
data = (u8*)cstr;
}
string (s64 _count, char* str) { count = _count; data = (u8*)str; }
string (s64 _count, u8* str) { count = _count; data = str; }
};
// ~ API ~ #TODO
string copy_string (string str);
bool strings_match(string first_string, string second_string);
// Unicode stuff
string wide_to_utf8 (u16* source, s32 length);
// string string_view(string n_string, int start_index, int view_count);
// string copy_string(char* c_string);
// void free(string& n_string);
bool is_valid(string n_string);
bool is_c_string(string n_string);
char* to_c_string(string n_string);
string format_string(char* format, ...);
string string_from_literal(char* literal);
#include "General_Purpose_Allocator.h"
#include <stdio.h> // vsnprintf
@ -107,4 +147,5 @@ string wide_to_utf8 (u16* source, s32 length) {
}
return utf8_string;
}
}

View File

@ -1,58 +0,0 @@
#pragma once
#include "Base.h"
enum ErrorSeverity: s32 {
SEVERITY_WARNING = 0,
SEVERITY_NON_FATAL = 1,
SEVERITY_FATAL = 2
};
#include "Base_String.h"
// typedef struct string Native_Error;
// Note: Native_Error should down-cast to a string.
struct Native_Error {
s64 count;
u8* data;
ErrorSeverity severity = SEVERITY_WARNING;
};
#define Null_Pointer_Check(arg) \
if (arg == nullptr) { \
return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is a null pointer.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); \
}
#define Array_Check(arg) \
if (!is_valid(arg)) { \
return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is not a valid array.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); \
}
#define String_Check(arg) \
if (!Is_Valid(arg)) { return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is not a valid string.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); }
#define Error_Check(error) \
if (error != nullptr) { \
return error; \
}
// An error from which the program cannot continue (e.g. a segmentation fault)
#define New_Fatal_Error(message) \
New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s.", __FILE__, __LINE__, __FUNCTION__, message)
#define New_Error(message) \
New_Error_Internal("%s:%d\n[%s] Error: %s.", __FILE__, __LINE__, __FUNCTION__, message)
#define New_Warning(message) \
New_Warning_Internal("%s:%d\n[%s] Warning: %s.", __FILE__, __LINE__, __FUNCTION__, message)
Native_Error* New_Fatal_Error_Internal(char* raw_message, ...);
Native_Error* New_Error_Internal(char* raw_message, ...);
Native_Error* New_Warning_Internal(char* raw_message, ...);
Native_Error* Native_Error_Callstack(Native_Error* new_error, Native_Error* old_error, ErrorSeverity severity);
PROTOTYPING_API C_API Native_Error* Cleanup_Error(Native_Error* error);
PROTOTYPING_API C_API Native_Error* Native_Error_Test();

View File

@ -1,9 +0,0 @@
#pragma once
#include "Base.h"
#include "error-codes.h"
#include "General_Purpose_Allocator.h"
#include "Array.h"
#include "Basic.h"

View File

@ -136,7 +136,7 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) {
}
}
debug_break();
// debug_break();
{ OS_Process_Info* info = &os_state_w32.process_info;
DWORD length = GetCurrentDirectoryW(0, 0);

View File

@ -1,10 +1,20 @@
#include "lib/Base/Base.cpp"
// This is quite disorganized. There must be a better way to do this by moving the stuff that requires forward declaration to the top
// with a metaprogram.
#include "lib/Base/Base.h"
#include "lib/Base/Allocator.h"
#include "lib/Base/Array.h"
#include "lib/Base/General_Purpose_Allocator.h"
#include "lib/Base/Arena.h"
#include "lib/Base/Arena_Array.h"
#include "lib/Base/String.cpp"
#include "lib/Base/ErrorCodes.cpp"
#include "lib/Base/Arena.cpp"
#include "lib/Base/Arena_Table.cpp"
#include "lib/Base/Base_Thread_Context.cpp"
#include "lib/Base/General_Purpose_Allocator.cpp"
#include "lib/Base/Allocator.cpp"
#include "lib/Base/General_Purpose_Allocator.cpp"
#include "lib/Base/Basic.cpp"
#include "lib/Base/ErrorCodes.cpp"
#include "lib/Base/String.cpp"
#include "lib/Base/Timing.cpp"