W32_SharedState W32_shared_state = ZI; //////////////////////////////////////////////////////////// //~ Win32 embedded data BOOL W32_FindEmbeddedRcData(HMODULE module, LPCWSTR type, LPWSTR wstr_entry_name, LONG_PTR udata) { W32_FindEmbeddedDataCtx *ctx = (W32_FindEmbeddedDataCtx *)udata; TempArena scratch = BeginScratchNoConflict(); String entry_name = StringFromWstrNoLimit(scratch.arena, (LPWSTR)wstr_entry_name); String embedded_data_prefix = Lit(Stringize(W32_EmbeddedDataPrefix)); if (StringBeginsWith(entry_name, embedded_data_prefix)) { HRSRC hres = FindResourceW(module, wstr_entry_name, type); if (hres) { HGLOBAL hg = LoadResource(module, hres); if (hg) { if (ctx->embedded_strings_count < countof(ctx->embedded_strings)) { String embedded = ZI; embedded.len = SizeofResource(module, hres); embedded.text = LockResource(hg); ctx->embedded_strings[ctx->embedded_strings_count++] = embedded; } else { Panic(Lit("Maximum number of embedded resource entries exceeded")); } } } } EndScratch(scratch); return 1; } //////////////////////////////////////////////////////////// //~ @hookimpl Core hooks StringList GetRawCommandline(void) { return W32_shared_state.raw_command_line; } void Echo(String msg) { HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); if (console_handle && console_handle != INVALID_HANDLE_VALUE) { WriteFile(console_handle, msg.text, msg.len, 0, 0); } else if (IsRunningInDebugger()) { char msg_cstr[16384]; u64 len = MinU64(countof(msg_cstr) - 1, msg.len); CopyBytes(msg_cstr, msg.text, len); msg_cstr[len] = 0; OutputDebugStringA(msg_cstr); } } b32 Panic(String msg) { LogPanic(msg); char msg_cstr[4096]; CstrFromStringToBuff(StringFromArray(msg_cstr), msg); { u32 mb_flags = MB_SETFOREGROUND | MB_ICONERROR; MessageBoxExA(0, msg_cstr, "Fatal error", mb_flags, 0); } HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE); if (console_handle != INVALID_HANDLE_VALUE) { WriteFile(console_handle, msg.text, msg.len, 0, 0); } if (IsRunningInDebugger()) { Assert(0); } else { ExitProcess(1); } return 0; } b32 IsRunningInDebugger(void) { return IsDebuggerPresent(); } i64 TimeNs(void) { struct W32_SharedState *g = &W32_shared_state; LARGE_INTEGER qpc; QueryPerformanceCounter(&qpc); i64 result = (qpc.QuadPart - g->timer_start_qpc) * g->ns_per_qpc; return result; } u32 GetNumHardwareThreads(void) { return GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); } void TrueRand(String buffer) { BCryptGenRandom(BCRYPT_RNG_ALG_HANDLE, (u8 *)buffer.text, buffer.len, 0); } //////////////////////////////////////////////////////////// //~ @hookimpl Swap hooks b32 IsSwappedIn(void) { return IsHotSwappingEnabled; } b32 IsSwappingOut(void) { return IsHotSwappingEnabled; } String SwappedStateFromName(Arena *arena, String name) { TempArena scratch = BeginScratch(arena); String result = ZI; String path = StringF(scratch.arena, "ppswap/%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 = ArenaNext(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(); /* TODO: Use directory non-relative to executable */ CreateDirectoryW(L"ppswap", 0); String result = ZI; String path = StringF(scratch.arena, "ppswap/%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); } //////////////////////////////////////////////////////////// //~ @hookimpl Exit hooks void OnExit(ExitFunc *func) { W32_SharedState *g = &W32_shared_state; 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 SignalExit(i32 code) { W32_SharedState *g = &W32_shared_state; Atomic32Set(&g->exit_code, code); SetEvent(g->exit_begin_event); } void ExitNow(i32 code) { ExitProcess(code); } //////////////////////////////////////////////////////////// //~ Startup / shutdown jobs JobImpl(W32_StartupLayers, UNUSED sig, UNUSED id) { W32_SharedState *g = &W32_shared_state; TempArena scratch = BeginScratchNoConflict(); { StartupLayers(); SetEvent(g->startup_end_event); } EndScratch(scratch); } JobImpl(W32_ShutdownLayers, UNUSED sig, UNUSED id) { __prof; W32_SharedState *g = &W32_shared_state; 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); } //////////////////////////////////////////////////////////// //~ Main i32 W32_Main(void) { __profthread("Main thread", PROF_THREAD_GROUP_MAIN); W32_SharedState *g = &W32_shared_state; #if IsProfilingEnabled /* 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 ok = CreateProcessW(0, cmd, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi); if (!ok) { 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 && MatchBytes(thread_name_wstr, prefix_name_wstr, prefix_name_wstr_len)) { __profn("Set profiler thread affinity"); b32 ok = SetThreadAffinityMask(thread, ProfilerThreadAffinityMask) != 0; { /* Retry until external tools can set correct process affinity */ i32 delay_ms = 16; while (!ok && delay_ms <= 1024) { __profn("Profiler thread affinity retry"); Sleep(delay_ms); ok = SetThreadAffinityMask(thread, ProfilerThreadAffinityMask) != 0; delay_ms *= 2; } } Assert(ok); LAX ok; } } CloseHandle(thread); } } } while (Thread32Next(snapshot, &te)); } } CloseHandle(snapshot); } } #endif /* Init time */ { LARGE_INTEGER qpf; QueryPerformanceFrequency(&qpf); g->ns_per_qpc = 1000000000 / qpf.QuadPart; } { LARGE_INTEGER qpc; QueryPerformanceCounter(&qpc); g->timer_start_qpc = qpc.QuadPart; } /* 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); g->main_thread_id = GetCurrentThreadId(); SetThreadDescription(GetCurrentThread(), L"Main thread"); /* Query system info */ GetSystemInfo(&g->info); /* Init job system */ InitJobSystem(); /* Init futex system */ InitFutexSystem(); /* Get raw args from command line */ { Arena *perm = PermArena(); StringList args_list = ZI; { LPCWSTR cmdline_wstr = GetCommandLineW(); i32 argc = 0; LPWSTR *argv = CommandLineToArgvW(cmdline_wstr, &argc); for (i32 i = 0; i < argc; ++i) { wchar_t *arg_wstr = argv[i]; String arg = StringFromWstrNoLimit(perm, arg_wstr); PushStringToList(perm, &args_list, arg); } } g->raw_command_line = args_list; } /* Init command line */ InitCmdline(); /* Init log system */ /* FIXME: Remove hardcoded log path */ InitLogSystem(Lit("log.log")); /* Init resources */ { W32_FindEmbeddedDataCtx ctx = ZI; EnumResourceNamesW(0, RT_RCDATA, &W32_FindEmbeddedRcData, (LONG_PTR)&ctx); InitResourceSystem(ctx.embedded_strings_count, ctx.embedded_strings); } //- App startup /* Startup layers */ if (!Atomic32Fetch(&g->panicking)) { RunJob(W32_StartupLayers); } /* 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, }; DWORD wake = WaitForMultipleObjects(countof(handles), handles, 0, INFINITE); } //- App shutdown /* Run exit callbacks job */ if (!Atomic32Fetch(&g->panicking)) { RunJob(W32_ShutdownLayers); } /* 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); } /* 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)) { WaitForSingleObject(g->panic_event, INFINITE); MessageBoxExW(0, g->panic_wstr, L"Fatal error", MB_ICONSTOP | MB_SETFOREGROUND | MB_TOPMOST, 0); Atomic32FetchTestSet(&g->exit_code, 0, 1); } return Atomic32Fetch(&g->exit_code); } //////////////////////////////////////////////////////////// //~ Crt main #if IsCrtlibEnabled # if IsConsoleApp int main(UNUSED char **argc, UNUSED int argv) { return W32_Main(); } # else int CALLBACK wWinMain(UNUSED _In_ HINSTANCE instance, UNUSED _In_opt_ HINSTANCE prev_instance, UNUSED _In_ LPWSTR cmdline_wstr, UNUSED _In_ int show_code) { return W32_Main(); } # endif /* IsConsoleApp */ #endif /* IsCrtlibEnabled */ //////////////////////////////////////////////////////////// //~ Crt stub #if !IsCrtlibEnabled #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) { i32 result = W32_Main(); ExitProcess(result); } #pragma clang diagnostic pop #endif /* !IsCrtlibEnabled */