mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
On MinGW, thread_local storage is emutls: libgcc frees a thread's blocks from a winpthread key destructor when the thread function returns, and the CRT runs the thread_local destructors later, from the module's TLS callback at thread detach. The destructors of the driver's non-trivial thread_locals (the deferred label queue, packet profile, capture and revalidation scratch vectors, build profile, memory counters, disk cache buffers) therefore ran on freed storage. Under the debug heap the flip test failed on every run at the queue worker's exit after the injected flip failure; without it the use-after-free showed up as intermittent heap corruption or a stack overflow on the Windows CI job. ShaderRecompiler::ThreadOwned keeps such state behind a trivially destructible thread_local pointer and deletes it from its own pthread key destructor, which receives the objects directly instead of reading emutls. The driver no longer registers any thread_local destructor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
1.5 KiB
C++
60 lines
1.5 KiB
C++
#ifndef CORE_SHADER_RECOMPILER_THREADOWNED_HPP
|
|
#define CORE_SHADER_RECOMPILER_THREADOWNED_HPP
|
|
|
|
#include <memory>
|
|
#include <pthread.h>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
namespace ShaderRecompiler {
|
|
|
|
namespace ThreadOwnedDetail {
|
|
|
|
struct Owned {
|
|
void* object;
|
|
void (*destroy)(void*);
|
|
};
|
|
|
|
inline void DestroyAll(void* value) {
|
|
auto* owned = static_cast<std::vector<Owned>*>(value);
|
|
for (auto it = owned->rbegin(); it != owned->rend(); ++it) it->destroy(it->object);
|
|
delete owned;
|
|
}
|
|
|
|
inline pthread_key_t Key() {
|
|
static const pthread_key_t key = [] {
|
|
pthread_key_t created{};
|
|
if (pthread_key_create(&created, DestroyAll) != 0) throw std::runtime_error("ThreadOwned: pthread_key_create failed");
|
|
return created;
|
|
}();
|
|
return key;
|
|
}
|
|
|
|
inline void DestroyAtThreadExit(void* object, void (*destroy)(void*)) {
|
|
auto* owned = static_cast<std::vector<Owned>*>(pthread_getspecific(Key()));
|
|
if (owned == nullptr) {
|
|
owned = new std::vector<Owned>();
|
|
if (pthread_setspecific(Key(), owned) != 0) {
|
|
delete owned;
|
|
throw std::runtime_error("ThreadOwned: pthread_setspecific failed");
|
|
}
|
|
}
|
|
owned->push_back({object, destroy});
|
|
}
|
|
|
|
}
|
|
|
|
template<typename T>
|
|
T& ThreadOwned(T*& slot) {
|
|
if (slot == nullptr) {
|
|
auto object = std::make_unique<T>();
|
|
ThreadOwnedDetail::DestroyAtThreadExit(object.get(), [](void* value) { delete static_cast<T*>(value); });
|
|
slot = object.release();
|
|
}
|
|
return *slot;
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|