293 lines
9.2 KiB
C
293 lines
9.2 KiB
C
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 */
|