W32_SharedEntryCtx W32_shared_entry_ctx = ZI; //////////////////////////////// //~ Startup / shutdown jobs JobDef(W32_AppStartupJob, UNUSED sig, UNUSED id) { W32_SharedEntryCtx *g = &W32_shared_entry_ctx; TempArena scratch = BeginScratchNoConflict(); { Startup(); SetEvent(g->startup_end_event); } EndScratch(scratch); } JobDef(W32_AppShutdownJob, UNUSED sig, UNUSED id) { __prof; W32_SharedEntryCtx *g = &W32_shared_entry_ctx; i32 num_funcs = Atomic32Fetch(&g->num_exit_funcs); for (i32 i = num_funcs - 1; i >= 0; --i) { ExitFunc *func = g->exit_funcs[i]; func(); } SetEvent(g->exit_end_event); } //////////////////////////////// //~ @hookdef Exit hooks void OnExit(ExitFunc *func) { W32_SharedEntryCtx *g = &W32_shared_entry_ctx; i32 index = Atomic32FetchAdd(&g->num_exit_funcs, 1); if (index >= W32_MaxOnExitFuncs) { Panic(Lit("Maximum on exit functions registered")); } g->exit_funcs[index] = func; } void Exit(void) { W32_SharedEntryCtx *g = &W32_shared_entry_ctx; SetEvent(g->exit_begin_event); } void Panic(String msg) { W32_SharedEntryCtx *g = &W32_shared_entry_ctx; if (Atomic32FetchTestSet(&g->panicking, 0, 1) == 0) { //LogPanic(msg); wchar_t *wstr = g->panic_wstr; u64 WstrLen = 0; wchar_t prefix[] = L"A fatal error has occured and the application needs to exit:\n\n"; CopyBytes(wstr, prefix, MinU64(countof(g->panic_wstr), (countof(prefix) << 1))); WstrLen += countof(prefix) - 1; /* Perform manual string encode to avoid any implicit memory * allocation (in case allocation is unreliable) */ String str8 = msg; u64 pos8 = 0; while (pos8 < str8.len) { String str8_remaining = { .len = (str8.len - pos8), .text = str8.text + pos8 }; Utf8DecodeResult decoded = DecodeUtf8(str8_remaining); Utf16EncodeResult encoded = EncodeUtf16(decoded.codepoint); u64 wstr_new_len = WstrLen + encoded.count16; if (wstr_new_len < (countof(g->panic_wstr) - 1)) { u16 *dest = wstr + WstrLen; CopyBytes(dest, encoded.chars16, (encoded.count16 << 1)); WstrLen = wstr_new_len; pos8 += decoded.advance8; } else { break; } } wstr[WstrLen] = 0; #if RtcIsEnabled MessageBoxExW(0, wstr, L"Fatal error", MB_ICONSTOP | MB_SETFOREGROUND | MB_TOPMOST, 0); Assert(0); #endif SetEvent(g->panic_event); /* Wait for process termination */ if (GetCurrentThreadId() != g->main_thread_id) { Sleep(INFINITE); } } } //////////////////////////////// //~ Winmain int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, _In_ LPWSTR cmdline_wstr, _In_ int show_code) { LAX instance; LAX prev_instance; LAX cmdline_wstr; LAX show_code; __profthread("Main thread", PROF_THREAD_GROUP_MAIN); W32_SharedEntryCtx *g = &W32_shared_entry_ctx; #if ProfilingIsEnabled /* Start profiler */ { __profn("Launch profiler"); STARTUPINFO si = ZI; si.cb = sizeof(si); PROCESS_INFORMATION pi = ZI; wchar_t cmd[sizeof(ProfilingCmdWstr)] = ZI; CopyBytes(cmd, ProfilingCmdWstr, sizeof(ProfilingCmdWstr)); DeleteFileW(ProfilingOutFileWstr); b32 success = CreateProcessW(0, cmd, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi); if (!success) { MessageBoxExW(0, L"Failed to launch profiler using command '" ProfilingCmdWstr L"'.", L"Error", MB_ICONSTOP | MB_SETFOREGROUND | MB_TOPMOST, 0); } } /* Set internal profiler thread affinities */ { __profn("Set profiler thread affinities"); wchar_t *prefix_name_wstr = ProfilerThreadPrefixWstr; u64 prefix_name_wstr_len = ((i32)sizeof(ProfilerThreadPrefixWstr) >> 1) - 1; if (prefix_name_wstr_len > 0 && ProfilerThreadAffinityMask != 0) { DWORD proc_id = GetCurrentProcessId(); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (snapshot != INVALID_HANDLE_VALUE) { THREADENTRY32 te = ZI; te.dwSize = sizeof(THREADENTRY32); if (Thread32First(snapshot, &te)) { do { if (te.th32OwnerProcessID == proc_id) { i32 thread_id = te.th32ThreadID; HANDLE thread = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); if (thread) { wchar_t *thread_name_wstr = 0; HRESULT hr = GetThreadDescription(thread, &thread_name_wstr); if (SUCCEEDED(hr)) { u64 thread_name_len = WstrLenNoLimit(thread_name_wstr); if (thread_name_len >= prefix_name_wstr_len && EqBytes(thread_name_wstr, prefix_name_wstr, prefix_name_wstr_len)) { __profn("Set profiler thread affinity"); b32 success = SetThreadAffinityMask(thread, ProfilerThreadAffinityMask) != 0; { /* Retry until external tools can set correct process affinity */ i32 delay_ms = 16; while (!success && delay_ms <= 1024) { __profn("Profiler thread affinity retry"); Sleep(delay_ms); success = SetThreadAffinityMask(thread, ProfilerThreadAffinityMask) != 0; delay_ms *= 2; } } Assert(success); LAX success; } } CloseHandle(thread); } } } while (Thread32Next(snapshot, &te)); } } CloseHandle(snapshot); } } #endif /* Set up exit events */ 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); u64 cmdline_len = WstrLen(cmdline_wstr, countof(g->cmdline_args_wstr) - 1); CopyBytes(g->cmdline_args_wstr, cmdline_wstr, cmdline_len * sizeof(*cmdline_wstr)); g->cmdline_args_wstr[cmdline_len] = 0; g->main_thread_id = GetCurrentThreadId(); SetThreadDescription(GetCurrentThread(), L"Main thread"); /* Query system info */ GetSystemInfo(&g->info); /* Initialize base layer */ BaseMain(); //- App startup /* Run app start job */ if (!Atomic32Fetch(&g->panicking)) { RunJob(1, W32_AppStartupJob, 0, JobPool_Floating, JobPriority_High, 0); } /* Wait for startup end or panic */ if (!Atomic32Fetch(&g->panicking)) { HANDLE handles[] = { g->startup_end_event, g->panic_event }; WaitForMultipleObjects(countof(handles), handles, 0, INFINITE); } /* Wait for exit start or panic */ if (!Atomic32Fetch(&g->panicking)) { HANDLE handles[] = { g->exit_begin_event, g->panic_event }; WaitForMultipleObjects(countof(handles), handles, 0, INFINITE); } //- App shutdown /* Run exit callbacks job */ if (!Atomic32Fetch(&g->panicking)) { RunJob(1, W32_AppShutdownJob, 0, JobPool_Floating, JobPriority_High, 0); } /* Wait for exit end or panic */ if (!Atomic32Fetch(&g->panicking)) { HANDLE handles[] = { g->exit_end_event, g->panic_event }; WaitForMultipleObjects(countof(handles), handles, 0, INFINITE); } /* Exit */ i32 exit_code = 0; if (Atomic32Fetch(&g->panicking)) { WaitForSingleObject(g->panic_event, INFINITE); MessageBoxExW(0, g->panic_wstr, L"Fatal error", MB_ICONSTOP | MB_SETFOREGROUND | MB_TOPMOST, 0); exit_code = 1; } return exit_code; } //////////////////////////////// //~ Crt stub #if !CrtlibIsEnabled #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-variable-declarations" #pragma clang diagnostic ignored "-Wmissing-prototypes" /* Enable floating point */ __attribute((used)) int _fltused; __attribute((used)) void __stdcall wWinMainCRTStartup(void) { int result = wWinMain(GetModuleHandle(0), 0, GetCommandLineW(), 0); ExitProcess(result); } #pragma clang diagnostic pop #endif /* !CrtlibIsEnabled */