476 lines
14 KiB
C
476 lines
14 KiB
C
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 */
|