Cleanup TODOs, refactoring, major bug fixes

This commit is contained in:
Musa Mahmood 2025-12-06 22:16:46 -05:00
parent 7755ef9225
commit 5c18e6f4da
18 changed files with 128 additions and 119 deletions

View File

@ -43,8 +43,6 @@ void* internal_alloc (s64 size);
void internal_free (void* memory); void internal_free (void* memory);
void* internal_realloc (void* memory, s64 size, s64 old_size); void* internal_realloc (void* memory, s64 size, s64 old_size);
template <typename T> force_inline void Initialize (T* memory) { (*memory) = T(); }
template <typename T> T* New (Allocator allocator, bool initialize=true) { template <typename T> T* New (Allocator allocator, bool initialize=true) {
auto memory = (T*)allocator.proc(Allocator_Mode::ALLOCATE, sizeof(T), 0, nullptr, allocator.data); auto memory = (T*)allocator.proc(Allocator_Mode::ALLOCATE, sizeof(T), 0, nullptr, allocator.data);
@ -105,20 +103,19 @@ force_inline void Delete (Allocator allocator, void* memory) {
// current allocator on the context. // current allocator on the context.
// For Resizes and Deletes, use internal_realloc and internal_free. // For Resizes and Deletes, use internal_realloc and internal_free.
template <typename T> void reset_struct (T* src) { // #NOTE: Initialize<T> and reset_struct<T> are exactly the same!
(*src) = T(); template <typename T> force_inline void Initialize (T* memory) { (*memory) = T(); }
} template <typename T> force_inline void reset_struct (T* src) { (*src) = T(); }
template <typename T> void zero_struct(T* src) { template <typename T> void zero_struct (T* src) {
memset(src, 0, sizeof(T)); memset(src, 0, sizeof(T));
} }
template <typename T> void poison_struct(T* src) { template <typename T> void poison_struct (T* src) {
memset(src, 0xCD, sizeof(T)); memset(src, 0xCD, sizeof(T));
} }
template <typename T> T* copy_struct(T* src) { template <typename T> T* copy_struct (T* src) {
T* dst = New<T>(false); T* dst = New<T>(false);
memcpy(dst, src, sizeof(T)); memcpy(dst, src, sizeof(T));
} }

View File

@ -209,9 +209,9 @@ struct Auto_Reset {
this->starting_point = arena->current_point; this->starting_point = arena->current_point;
} }
// #TODO: Implement with ExpandableArena // #TODO: Implement with ExpandableArena (probably just use the same implementation as Auto_Release?)
// Auto_Reset(ExpandableArena* arena_ex) { // Auto_Reset(ExpandableArena* arena_ex) {
// Auto_Reset((Arena*)arena_ex); //
// } // }
~Auto_Reset() { ~Auto_Reset() {

View File

@ -3,7 +3,7 @@
constexpr s64 ARRAY_ARENA_START_OFFSET = 64; constexpr s64 ARRAY_ARENA_START_OFFSET = 64;
template <typename T> template <typename T>
struct ArenaArray { // downcasts to an ArrayView. struct ArenaArray { // #downcasts to an ArrayView.
using ValueType = T; using ValueType = T;
s64 count; s64 count;
T* data; T* data;

View File

@ -6,7 +6,7 @@
MSVC_RUNTIME_CHECKS_OFF MSVC_RUNTIME_CHECKS_OFF
template <typename T> template <typename T>
struct Array { // downcasts to an ArrayView. struct Array { // #downcasts to an ArrayView.
using ValueType = T; using ValueType = T;
s64 count; s64 count;
T* data; T* data;
@ -249,12 +249,17 @@ struct ArrayView {
s64 count; s64 count;
T* data; T* data;
// #TODO: Add initializers ArrayView<u8> from string, ArrayView<T> from ArenaArray<T> ArrayView(Array<T> array) { // auto-#downcast from Array<T>
ArrayView(Array<T> array) { // auto-downcast from Array<T>
count = array.count; count = array.count;
data = array.data; data = array.data;
} }
// Unfortunately we need ArenaArray to be declared ahead. God, I hate C++.
// ArrayView(ArenaArray<T> array) { // auto-#downcast from ArenaArray<T>
// count = array.count;
// data = array.data;
// }
ArrayView() { count = 0; data = nullptr; } ArrayView() { count = 0; data = nullptr; }
ArrayView(s64 new_count, bool initialize=true) { ArrayView(s64 new_count, bool initialize=true) {

View File

@ -192,7 +192,6 @@ force_inline s64 Next_Power_Of_Two(s64 v) {
#define context_allocator() thread_context()->allocator #define context_allocator() thread_context()->allocator
#define context_logger() &thread_context()->logger #define context_logger() &thread_context()->logger
// #TODO #constexpr #MATH - make these constexpr
// CHECK THAT THESE ARE CORRECT! // CHECK THAT THESE ARE CORRECT!
constexpr f32 TAU = 6.283185f; constexpr f32 TAU = 6.283185f;
constexpr f64 TAU_64 = 6.28318530717958648; constexpr f64 TAU_64 = 6.28318530717958648;

View File

@ -1,6 +1,3 @@
// #NOTE: To keep things simple, all allocations for Error should be via GPAllocator.
// We really allocate two things: the Error struct and the error string copy.
#define NO_ERROR nullptr #define NO_ERROR nullptr
enum class ErrorClass: s32 { enum class ErrorClass: s32 {
@ -19,6 +16,8 @@ struct Error {
s32 source_line; s32 source_line;
string file_path; string file_path;
string function_name; string function_name;
string thread_name;
f64 timestamp;
// Linked list to errors // Linked list to errors
Error* previous_error; // if we're passing errors up the callstack. Error* previous_error; // if we're passing errors up the callstack.
@ -70,10 +69,6 @@ Error* new_error (ErrorClass severity, string error_string) {
return error; return error;
} }
// #TODO: // void OS_Log_Error_With_Code // OS-SPECIFIC CALLING GetLastError, etc.
// Then remove all instances of log_error_code_and_string()
//
void Log_Error_2 (string file_path, string function_name, s32 line_number, ErrorClass severity, string fmt, ...) { void Log_Error_2 (string file_path, string function_name, s32 line_number, ErrorClass severity, string fmt, ...) {
auto tctx = thread_context(); auto tctx = thread_context();
Assert(tctx != nullptr); Assert(tctx != nullptr);
@ -100,10 +95,30 @@ void Log_Error_2 (string file_path, string function_name, s32 line_number, Error
// Note: we don't need to assign previous_error or next_error, as that is done by the thread_context when we #push_error // Note: we don't need to assign previous_error or next_error, as that is done by the thread_context when we #push_error
error->previous_error = nullptr; error->previous_error = nullptr;
error->next_error = nullptr; error->next_error = nullptr;
error->thread_name = copy_string(tctx->thread_name);
error->timestamp = GetUnixTimestamp();
push_error(tctx, error); push_error(tctx, error);
} }
Error* copy_error (Thread_Context* tctx, Error* old_error) {
push_arena(tctx->error_arena);
Error* error = new_error(old_error->severity, to_string(old_error));
error->thread_id = old_error->thread_id;
error->source_line = old_error->source_line;
error->file_path = copy_string(old_error->file_path);
error->function_name = copy_string(old_error->function_name);
// Note: we don't need to assign previous_error or next_error, as that is done by the thread_context when we #push_error
error->previous_error = nullptr;
error->next_error = nullptr;
error->thread_name = copy_string(old_error->thread_name);
error->timestamp = old_error->timestamp;
return error;
}
void push_error (Thread_Context* tctx, Error* new_error) { void push_error (Thread_Context* tctx, Error* new_error) {
Assert(tctx == thread_context()); // Not a real assert, just wondering if we'll ever call this with a non-local context? Assert(tctx == thread_context()); // Not a real assert, just wondering if we'll ever call this with a non-local context?
Assert(new_error != nullptr); Assert(new_error != nullptr);
@ -191,7 +206,8 @@ void push_errors_to_parent_thread (Thread_Context* tctx) {
Assert(tctx->parent_thread_context); Assert(tctx->parent_thread_context);
while (tctx->first_error) { while (tctx->first_error) {
push_error_no_context(tctx->parent_thread_context, tctx->first_error); Error* error_copy = copy_error(tctx->parent_thread_context, tctx->first_error);
push_error_no_context(tctx->parent_thread_context, error_copy);
clear_error(tctx, tctx->first_error); clear_error(tctx, tctx->first_error);
} }
} }
@ -206,9 +222,11 @@ ArrayView<Error*> get_all_errors (Thread_Context* tctx) {
} }
for_each(t, tctx->child_threads) { // #TODO(Low priority): also recurse through child threads?
// #TODO: also recurse through child threads? // NOTE: I don't think we actually want this, because we merge
} // our errors on the main thread when we thread_deinit.
// for_each(t, tctx->child_threads) {
// }
return error_array; return error_array;
} }

View File

@ -51,8 +51,9 @@ u32 table_hash_function_knuth (void* key, s64 size) {
u32 string_hash_function_fnv1a (void* key, s64 size) { u32 string_hash_function_fnv1a (void* key, s64 size) {
Assert(size == sizeof(string)); Assert(size == sizeof(string));
string key_as_string = *((string*)key); string key_as_string = *((string*)key);
// #TODO It should be xor folded to the desired range rather than shifted. u64 hash_u64 = fnv1a_hash_any(key_as_string.data, key_as_string.count);
return (u32)(fnv1a_hash_any(key_as_string.data, key_as_string.count)); // It should be xor folded to the desired range rather than shifted:
return (u32)(hash_u64 ^ (hash_u64 >> 32));
} }
bool u64_keys_match (void* key1, void* key2) { bool u64_keys_match (void* key1, void* key2) {

View File

@ -1,30 +1,5 @@
// #NOTE: This Hash Table is borrowed from Jai's implementation! (With some tweaks) // #NOTE: This Hash Table is borrowed from Jai's implementation! (With some tweaks)
// I made my own version that's arena-backed, but the mechanisms are the same. // I made my own version that's arena-backed, but the mechanisms are the same.
// s32 drive_count = 0;
// for (s64 i = 0; i < drive_table->allocated; i += 1) {
// Table_Entry<string, OS_Drive>* entry = &drive_table->entries[i]; // we should take ptr here if we want to modify?
// if (entry->hash > HASH_TABLE_FIRST_VALID_HASH) {
// // #TODO #MOVE THIS + maybe don't check this every frame!
// // entry->value.is_present = Win32_Drive_Exists(entry->value.label);
// if (entry->value.label.data == nullptr) continue;
// Text(" > [%d] drive letter: %s (is_present: %d)", drive_count + 1, entry->value.label.data, entry->value.is_present);
// drive_count += 1;
// SameLine();
// push_allocator(temp());
// if (Button(format_cstring("Read NTFS MFT Raw##%s", entry->value.label.data))) {
// push_arena(thread_context()->arena);
// // auto_release(thread_context()->arena);
// auto dfs = NTFS_MFT_read_raw(entry->value.label);
// if (dfs == nullptr) {
// log("[NTFS_MFT_read_raw] operation failed");
// }
// }
// }
// }
typedef u32 hash_result; typedef u32 hash_result;
constexpr hash_result HASH_TABLE_FIRST_VALID_HASH = 2; constexpr hash_result HASH_TABLE_FIRST_VALID_HASH = 2;
@ -198,8 +173,6 @@ template <typename T, typename U> void table_resize (Table<T, U>* table, s64 slo
return; return;
} }
// #TODO: When you resize you need to reinsert all the values, so we
// need a temporary copy of the original data:
ArrayView<Table_Entry<T, U>> old_entries = table->entries; ArrayView<Table_Entry<T, U>> old_entries = table->entries;
s64 n = Next_Power_Of_Two(slots_to_allocate); s64 n = Next_Power_Of_Two(slots_to_allocate);
@ -290,10 +263,10 @@ template <typename T, typename U> bool table_remove (Table<T, U>* table, T key,
return false; return false;
} }
// #TODO: We should allow setting an allocator instead of defaulting to temp()?
template <typename T, typename U> ArrayView<U> table_find_multiple (Table<T, U>* table, T key, U* value) { template <typename T, typename U> ArrayView<U> table_find_multiple (Table<T, U>* table, T key, U* value) {
Array<U> results; Array<U> results;
results.allocator = temp(); // #NOTE: We should allow setting an allocator instead of defaulting to temp()?
// results.allocator = temp();
// #Walk_Table // #Walk_Table
u32 mask = (u32)(table->allocated - 1); u32 mask = (u32)(table->allocated - 1);

View File

@ -1,4 +1,4 @@
// #TODO #Logger module // #TODO(Low priority) #Logger module
// [ ] Add colored prints (See: Print_Color.jai) // [ ] Add colored prints (See: Print_Color.jai)
// See Logger.jai in our jiim-dev-gui project for how to do fancy colored text. // See Logger.jai in our jiim-dev-gui project for how to do fancy colored text.

View File

@ -54,7 +54,7 @@ force_inline void AddString16 (Serializer* serializer, string s) {
AddArray_NoSize(serializer, to_view(s)); AddArray_NoSize(serializer, to_view(s));
} }
struct Deserializer { // downcasts to ArrayView<u8> struct Deserializer { // #downcasts to ArrayView<u8>
s64 count; s64 count;
u8* data; u8* data;
s64 cursor; s64 cursor;

View File

@ -1,6 +1,5 @@
// #TODO #string module // #NOTE: All string building, printing and copying operations SHOULD null-terminate the
// [ ] I'm debating if string type should automatically null-terminate. // strings for backwards compatibility reasons. #FIX if something doesn't follow this rule!
// I personally do not like it, and think we should temp-copy c-strings as they're needed.
bool is_valid (string s) { bool is_valid (string s) {
return (s.data != nullptr && s.count > 0); return (s.data != nullptr && s.count > 0);
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
// #TODO: #strings: // #TODO: #strings:
// [x] Always null-terminate strings! // [ ] see: #Parsing stuff:
// [ ] How do I accept variadic arguments of any type to my print function? // [?] How do I accept variadic arguments of any type to my print function?
// [ ] Need to sort out how formatted strings and string builders are allocated // [ ] Need to sort out how formatted strings and string builders are allocated
// [ ] Separate functions for temp alloc (tprint??) // [ ] Separate functions for temp alloc (tprint??)
// [ ] I should also put path manipulation here or in a separate file? // [ ] I should also put path manipulation here or in a separate file?
@ -82,7 +82,6 @@ force_inline string string_view (string s, s64 start_index, s64 view_count);
bool strings_match (string first_string, string second_string); bool strings_match (string first_string, string second_string);
// #Unicode // #Unicode
// #TODO: Make a raw version that returns the raw pointer?
string wide_to_utf8 (u16* source, s32 length=-1); string wide_to_utf8 (u16* source, s32 length=-1);
wstring utf8_to_wide (string source); wstring utf8_to_wide (string source);

View File

@ -82,6 +82,6 @@ void graphics_set_render_target (Window_Info window_info) {
graphics->texture_render_target = nullptr; graphics->texture_render_target = nullptr;
graphics->current_window = window_info; graphics->current_window = window_info;
// #TODO: graphics_update_window :: // #TODO: #Graphics graphics_update_window ::
} }

View File

@ -87,7 +87,7 @@ ArrayView<OS_Drive*> os_get_available_drives () {
Table_Entry<string, OS_Drive*>* entry = &drive_table->entries[i]; // we should take ptr here if we want to modify? Table_Entry<string, OS_Drive*>* entry = &drive_table->entries[i]; // we should take ptr here if we want to modify?
if (entry->hash > HASH_TABLE_FIRST_VALID_HASH) { if (entry->hash > HASH_TABLE_FIRST_VALID_HASH) {
if (entry->value->label.data == nullptr) continue; // Some volumes may not be real and therefore have no label. if (entry->value->label.data == nullptr || !entry->value->is_present) continue; // Some volumes may not be real and therefore have no label.
array_add(drives, entry->value); array_add(drives, entry->value);
} }
@ -264,13 +264,15 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name=
s64 this_thread_index = InterlockedIncrement(&next_thread_index); s64 this_thread_index = InterlockedIncrement(&next_thread_index);
// 2. #NewContext Setup NEW thread local context // 2. #NewContext Setup NEW thread local context
ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16); ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16);
push_arena(arena_ex);
// #NOTE: we don't assign thread_local_context until we hit the #thread_entry_point // #NOTE: we don't assign thread_local_context until we hit the #thread_entry_point
thread->context = New<Thread_Context>(allocator(arena_ex)); thread->context = New<Thread_Context>();
thread->context->temp = expandable_arena_new(Arena_Reserve::Size_2M, 16); thread->context->temp = expandable_arena_new(Arena_Reserve::Size_2M, 16);
thread->context->arena = arena_ex; thread->context->arena = arena_ex;
thread->context->allocator = allocator(arena_ex); thread->context->allocator = allocator(arena_ex);
thread->context->thread_idx = (s32)this_thread_index; thread->context->thread_idx = (s32)this_thread_index;
// #NOTE: This will disappear once the thread is de-initted. If we want this string, copy it!
thread->context->thread_name = copy_string(thread_name); thread->context->thread_name = copy_string(thread_name);
thread->context->log_builder = new_string_builder(Arena_Reserve::Size_64M); thread->context->log_builder = new_string_builder(Arena_Reserve::Size_64M);
thread->context->error_arena = next_arena(Arena_Reserve::Size_64M); thread->context->error_arena = next_arena(Arena_Reserve::Size_64M);
@ -296,8 +298,8 @@ internal void thread_deinit (Thread* thread, bool zero_thread=false) {
if (thread->os_thread.windows_thread) { if (thread->os_thread.windows_thread) {
CloseHandle(thread->os_thread.windows_thread); CloseHandle(thread->os_thread.windows_thread);
}
thread->os_thread.windows_thread = nullptr; thread->os_thread.windows_thread = nullptr;
}
array_reset(*thread->context->log_builder); array_reset(*thread->context->log_builder);
free_string_builder(thread->context->log_builder); free_string_builder(thread->context->log_builder);
@ -392,7 +394,7 @@ internal string get_error_string (OS_Error_Code error_code) {
return trim_right(result); return trim_right(result);
} }
internal void log_error_code_and_string () { // #TODO: replace with call to log_error_with_code internal void os_log_error () {
OS_Error_Code error_code = GetLastError(); OS_Error_Code error_code = GetLastError();
log_error(" > GetLastError code: %d, %s", error_code, get_error_string(error_code).data); log_error(" > GetLastError code: %d, %s", error_code, get_error_string(error_code).data);
} }
@ -428,7 +430,7 @@ internal File file_open (string file_path, bool for_writing, bool keep_existing_
if (handle == INVALID_HANDLE_VALUE && log_errors) { if (handle == INVALID_HANDLE_VALUE && log_errors) {
// OS_Error_Code error_code = GetLastError(); // OS_Error_Code error_code = GetLastError();
log_error("Could not open file `%s`", file_path); log_error("Could not open file `%s`", file_path);
log_error_code_and_string(); os_log_error();
} }
File file; File file;
@ -445,7 +447,7 @@ internal bool file_read (File file, u8* data, s64 bytes_to_read_count, s64* byte
// ignore bytes_read_count if null. // ignore bytes_read_count if null.
if (data == nullptr) { if (data == nullptr) {
log_error("file_read called with null destination pointer."); log_error("file_read called with null destination pointer.");
log_error_code_and_string(); os_log_error();
if (bytes_read_count) (*bytes_read_count) = 0; if (bytes_read_count) (*bytes_read_count) = 0;
return false; return false;
} }
@ -624,8 +626,8 @@ monitor_enum_proc (HMONITOR hMonitor, HDC hdc, RECT* rect, LPARAM data) {
return true; return true;
} }
// #TODO: how do I setup a callback if monitors configuration is changed? // #TODO(Low Priority): how do I setup a callback if monitors configuration is changed?
// we handle WM_DISPLAYCHANGE or WM_DEVICECHANGE and call EnumDisplayMonitors // we handle WM_DISPLAYCHANGE or WM_DEVICECHANGE and call EnumDisplayMonitors again.
internal void os_enumerate_monitors () { internal void os_enumerate_monitors () {
if (!global_win32_state.system_info.monitors_enumerated) { if (!global_win32_state.system_info.monitors_enumerated) {
// should reset array? // should reset array?
@ -979,7 +981,7 @@ bool Win32_Discover_Drives () {
Table<string, OS_Drive*>* drive_table = get_drive_table(); Table<string, OS_Drive*>* drive_table = get_drive_table();
if (!drive_table->allocated) { if (!drive_table->allocated) {
drive_table->allocator = GPAllocator(); drive_table->allocator = GPAllocator();
// #TODO: #hash_table need a macro for string keys! // #TODO(Low priority): #hash_table need a macro for initializing with string keys!
drive_table->hash_function = string_hash_function_fnv1a; drive_table->hash_function = string_hash_function_fnv1a;
drive_table->compare_function = string_keys_match; drive_table->compare_function = string_keys_match;
s64 slots_to_allocate = 64; s64 slots_to_allocate = 64;
@ -990,7 +992,7 @@ bool Win32_Discover_Drives () {
u32 result_length = GetLogicalDriveStringsW(1024, (LPWSTR)lpBuf); u32 result_length = GetLogicalDriveStringsW(1024, (LPWSTR)lpBuf);
if (!result_length) { if (!result_length) {
log_error("GetLogicalDriveStringsW failed!"); log_error("GetLogicalDriveStringsW failed!");
log_error_code_and_string(); os_log_error();
return false; return false;
} }
@ -1048,7 +1050,7 @@ bool Win32_Discover_Drives () {
log(" - file_system: %s", wide_to_utf8(file_system_name).data); log(" - file_system: %s", wide_to_utf8(file_system_name).data);
} else { } else {
log_error("GetVolumeInformationW failed! (drive label: %s)", drive_label.data); log_error("GetVolumeInformationW failed! (drive label: %s)", drive_label.data);
log_error_code_and_string(); os_log_error();
drive->is_present = false; drive->is_present = false;
} }
} }
@ -1072,7 +1074,7 @@ string Win32_drive_letter (string any_path) {
return copy_string({1, any_path.data}); return copy_string({1, any_path.data});
} }
// #TODO: #window_creation // #TODO: #window_creation #window_manipulation
// [ ] resize_window // [ ] resize_window
// [ ] position_window // [ ] position_window
// [ ] toggle_fullscreen // [ ] toggle_fullscreen

View File

@ -164,7 +164,7 @@ Error* NTFS_MFT_read_raw (OS_Drive* drive) {
if (file_handle == INVALID_HANDLE_VALUE) { if (file_handle == INVALID_HANDLE_VALUE) {
log_error("CreateFileA failed on target %s", create_file_target.data); log_error("CreateFileA failed on target %s", create_file_target.data);
log_error_code_and_string(); os_log_error();
return nullptr; return nullptr;
} }
@ -272,7 +272,7 @@ Error* NTFS_MFT_read_raw (OS_Drive* drive) {
file.is_directory = fileRecord->isDirectory; file.is_directory = fileRecord->isDirectory;
// We need to get size from the data attribute // We need to get size from the data attribute
// #TODO: continue from here! // #TODO: #continue from here!
mft->file_count += 1; mft->file_count += 1;
} }
} }
@ -292,7 +292,7 @@ Error* NTFS_MFT_read_raw (OS_Drive* drive) {
CloseHandle(file_handle); CloseHandle(file_handle);
log("Found %lld files (bytes_accessed: %s)", mft->file_count, format_bytes(mft->bytes_accessed).data); log_none("Found %lld files (bytes_accessed: %s)", mft->file_count, format_bytes(mft->bytes_accessed).data);
drive->file_count = mft->file_count; drive->file_count = mft->file_count;
drive->bytes_accessed = mft->bytes_accessed; drive->bytes_accessed = mft->bytes_accessed;

View File

@ -44,7 +44,7 @@ internal void Main_Entry_Point (int argc, WCHAR **argv) { // #entry_point
graphics_set_render_target(get_main_window()); graphics_set_render_target(get_main_window());
// 3. Init Graphics (DX11 or OpenGL3) // 3. Init Graphics (DX11 or OpenGL3)
// #TODO: #Main - `Main_Entry_Point` // #TODO: #Graphics#Main - `Main_Entry_Point`
// 4. [ ] Setup Mouse and Keyboard Inputs // 4. [ ] Setup Mouse and Keyboard Inputs
// 5. [ ] Launch second thread; thread groups // 5. [ ] Launch second thread; thread groups

View File

@ -12,7 +12,9 @@ struct Explorer {
}; };
struct Ex1_NTFS_Enumeration { struct Ex1_NTFS_Enumeration {
bool initialized; b32 initialized;
b32 threads_started;
ArrayView<Thread> threads; ArrayView<Thread> threads;
Array<Thread*> threads_in_flight; Array<Thread*> threads_in_flight;
}; };
@ -22,6 +24,19 @@ global ExplorerUI explorer_ui;
global Explorer explorer; global Explorer explorer;
global Ex1_NTFS_Enumeration ex1_ntfs; global Ex1_NTFS_Enumeration ex1_ntfs;
void ntfs_create_enumeration_threads (s32 thread_count) {
if (!ex1_ntfs.initialized) { Timed_Block_Print("Thread initialization (ntfs)");
ex1_ntfs.initialized = true;
ex1_ntfs.threads = ArrayView<Thread>(thread_count);
ex1_ntfs.threads_in_flight.allocator = GPAllocator();
for_each(t, ex1_ntfs.threads) {
string thread_name = format_string("ntfs_enumeration_thread#%d", t);
bool success = thread_init(&ex1_ntfs.threads[t], ntfs_enumeration_thread_proc, thread_name);
Assert(success);
}
}
}
#define HOTKEY_ID_BRING_TO_FOREGROUND 1 #define HOTKEY_ID_BRING_TO_FOREGROUND 1
#define VK_SPACE_KEY_CODE 0x20 #define VK_SPACE_KEY_CODE 0x20
// #define HOTKEY_ID_HIDE_TITLEBAR // #define HOTKEY_ID_HIDE_TITLEBAR
@ -67,13 +82,14 @@ void Ex1_Control_Panel () { using namespace ImGui;
Table<string, OS_Drive*>* drive_table = get_drive_table(); Table<string, OS_Drive*>* drive_table = get_drive_table();
Begin("Control Panel"); Begin("Control Panel");
if (Button("Debug break")) { debug_break(); }
if (Button("Discover drives") || !table_is_valid(drive_table)) { if (Button("Discover drives") || !table_is_valid(drive_table)) {
Win32_Discover_Drives(); Win32_Discover_Drives();
} }
Text("drive_table is valid: %d", table_is_valid(drive_table)); Text("drive_table is valid: %d", table_is_valid(drive_table));
push_allocator(temp()); push_allocator(temp());
ArrayView<OS_Drive*> drives = os_get_available_drives(); ArrayView<OS_Drive*> drives = os_get_available_drives(); // only includes drives that are ready.
for_each(i, drives) { for_each(i, drives) {
OS_Drive* drive = drives[i]; OS_Drive* drive = drives[i];
@ -90,36 +106,24 @@ void Ex1_Control_Panel () { using namespace ImGui;
// } // }
} }
if (drives.count > 0) { if (drives.count > 0 && Button("Enumerate all NTFS drives")) { // && ex1_ntfs.initialized
if (!ex1_ntfs.initialized) { Timed_Block_Print("Thread initialization (ntfs)");
push_allocator(GPAllocator());
ex1_ntfs.initialized = true;
s32 cpu_core_count = os_cpu_physical_core_count();
ex1_ntfs.threads = ArrayView<Thread>(cpu_core_count);
ex1_ntfs.threads_in_flight.allocator = GPAllocator();
for_each(t, ex1_ntfs.threads) {
string thread_name = format_string("ntfs_enumeration_thread#%d", t);
bool success = thread_init(&ex1_ntfs.threads[t], ntfs_enumeration_thread_proc, thread_name);
Assert(success);
}
}
}
if (drives.count > 0 && ex1_ntfs.initialized && Button("Enumerate all NTFS drives")) {
// if drive count exceeds the number of threads, we need to group them so each thread // if drive count exceeds the number of threads, we need to group them so each thread
// can enumerate multiple drives. // can enumerate multiple drives.
// We need to distribute the drives across our available threads: // We need to distribute the drives across our available threads:
// #TODO #is_present - drive_count should only include if `drive->is_present`. push_allocator(GPAllocator());
Array<ArrayView<OS_Drive*>> drive_split; Array<ArrayView<OS_Drive*>> drive_split;
s32 thread_count = (s32)ex1_ntfs.threads.count; drive_split.allocator = temp(); // this is only needed for this frame
drive_split.allocator = GPAllocator();
if (drives.count > thread_count) { if (drives.count > os_cpu_physical_core_count()) {
s32 thread_count = os_cpu_physical_core_count();
array_resize(drive_split, thread_count);
ntfs_create_enumeration_threads(thread_count);
s32 threads_to_create = thread_count;
s64 drives_per_thread = (drives.count / thread_count); s64 drives_per_thread = (drives.count / thread_count);
s64 remainder = drives.count % thread_count; s64 remainder = drives.count % thread_count;
s64 current_drive = 0; s64 current_drive = 0;
push_allocator(GPAllocator());
array_resize(drive_split, thread_count);
for_each(d, drive_split) { for_each(d, drive_split) {
if (d == drive_split.count) { if (d == drive_split.count) {
drive_split[d] = ArrayView<OS_Drive*>(remainder); drive_split[d] = ArrayView<OS_Drive*>(remainder);
@ -135,7 +139,9 @@ void Ex1_Control_Panel () { using namespace ImGui;
debug_break(); // #TODO: Check that the work has been distributed correctly. debug_break(); // #TODO: Check that the work has been distributed correctly.
} else { // more threads than drives, or same amount } else { // more threads than drives, or same amount
s32 thread_count = (s32)drives.count;
array_resize(drive_split, drives.count); array_resize(drive_split, drives.count);
ntfs_create_enumeration_threads(thread_count);
for_each(d, drives) { for_each(d, drives) {
auto drive = drives[d]; auto drive = drives[d];
@ -147,6 +153,7 @@ void Ex1_Control_Panel () { using namespace ImGui;
s64 active_thread_count = drive_split.count; s64 active_thread_count = drive_split.count;
ex1_ntfs.threads_started = true;
for (s64 t = 0; t < active_thread_count; t += 1) { for (s64 t = 0; t < active_thread_count; t += 1) {
Thread* thread = &ex1_ntfs.threads[t]; Thread* thread = &ex1_ntfs.threads[t];
Arena* thread_arena = next_arena(Arena_Reserve::Size_64K); Arena* thread_arena = next_arena(Arena_Reserve::Size_64K);
@ -159,19 +166,30 @@ void Ex1_Control_Panel () { using namespace ImGui;
} }
} }
if (ex1_ntfs.threads_started && !ex1_ntfs.threads_in_flight.count) {
// All threads are complete, we're free to clean up remaining memory
push_allocator(GPAllocator());
array_free(ex1_ntfs.threads);
array_free(ex1_ntfs.threads_in_flight);
// Instead maybe we should just memset this to zero.
reset_struct(&ex1_ntfs);
}
if (ex1_ntfs.threads_in_flight.count) { if (ex1_ntfs.threads_in_flight.count) {
Text("Threads in flight: %d", ex1_ntfs.threads_in_flight.count); Text("Threads in flight: %d", ex1_ntfs.threads_in_flight.count);
// keep track of which indices to remove!
for_each(t, ex1_ntfs.threads_in_flight) { for_each(t, ex1_ntfs.threads_in_flight) {
if (thread_is_done(ex1_ntfs.threads_in_flight[t])) { if (thread_is_done(ex1_ntfs.threads_in_flight[t])) {
push_allocator(GPAllocator());
Thread* thread = ex1_ntfs.threads_in_flight[t]; Thread* thread = ex1_ntfs.threads_in_flight[t];
auto task = thread_task(NTFS_Enumeration_Task); auto task = thread_task(NTFS_Enumeration_Task);
array_free(task->drives);
// internal_free(
// make sure to retreive any data you need to from here!
release_arena(task->pool); release_arena(task->pool);
// #TODO Before #deiniting thread we SHOULD copy the errors from the thread_deinit(ex1_ntfs.threads_in_flight[t], false);
// thread before it's deleted forever.
thread_deinit(ex1_ntfs.threads_in_flight[t], true);
array_unordered_remove_by_index(ex1_ntfs.threads_in_flight, t); array_unordered_remove_by_index(ex1_ntfs.threads_in_flight, t);
t -= 1; // check this element index again! t -= 1; // check this element index again!
} }

View File

@ -298,6 +298,7 @@ global f32 imgui_font_sizes[6] = {18, 24, 30, 36, 42, 48};
global ImGui_Font_Info imgui_default_font; global ImGui_Font_Info imgui_default_font;
bool ImGui_Default_Font_Loaded () { bool ImGui_Default_Font_Loaded () {
// #TODO #font_crash Should only check once at the top of the frame!
return imgui_default_font.sizes.count > 0; return imgui_default_font.sizes.count > 0;
} }
@ -306,10 +307,6 @@ f32 ImGui_Default_Font_Current_Size () {
} }
void ImGui_Push_Default_Font () { void ImGui_Push_Default_Font () {
// #TODO: Replace these asserts so it just returns if not present!
// And just warn in the UI that a font is missing.
Assert(imgui_default_font.sizes.count > 0);
Assert(imgui_default_font.current_size >= 0 && imgui_default_font.current_size < imgui_default_font.sizes.count);
if (ImGui_Default_Font_Loaded()) { if (ImGui_Default_Font_Loaded()) {
ImFont* font_data = imgui_default_font.sizes[imgui_default_font.current_size].data; ImFont* font_data = imgui_default_font.sizes[imgui_default_font.current_size].data;
f32 unscaled_font_size = imgui_default_font.sizes[imgui_default_font.current_size].font_size; f32 unscaled_font_size = imgui_default_font.sizes[imgui_default_font.current_size].font_size;
@ -318,7 +315,8 @@ void ImGui_Push_Default_Font () {
} }
void ImGui_Pop_Default_Font () { void ImGui_Pop_Default_Font () {
// This will break if we load font mid-frame! don't do this. // #font_crash: This will break if we load font mid-frame! don't do this.
// We should only check this once at the top of the frame!
if (ImGui_Default_Font_Loaded()) { if (ImGui_Default_Font_Loaded()) {
ImGui::PopFont(); ImGui::PopFont();
} }