mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 16:43:05 -05:00
* fix(agc): keep the driver's per-thread scratch out of thread_local teardown
With MinGW's emulated TLS, a thread_local with a destructor in a DLL is
destroyed after its storage is gone: when a std::thread ends, winpthread
runs libgcc's emutls key destructor, which frees the thread's thread_local
storage, and only then does the loader's DLL_THREAD_DETACH run the DLL's
tls_atexit list, so the destructors run on freed memory. The queue worker
touches four of them in libSceAgcDriver (the dispatch-ahead map, the
pending waits, the draw-snapshot compare vectors and the image decode
memo), and Driver::stop's join of the worker then corrupts the heap.
Under cdb's checking heap agc_driver_draw_cache_reuse stopped at the
worker's exit with heap corruption in all 127 runs, with graphics
pipeline libraries, with APS5_NO_PIPELINE_LTO=1 and with APS5_NO_GPL=1.
Without it the damage shows intermittently, as exit 0xc0000374 (2 of 143
runs with a cold NVIDIA shader cache) or as a fault in whatever allocates
next, such as NVIDIA's compiler on the optimized-link thread (#2980).
These objects now live in HostThreadLocal, fiber-local storage that owns
them, as the recorder's buffers do since e424b6b7b.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(libs): keep thread_local objects with destructors out of module teardown
The teardown order behind the driver's heap corruption holds in every
module: with MinGW's emulated TLS a thread's thread_local storage is freed
before the module's DLL_THREAD_DETACH runs its thread_local destructors,
so they run on freed objects. PreciseSleepUs's timer (used, among others,
by the video-out vblank thread) then closes a stale handle; libkernel's
directory path, socket waker and UUID random device, libSceRandom's random
device and libScePosixForWebKit's random device and host lookup free or
close stale objects. They move to HostThreadLocal. The other thread_locals
in the modules are trivially destructible or only built for Linux.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(libs): fail when a module registers a thread_local destructor
prx_thread_local_destructors lists the symbols of every built module and,
where MinGW's thread_local destructor support is linked in, disassembles
the module for calls to __cxa_thread_atexit outside libc's registration of
guest destructors. A module that keeps a thread_local with a destructor
fails it with the function that registers one: with main's libSceRandom
it reports sceRandomGetRandomNumber, and with this branch all 122 modules
pass, libc being the only one that links the support.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(agc): keep the image memo behind its pointer
The image decode memo went into HostThreadLocal as the whole array. On
Linux, where HostThreadLocal is a plain thread_local, that put 1024 memo
entries into every thread's TLS block instead of one pointer. It keeps
its unique_ptr as before, now owned by HostThreadLocal, so Linux builds
the same thread_local as main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Dean Galvin <dean.galvin@live.com>
39 lines
1.7 KiB
CMake
39 lines
1.7 KiB
CMake
file(GLOB modules "${MODULES}/*.prx")
|
|
if(NOT modules)
|
|
message(FATAL_ERROR "no modules in ${MODULES}")
|
|
endif()
|
|
set(explicit "__cxa_thread_atexit_impl_nid_postfix|LibcInternalExtCxaThreadAtexit_nid_postfix")
|
|
set(failures "")
|
|
foreach(module IN LISTS modules)
|
|
get_filename_component(name "${module}" NAME)
|
|
execute_process(COMMAND "${NM}" --defined-only "${module}" OUTPUT_VARIABLE symbols RESULT_VARIABLE result ERROR_QUIET)
|
|
if(NOT result EQUAL 0 OR symbols STREQUAL "")
|
|
list(APPEND failures "${name}: no symbol table to inspect")
|
|
continue()
|
|
endif()
|
|
if(NOT symbols MATCHES " T __mingw_cxa_thread_atexit\n")
|
|
continue()
|
|
endif()
|
|
execute_process(COMMAND "${OBJDUMP}" -d --no-show-raw-insn "${module}" OUTPUT_VARIABLE code RESULT_VARIABLE result ERROR_QUIET)
|
|
if(NOT result EQUAL 0)
|
|
list(APPEND failures "${name}: cannot disassemble")
|
|
continue()
|
|
endif()
|
|
string(REGEX MATCHALL "\n[0-9a-f]+ <[^>\n]+>:|(call|jmp)[^\n]*<__cxa_thread_atexit>" lines "${code}")
|
|
set(function "")
|
|
foreach(line IN LISTS lines)
|
|
if(line MATCHES "^\n[0-9a-f]+ <([^>\n]+)>:$")
|
|
set(function "${CMAKE_MATCH_1}")
|
|
elseif(NOT function MATCHES "^(${explicit})$")
|
|
list(APPEND failures "${name}: ${function} registers a thread_local destructor")
|
|
endif()
|
|
endforeach()
|
|
endforeach()
|
|
if(failures)
|
|
list(REMOVE_DUPLICATES failures)
|
|
list(JOIN failures "\n " text)
|
|
message(FATAL_ERROR "On Windows a thread_local destructor in a module runs after emulated TLS freed the object; keep such objects in HostThreadLocal:\n ${text}")
|
|
endif()
|
|
list(LENGTH modules count)
|
|
message(STATUS "${count} modules register no thread_local destructors")
|