From 05f6c937cb63175b89a56bad2b19b14a085fab72 Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 24 Oct 2025 05:40:13 -0500 Subject: [PATCH] swap testing --- src/base/base.h | 4 ++ src/base/base_entry.h | 11 +++- src/base/base_win32/base_win32.c | 90 +++++++++++++++++++++++++++++--- src/base/base_win32/base_win32.h | 1 + src/meta/meta.c | 40 +++++++++++++- src/pp/pp.c | 43 +++++++++++++++ src/pp/pp.h | 8 +++ 7 files changed, 189 insertions(+), 8 deletions(-) diff --git a/src/base/base.h b/src/base/base.h index dd93e873..b53f551e 100644 --- a/src/base/base.h +++ b/src/base/base.h @@ -37,6 +37,10 @@ # error Missing compile time definition for 'TestsAreEnabled' #endif +#ifndef HotSwappingIsEnabled +# error Missing compile time definition for 'HotSwappingIsEnabled' +#endif + //////////////////////////////////////////////////////////// //~ Machine context diff --git a/src/base/base_entry.h b/src/base/base_entry.h index d25118c2..f6403877 100644 --- a/src/base/base_entry.h +++ b/src/base/base_entry.h @@ -5,7 +5,16 @@ typedef ExitFuncDef(ExitFunc); //////////////////////////////////////////////////////////// -//~ @hookdecl Exit operations +//~ @hookdecl Swap hooks + +b32 IsSwappingIn(void); +b32 IsSwappingOut(void); + +String SwappedStateFromName(Arena *arena, String name); +void WriteSwappedState(String name, String data); + +//////////////////////////////////////////////////////////// +//~ @hookdecl Exit hooks void OnExit(ExitFunc *func); void SignalExit(i32 code); diff --git a/src/base/base_win32/base_win32.c b/src/base/base_win32/base_win32.c index b43cfd90..106cb796 100644 --- a/src/base/base_win32/base_win32.c +++ b/src/base/base_win32/base_win32.c @@ -109,6 +109,68 @@ void TrueRand(String buffer) BCryptGenRandom(BCRYPT_RNG_ALG_HANDLE, (u8 *)buffer.text, buffer.len, 0); } +//////////////////////////////////////////////////////////// +//~ Swap hooks + +b32 IsSwappingIn(void) +{ + return HotSwappingIsEnabled; +} + +b32 IsSwappingOut(void) +{ + return HotSwappingIsEnabled; +} + +String SwappedStateFromName(Arena *arena, String name) +{ + TempArena scratch = BeginScratch(arena); + String result = ZI; + String path = StringF(scratch.arena, "swap/%F.swp", FmtString(name)); + wchar_t *path_wstr = WstrFromString(scratch.arena, path); + HANDLE handle = CreateFileW(path_wstr, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (handle != INVALID_HANDLE_VALUE) + { + u32 chunk_size = Kibi(64); + result.text = PushDry(arena, u8); + for (;;) + { + u8 *chunk = PushStructsNoZero(arena, u8, chunk_size); + u32 chunk_bytes_read = 0; + ReadFile(handle, chunk, chunk_size, (LPDWORD)&chunk_bytes_read, 0); + result.len += chunk_bytes_read; + if (chunk_bytes_read < chunk_size) + { + PopStructsNoCopy(arena, u8, chunk_size - chunk_bytes_read); + break; + } + } + } + CloseHandle(handle); + EndScratch(scratch); + return result; +} + +void WriteSwappedState(String name, String data) +{ + TempArena scratch = BeginScratchNoConflict(); + String result = ZI; + String path = StringF(scratch.arena, "swap/%F.swp", FmtString(name)); + wchar_t *path_wstr = WstrFromString(scratch.arena, path); + HANDLE handle = CreateFileW(path_wstr, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (handle != INVALID_HANDLE_VALUE) + { + SetFilePointer(handle, 0, 0, FILE_BEGIN); + SetEndOfFile(handle); + WriteFile(handle, data.text, data.len, 0, 0); + } + CloseHandle(handle); + EndScratch(scratch); +} + +//////////////////////////////////////////////////////////// +//~ @hookdef Exit hooks + void OnExit(ExitFunc *func) { W32_SharedState *g = &W32_shared_state; @@ -254,10 +316,15 @@ i32 W32_Main(void) } /* Set up exit events */ - g->panic_event = CreateEventW(0, 1, 0, 0); +#if HotSwappingIsEnabled + g->swap_event = CreateEventW(0, 1, 0, L"Local\\pp_swap_start"); +#else + g->swap_event = CreateEventW(0, 1, 0, 0); +#endif + g->panic_event = CreateEventW(0, 1, 0, 0); g->startup_end_event = CreateEventW(0, 1, 0, 0); - g->exit_begin_event = CreateEventW(0, 1, 0, 0); - g->exit_end_event = CreateEventW(0, 1, 0, 0); + g->exit_begin_event = CreateEventW(0, 1, 0, 0); + g->exit_end_event = CreateEventW(0, 1, 0, 0); g->main_thread_id = GetCurrentThreadId(); SetThreadDescription(GetCurrentThread(), L"Main thread"); @@ -291,7 +358,7 @@ i32 W32_Main(void) { HANDLE handles[] = { g->startup_end_event, - g->panic_event + g->panic_event, }; WaitForMultipleObjects(countof(handles), handles, 0, INFINITE); } @@ -301,9 +368,10 @@ i32 W32_Main(void) { HANDLE handles[] = { g->exit_begin_event, - g->panic_event + g->panic_event, + g->swap_event, }; - WaitForMultipleObjects(countof(handles), handles, 0, INFINITE); + DWORD wake = WaitForMultipleObjects(countof(handles), handles, 0, INFINITE); } //- App shutdown @@ -324,6 +392,16 @@ i32 W32_Main(void) WaitForMultipleObjects(countof(handles), handles, 0, INFINITE); } + /* Signal swap finish */ + if (!Atomic32Fetch(&g->panicking) && IsSwappingOut()) + { + HANDLE swap_end_event = OpenEventW(EVENT_MODIFY_STATE, 0, L"Local\\pp_swap_end"); + if (swap_end_event != 0) + { + SetEvent(swap_end_event); + } + } + /* Exit */ if (Atomic32Fetch(&g->panicking)) { diff --git a/src/base/base_win32/base_win32.h b/src/base/base_win32/base_win32.h index 0a613856..a6cee7f4 100644 --- a/src/base/base_win32/base_win32.h +++ b/src/base/base_win32/base_win32.h @@ -38,6 +38,7 @@ Struct(W32_SharedState) Atomic32 panicking; wchar_t panic_wstr[4096]; HANDLE panic_event; + HANDLE swap_event; HANDLE startup_end_event; HANDLE exit_begin_event; HANDLE exit_end_event; diff --git a/src/meta/meta.c b/src/meta/meta.c index 9e7ef1b5..3cc99948 100644 --- a/src/meta/meta.c +++ b/src/meta/meta.c @@ -45,6 +45,10 @@ # define TestsAreEnabled 0 #endif +#ifndef HotSwappingIsEnabled +# define HotSwappingIsEnabled 0 +#endif + //////////////////////////////////////////////////////////// //~ Includes @@ -96,6 +100,36 @@ LineCol LineColFromPos(String data, i64 pos) return result; } +//////////////////////////////////////////////////////////// +//~ Swap + +#if PlatformIsWindows +u64 SignalSwapStart(void) +{ + OS_Mkdir(Lit("swap")); + HANDLE swap_end_event = CreateEventW(0, 1, 0, L"Local\\pp_swap_end"); + HANDLE swap_start_event = OpenEventW(EVENT_MODIFY_STATE, 0, L"Local\\pp_swap_start"); + if (swap_start_event != 0) + { + SetEvent(swap_start_event); + CloseHandle(swap_start_event); + } + return (u64)swap_end_event; +} + +void WaitForSwapEnd(u64 swap_start, i64 timeout_ns) +{ + HANDLE swap_start_event = (HANDLE)swap_start; + if (swap_start_event != 0) + { + DWORD timeout_ms = timeout_ns / 1000000; + WaitForSingleObject(swap_start_event, timeout_ms); + } +} +#else +# error Swap signaling not implemented +#endif + //////////////////////////////////////////////////////////// //~ OS command job @@ -138,7 +172,6 @@ Struct(CompilerParams) StringList linker_only_flags_clang; }; - //////////////////////////////////////////////////////////// //~ Build step job @@ -872,6 +905,8 @@ JobDef(Build, _, __) } } + u64 swap_start = SignalSwapStart(); + ////////////////////////////// //- Args @@ -895,6 +930,7 @@ JobDef(Build, _, __) PushStringToList(arena, &cp.defs, Lit("-DProfilingIsEnabled=0")); PushStringToList(arena, &cp.defs, Lit("-DUnoptimizedIsEnabled=1")); PushStringToList(arena, &cp.defs, Lit("-DTestsAreEnabled=0")); + PushStringToList(arena, &cp.defs, Lit("-DHotSwappingIsEnabled=1")); //- Msvc { @@ -1049,6 +1085,8 @@ JobDef(Build, _, __) //- Link + WaitForSwapEnd(swap_start, NsFromSeconds(0.5f / 1000.f)); + String exe_file = Lit("pp.exe"); i64 link_elapsed_ns = 0; if (ret == 0) diff --git a/src/pp/pp.c b/src/pp/pp.c index 8931b414..4afb966d 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -30,8 +30,36 @@ void StartupUser(void) g->local_to_user_client_store = AcquireClientStore(); g->local_to_user_client = AcquireClient(g->local_to_user_client_store); + /* Init from swap */ g->world_to_ui_xf = XformIdentity; g->world_to_render_xf = XformIdentity; + if (IsSwappingIn()); + { + TempArena scratch = BeginScratchNoConflict(); + String swap_str = SwappedStateFromName(scratch.arena, Lit("pp_user")); + if (swap_str.len == sizeof(SwappedUserState)) + { + SwappedUserState *swap = (SwappedUserState *)swap_str.text; + SharedUserState *old = &swap->s; + g->debug_following = old->debug_following; + g->debug_camera = old->debug_camera; + g->debug_camera_panning = old->debug_camera_panning; + g->debug_camera_pan_start = old->debug_camera_pan_start; + g->debug_draw = old->debug_draw; + g->screen_size = old->screen_size; + g->screen_cursor = old->screen_cursor; + g->ui_to_screen_xf = old->ui_to_screen_xf; + g->ui_size = old->ui_size; + g->ui_cursor = old->ui_cursor; + g->render_to_ui_xf = old->render_to_ui_xf; + g->render_size = old->render_size; + g->world_to_render_xf = old->world_to_render_xf; + g->world_to_ui_xf = old->world_to_ui_xf; + g->world_cursor = old->world_cursor; + g->focus_send = old->focus_send; + } + EndScratch(scratch); + } g->console_logs_arena = AcquireArena(Gibi(64)); //P_RegisterLogCallback(ConsoleLogCallback, P_LogLevel_Success); @@ -55,6 +83,19 @@ ExitFuncDef(ShutdownUser) SharedUserState *g = &shared_user_state; Atomic32Set(&g->shutdown, 1); YieldOnFence(&g->shutdown_jobs_fence, g->shutdown_jobs_count); + + /* Swap out */ + if (IsSwappingOut()) + { + TempArena scratch = BeginScratchNoConflict(); + + SwappedUserState *swap = PushStruct(scratch.arena, SwappedUserState); + swap->s = *g; + WriteSwappedState(Lit("pp_user"), StringFromStruct(swap)); + + EndScratch(scratch); + } + P_ReleaseWindow(g->window); } @@ -3215,9 +3256,11 @@ JobDef(UpdateSim, UNUSED sig, UNUSED id) } } +#if 0 ReleaseClientStore(store); ReleaseSimAccel(&accel); BB_ReleaseBuff(&snapshot_writer_bb); BB_ReleaseBuff(&msg_writer_bb); N_ReleaseHost(host); +#endif } diff --git a/src/pp/pp.h b/src/pp/pp.h index 3e7a295f..153c6465 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -264,6 +264,14 @@ Struct(SharedUserState) Vec2 focus_send; } extern shared_user_state; +//////////////////////////////////////////////////////////// +//~ Swap types + +Struct(SwappedUserState) +{ + SharedUserState s; +}; + //////////////////////////////////////////////////////////// //~ Startup