#ifdef INTELLISENSE_DIRECTIVES # pragma once # include "base/debug.h" # include "base/strings.h" # include "base/thread_context.h" # include "base/file.h" #endif // Copyright (c) 2024 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// //~ rjf: System Info typedef struct OS_SystemInfo OS_SystemInfo; struct OS_SystemInfo { U32 logical_processor_count; U64 page_size; U64 large_page_size; U64 allocation_granularity; String8 machine_name; }; //////////////////////////////// //~ rjf: Process Info typedef struct OS_ProcessInfo OS_ProcessInfo; struct OS_ProcessInfo { U32 pid; String8 binary_path; String8 initial_path; String8 user_program_data_path; String8List module_load_paths; String8List environment; }; //////////////////////////////// //~ rjf: Access Flags typedef U32 OS_AccessFlags; enum { OS_AccessFlag_Read = (1 << 0), OS_AccessFlag_Write = (1 << 1), OS_AccessFlag_Execute = (1 << 2), OS_AccessFlag_Append = (1 << 3), OS_AccessFlag_ShareRead = (1 << 4), OS_AccessFlag_ShareWrite = (1 << 5), }; //////////////////////////////// //~ rjf: Files typedef U32 OS_FileIterFlags; enum { OS_FileIterFlag_SkipFolders = (1 << 0), OS_FileIterFlag_SkipFiles = (1 << 1), OS_FileIterFlag_SkipHiddenFiles = (1 << 2), OS_FileIterFlag_Done = (1 << 31), }; typedef struct OS_FileIter OS_FileIter; struct OS_FileIter { OS_FileIterFlags flags; U8 memory[800]; }; typedef struct OS_FileInfo OS_FileInfo; struct OS_FileInfo { String8 name; FileProperties props; }; // nick: on-disk file identifier typedef struct OS_FileID OS_FileID; struct OS_FileID { U64 v[3]; }; //////////////////////////////// //~ rjf: Process Launch Parameters typedef struct OS_ProcessLaunchParams OS_ProcessLaunchParams; struct OS_ProcessLaunchParams { String8List cmd_line; String8 path; String8List env; B32 inherit_env; B32 consoleless; }; //////////////////////////////// //~ rjf: Handle Type typedef struct OS_Handle OS_Handle; struct OS_Handle { U64 u64[1]; }; typedef struct OS_HandleNode OS_HandleNode; struct OS_HandleNode { OS_HandleNode* next; OS_Handle v; }; typedef struct OS_HandleList OS_HandleList; struct OS_HandleList { OS_HandleNode* first; OS_HandleNode* last; U64 count; }; typedef struct OS_HandleArray OS_HandleArray; struct OS_HandleArray { OS_Handle* v; U64 count; }; //////////////////////////////// //~ rjf: Globally Unique IDs typedef struct OS_Guid OS_Guid; struct OS_Guid { U32 data1; U16 data2; U16 data3; U8 data4[8]; }; md_static_assert(size_of(OS_Guid) == 16, os_guid_check); //////////////////////////////// //~ rjf: Thread Types typedef void OS_ThreadFunctionType(void *ptr); //////////////////////////////// //~ rjf: Handle Type Functions (Helpers, Implemented Once) force_inline OS_Handle os_handle_zero (void) { OS_Handle handle = {0}; return handle; } force_inline B32 os_handle_match(OS_Handle a, OS_Handle b) { return a.u64[0] == b.u64[0]; } void os_handle_list_push__arena (Arena* arena, OS_HandleList* handles, OS_Handle handle); void os_handle_list_push__ainfo (AllocatorInfo ainfo, OS_HandleList* handles, OS_Handle handle); OS_HandleArray os_handle_array_from_list__arena(Arena* arena, OS_HandleList* list); OS_HandleArray os_handle_array_from_list__ainfo(AllocatorInfo ainfo, OS_HandleList* list); #define os_handle_ist_push(arena, handles, handle) _Generic(allocator, Arena*: os_handle_list_push__arena, AllocatorInfo: os_handle_list_push__ainfo, default: assert_generic_selection_fail) resolved_function_call(allocator, handles, handle) #define os_handle_array_from_list(allocator, list) _Generic(allocator, Arena*: os_handle_array_from_list__arena, AllocatorInfo: os_handle_list_push__ainfo, default: assert_generic_selection_fail) resolved_function_call(allocator, list) force_inline void os_handle_list_push__arena (Arena* arena, OS_HandleList* handles, OS_Handle handle) { os_handle_list__push_ainfo (arena_allocator(arena), handles, handle); } force_inline OS_HandleArray os_handle_array_from_list__arena(Arena* arena, OS_HandleList* list) { return os_handle_array_from_list__ainfo(arena_allocator(arena), list); } inline void os_handle_list_alloc(AllocatorInfo ainfo, OS_HandleList* handles, OS_Handle handle) { OS_HandleNode* n = alloc_array(ainfo, OS_HandleNode, 1); n->v = handle; sll_queue_push(handles->first, handles->last, n); handles->count += 1; } inline OS_HandleArray os_handle_array_from_list_alloc(AllocatorInfo ainfo, OS_HandleList* list) { OS_HandleArray result = {0}; result.count = list->count; result.v = alloc_array_no_zero(ainfo, OS_Handle, result.count); U64 idx = 0; for(OS_HandleNode* n = list->first; n != 0; n = n->next, idx += 1) { result.v[idx] = n->v; } return result; } //////////////////////////////// //~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once) String8List os_string_list_from_argcv__arena(Arena* arena, int argc, char** argv); String8List os_string_list_from_argcv__ainfo(AllocatorInfo ainfo, int argc, char** argv); inline String8List os_string_list_from_argcv__ainfo(AllocatorInfo ainfo, int argc, char** argv) { String8List result = {0}; for(int i = 0; i < argc; i += 1) { String8 str = str8_cstring(argv[i]); str8_list_push(ainfo, &result, str); } return result; } #define os_string_list_from_argcv(allocator, argc, argv) _Generic(allocator, Arena*: os_string_list_from_argcv__arena, AllocatorInfo: os_string_list_from_argcv__ainfo, default: assert_generic_selection_fail) resolved_function_call(allocator, argc, argv) force_inline String8List os_string_list_from_argcv__arena(Arena* arena, int argc, char** argv) { return os_string_list_from_argcv__ainfo(arena_allocator(arena), argc, argv); } //////////////////////////////// //~ rjf: @os_hooks File System (Implemented Per-OS) //- rjf: files MD_API OS_Handle os_file_open (OS_AccessFlags flags, String8 path); MD_API void os_file_close (OS_Handle file); MD_API U64 os_file_read (OS_Handle file, Rng1U64 rng, void *out_data); MD_API U64 os_file_write (OS_Handle file, Rng1U64 rng, void *data); MD_API B32 os_file_set_times (OS_Handle file, DateTime time); MD_API FileProperties os_properties_from_file (OS_Handle file); MD_API OS_FileID os_id_from_file (OS_Handle file); MD_API B32 os_delete_file_at_path (String8 path); MD_API B32 os_copy_file_path (String8 dst, String8 src); MD_API B32 os_file_path_exists (String8 path); MD_API FileProperties os_properties_from_file_path (String8 path); MD_API String8 os_full_path_from_path__arena(Arena* arena, String8 path); MD_API String8 os_full_path_from_path__ainfo(AllocatorInfo arena, String8 path); #define os_full_path_from_path(allocator, path) _Generic(allocator, Arena*: os_full_path_from_path__arena, AllocatorInfo: os_full_path_from_path__ainfo, default: assert_generic_selection_fail) resolved_function_call(allocator, path) force_inline String8 os_full_path_from_path__arena(Arena* arena, String8 path) { return os_full_path_from_path__ainfo(arena_allocator(arena), path); } //- rjf: file maps MD_API OS_Handle os_file_map_open (OS_AccessFlags flags, OS_Handle file); MD_API void os_file_map_close (OS_Handle map); MD_API void* os_file_map_view_open (OS_Handle map, OS_AccessFlags flags, Rng1U64 range); MD_API void os_file_map_view_close(OS_Handle map, void* ptr, Rng1U64 range); //- rjf: directory iteration OS_FileIter* os_file_iter_begin__arena(Arena* arena, String8 path, OS_FileIterFlags flags); MD_API OS_FileIter* os_file_iter_begin__ainfo(AllocatorInfo ainfo, String8 path, OS_FileIterFlags flags); B32 os_file_iter_next__arena (Arena* arena, OS_FileIter* iter, OS_FileInfo* info_out); MD_API B32 os_file_iter_next__ainfo (AllocatorInfo arena, OS_FileIter* iter, OS_FileInfo* info_out); MD_API void os_file_iter_end ( OS_FileIter* iter); #define os_file_iter_begin(allocator, path, flags) _Generic(allocator, Arena*: os_file_iter_begin__arena, AllocatorInfo: os_file_iter_begin__ainfo, default: assert_generic_selection_fail) resolved_function_call(allocator, path) #define os_file_iter_next(allocator, iter, info_out) _Generic(allocator, Arena*: os_file_iter_next__arena, AllocatorInfo: os_file_iter_next__ainfo, default: assert_generic_selection_fail) resolved_function_call(allocator, path) force_inline OS_FileIter* os_file_iter_begin__arena(Arena* arena, String8 path, OS_FileIterFlags flags) { reutrn (); } force_inline B32 os_file_iter_next__arena (Arena* arena, OS_FileIter* iter, OS_FileInfo* info_out) { reutrn (); } //- rjf: directory creation MD_API B32 os_make_directory(String8 path); //////////////////////////////// //~ rjf: Filesystem Helpers (Helpers, Implemented Once) MD_API B32 os_write_data_to_file_path (String8 path, String8 data); MD_API B32 os_write_data_list_to_file_path(String8 path, String8List list); MD_API B32 os_append_data_to_file_path (String8 path, String8 data); OS_FileID os_id_from_file_path (String8 path); S64 os_file_id_compare (OS_FileID a, OS_FileID b); String8 os_data_from_file_path__arena (Arena* arena, String8 path); String8 os_data_from_file_path__ainfo (AllocatorInfo ainfo, String8 path); MD_API String8 os_string_from_file_range__arena(Arena* arena, OS_Handle file, Rng1U64 range); MD_API String8 os_string_from_file_range__ainfo(AllocatorInfo ainfo, OS_Handle file, Rng1U64 range); #define os_data_from_file_path(allocator, path) _Generic(allocator, Arena*: os_data_from_file_path__arena, AllocatorInfo: os_data_from_file_path__ainfo, default: assert_generic_selection_fail) resolved_function_call(allocator, path) #define os_string_from_file_range(allocator, file, range) _Generic(allocator, Arena*: os_string_from_file_range__arena, AllocatorInfo: os_string_from_file_range__ainfo, default: assert_generic_selection_fail) resolved_function_call(allocator, file, range) force_inline String8 os_data_from_file_path__arena(Arena* arena, String8 path) { return os_data_from_file_path__ainfo(arena_allocator(arena), path); } inline String8 os_data_from_file_path__ainfo(AllocatorInfo ainfo, String8 path) { OS_Handle file = os_file_open(OS_AccessFlag_Read | OS_AccessFlag_ShareRead, path); FileProperties props = os_properties_from_file(file); String8 data = os_string_from_file_range(ainfo, file, r1u64(0, props.size)); os_file_close(file); return data; } inline OS_FileID os_id_from_file_path(String8 path) { OS_Handle file = os_file_open(OS_AccessFlag_Read | OS_AccessFlag_ShareRead, path); OS_FileID id = os_id_from_file(file); os_file_close(file); return id; } inline S64 os_file_id_compare(OS_FileID a, OS_FileID b) { S64 cmp = memory_compare((void*)&a.v[0], (void*)&b.v[0], sizeof(a.v)); return cmp; } //////////////////////////////// //~ rjf: GUID Helpers (Helpers, Implemented Once) String8 os_string_from_guid__arena(Arena* arena, OS_Guid guid); String8 os_string_from_guid__ainfo(AllocatorInfo ainfo, OS_Guid guid); #define os_string_from_guid(allocator, guid) _Generic(allocator, Arena*: os_string_from_guid__arena, AllocatorInfo: os_string_from_guid__ainfo, default: assert_generic_selection_fail) resolved_function_call(allocator, guid) force_inline String8 os_string_from_guid__arena(Arena* arena, OS_Guid guid) { os_string_from_guid__ainfo(arena_allocator(arena), guid); } inline String8 os_string_from_guid_alloc(AllocatorInfo ainfo, OS_Guid guid) { String8 result = str8f(ainfo, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", guid.data1, guid.data2, guid.data3, guid.data4[0], guid.data4[1], guid.data4[2], guid.data4[3], guid.data4[4], guid.data4[5], guid.data4[6], guid.data4[7] ); return result; } //////////////////////////////// //~ rjf: @os_hooks System/Process Info (Implemented Per-OS) MD_API OS_SystemInfo* os_get_system_info (void); MD_API OS_ProcessInfo* os_get_process_info(void); String8 os_get_current_path__arena(Arena* arena); String8 os_get_current_path__ainfo(AllocatorInfo arena); #define os_get_current_path(allocator) _Generic(allocator, Arena*: os_get_current_path__arena, AllocatorInfo: os_get_current_path__ainfo) resolved_function_call(allocator) force_inline String8 os_get_current_path__arena(Arena* arena) { return os_get_current_path__ainfo(arena_allocator(arena)); } //////////////////////////////// //~ rjf: @os_hooks Memory Allocation (Implemented Per-OS) //- rjf: basic void* os_reserve ( U64 size); B32 os_commit (void* ptr, U64 size); void os_decommit(void* ptr, U64 size); void os_release (void* ptr, U64 size); //- rjf: large pages void* os_reserve_large( U64 size); B32 os_commit_large (void* ptr, U64 size); //////////////////////////////// //~ rjf: @os_hooks Thread Info (Implemented Per-OS) U32 os_tid(void); MD_API void os_set_thread_name(String8 string); //////////////////////////////// //~ rjf: @os_hooks Aborting (Implemented Per-OS) MD_API void os_abort(S32 exit_code); //////////////////////////////// //~ rjf: @os_hooks Shared Memory (Implemented Per-OS) MD_API OS_Handle os_shared_memory_alloc (U64 size, String8 name); MD_API OS_Handle os_shared_memory_open (String8 name); MD_API void os_shared_memory_close (OS_Handle handle); MD_API void* os_shared_memory_view_open (OS_Handle handle, Rng1U64 range); MD_API void os_shared_memory_view_close(OS_Handle handle, void* ptr, Rng1U64 range); //////////////////////////////// //~ rjf: @os_hooks Time (Implemented Per-OS) MD_API U64 os_now_microseconds (void); MD_API U32 os_now_unix (void); MD_API DateTime os_now_universal_time (void); MD_API DateTime os_universal_time_from_local(DateTime* local_time); MD_API DateTime os_local_time_from_universal(DateTime* universal_time); MD_API void os_sleep_milliseconds (U32 msec); //////////////////////////////// //~ rjf: @os_hooks Child Processes (Implemented Per-OS) MD_API OS_Handle os_process_launch(OS_ProcessLaunchParams* params); MD_API B32 os_process_join (OS_Handle handle, U64 endt_us); MD_API void os_process_detach(OS_Handle handle); //////////////////////////////// //~ rjf: @os_hooks Threads (Implemented Per-OS) MD_API OS_Handle os_thread_launch(OS_ThreadFunctionType* func, void* ptr, void* params); MD_API B32 os_thread_join (OS_Handle handle, U64 endt_us); MD_API void os_thread_detach(OS_Handle handle); //////////////////////////////// //~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS) //- rjf: recursive mutexes MD_API OS_Handle os_mutex_alloc (void); MD_API void os_mutex_release(OS_Handle mutex); MD_API void os_mutex_take (OS_Handle mutex); MD_API void os_mutex_drop (OS_Handle mutex); //- rjf: reader/writer mutexes MD_API OS_Handle os_rw_mutex_alloc (void); MD_API void os_rw_mutex_release(OS_Handle rw_mutex); MD_API void os_rw_mutex_take_r (OS_Handle mutex); MD_API void os_rw_mutex_drop_r (OS_Handle mutex); MD_API void os_rw_mutex_take_w (OS_Handle mutex); MD_API void os_rw_mutex_drop_w (OS_Handle mutex); //- rjf: condition variables MD_API OS_Handle os_condition_variable_alloc (void); MD_API void os_condition_variable_release(OS_Handle cv); // returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout MD_API B32 os_condition_variable_wait (OS_Handle cv, OS_Handle mutex, U64 endt_us); MD_API B32 os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us); MD_API B32 os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us); MD_API void os_condition_variable_signal (OS_Handle cv); MD_API void os_condition_variable_broadcast(OS_Handle cv); //- rjf: cross-process semaphores MD_API OS_Handle os_semaphore_alloc (U32 initial_count, U32 max_count, String8 name); MD_API void os_semaphore_release(OS_Handle semaphore); MD_API OS_Handle os_semaphore_open (String8 name); MD_API void os_semaphore_close (OS_Handle semaphore); MD_API B32 os_semaphore_take (OS_Handle semaphore, U64 endt_us); MD_API void os_semaphore_drop (OS_Handle semaphore); //- rjf: scope macros #define os_mutex_scope(mutex) defer_loop( os_mutex_take (mutex), os_mutex_drop (mutex)) #define os_mutex_scope_r(mutex) defer_loop( os_rw_mutex_take_r(mutex), os_rw_mutex_drop_r(mutex)) #define os_mutex_scope_W(mutex) defer_loop( os_rw_mutex_take_w(mutex), os_rw_mutex_drop_w(mutex)) #define os_mutex_scope_rw_promote(mutex) defer_loop((os_rw_mutex_drop_r(mutex), os_rw_mutex_take_w(mutex)), (os_rw_mutex_drop_w(mutex), os_rw_mutex_take_r(mutex))) //////////////////////////////// //~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) MD_API OS_Handle os_library_open (String8 path); MD_API void os_library_close (OS_Handle lib); MD_API VoidProc* os_library_load_proc(OS_Handle lib, String8 name); //////////////////////////////// //~ rjf: @os_hooks Safe Calls (Implemented Per-OS) MD_API void os_safe_call(OS_ThreadFunctionType* func, OS_ThreadFunctionType* fail_handler, void* ptr); //////////////////////////////// //~ rjf: @os_hooks GUIDs (Implemented Per-OS) MD_API OS_Guid os_make_guid(void); //////////////////////////////// //~ rjf: @os_hooks Entry Points (Implemented Per-OS) // NOTE(rjf): The implementation of `os_core` will define low-level entry // points if BUILD_ENTRY_DEFINING_UNIT is defined to 1. These will call // into the standard codebase program entry points, named "entry_point". #if BUILD_ENTRY_DEFINING_UNIT void entry_point(CmdLine* cmdline); #endif //////////////////////////////// //~ Ed: Manual OS Bootstrap (Implemented Per-OS) typedef struct OS_Context OS_Context; struct OS_Context { Arena* state_arena; Arena* entity_arena; B32 enable_large_pages; }; // OS layer initialization MD_API void os_init(OS_Context* ctx, TCTX* thread_ctx);