Files
AnyPS5/cmake/WindowsTestExit.cpp
T
Dean Galvin ea33174d7d fix(tests): end Windows test processes through libc before DLL detach
On Windows a test that returns from main ends through exit and ExitProcess,
which kills every other thread before the prx static destructors run at
DLL_PROCESS_DETACH. Detached host threads (the driver's async log thread,
libkernel's file watch thread, ...) die holding winpthreads and queue locks,
and a destructor that then locks a mutex, waits for the dead logger or
destroys a condition variable never returns: the test prints its last line
and hangs until the CTest timeout (#6791).

Every test executable now wraps main on MinGW. After main returns the
wrapper runs the executable's own exit handlers and static destructors
(_cexit), as exit does today, flushes the streams and ends the process with
LibcTerminate_nid_no_patch, the path the guest exit already takes. That
flushes the driver log while its thread is alive and calls TerminateProcess,
so no DLL_PROCESS_DETACH work runs after the threads are gone. The libc
shutdown callbacks are not run, as on a plain return from main on Linux. A
test that does not load libc.prx returns as before.
2026-10-17 14:45:48 +13:00

32 lines
809 B
C++

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <process.h>
#include <windows.h>
extern "C" int __real_main(int argc, char** argv);
namespace {
template<typename TFunction>
TFunction LibcFunction(const char* name) {
const HMODULE libc = GetModuleHandleW(L"libc.prx");
if (libc == nullptr) return nullptr;
return reinterpret_cast<TFunction>(reinterpret_cast<void*>(GetProcAddress(libc, name)));
}
}
extern "C" int __wrap_main(int argc, char** argv) {
const int code = __real_main(argc, argv);
const auto terminate = LibcFunction<void (*)(int)>("LibcTerminate_nid_no_patch");
if (terminate == nullptr) return code;
_cexit();
std::cout.flush();
std::clog.flush();
std::cerr.flush();
std::fflush(nullptr);
terminate(code);
return code;
}