mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
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.
This commit is contained in:
1 parent
fd9e577d98
commit
ea33174d7d
2 files changed
+36
No files matched your search
@@ -6,10 +6,15 @@ endif()
|
||||
project(Relinker CXX)
|
||||
|
||||
set(ANYPS5_TEST_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
|
||||
set(ANYPS5_WINDOWS_TEST_EXIT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/WindowsTestExit.cpp")
|
||||
|
||||
function(add_test_executable target)
|
||||
add_executable(${target} ${ARGN})
|
||||
set_target_properties(${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$<1:${ANYPS5_TEST_OUTPUT_DIRECTORY}>")
|
||||
if(MINGW)
|
||||
target_sources(${target} PRIVATE "${ANYPS5_WINDOWS_TEST_EXIT}")
|
||||
target_link_options(${target} PRIVATE LINKER:--wrap=main)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
option(BUILD_TESTING "Build and register relinker tests" OFF)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#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;
|
||||
}
|
||||
Reference in new issue
Block a user