hle: kernel: Fix service_threads access to be thread safe V2.

- PR #7699 attempted to fix CreateServiceThread and ReleaseServiceThread to be thread safe, but inadvertently introduced a possible dead-lock.
- With this PR, we use a worker thread to manage the service thread list, allowing it only to be accessed by a single thread, and guaranteeing threads will not destroy themselves.
- Fixes a rare crash in Pokemon Sword/Shield, I've now run this game for ~12 hours non-stop and am quite confident this is a good solution for this issue.
This commit is contained in:
bunnei 2022-01-14 16:02:57 -08:00
parent b2d45a4072
commit cc112f971e

View file

@ -51,7 +51,8 @@ namespace Kernel {
struct KernelCore::Impl { struct KernelCore::Impl {
explicit Impl(Core::System& system_, KernelCore& kernel_) explicit Impl(Core::System& system_, KernelCore& kernel_)
: time_manager{system_}, object_list_container{kernel_}, system{system_} {} : time_manager{system_}, object_list_container{kernel_},
service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {}
void SetMulticore(bool is_multi) { void SetMulticore(bool is_multi) {
is_multicore = is_multi; is_multicore = is_multi;
@ -707,24 +708,22 @@ struct KernelCore::Impl {
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
const std::string& name) { const std::string& name) {
auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
{
std::lock_guard lk(service_threads_lock); service_threads_manager.QueueWork(
service_threads.emplace(service_thread); [this, service_thread]() { service_threads.emplace(service_thread); });
}
return service_thread; return service_thread;
} }
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
auto strong_ptr = service_thread.lock(); if (auto strong_ptr = service_thread.lock()) {
{ service_threads_manager.QueueWork(
std::lock_guard lk(service_threads_lock); [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
service_threads.erase(strong_ptr);
} }
} }
void ClearServiceThreads() { void ClearServiceThreads() {
std::lock_guard lk(service_threads_lock); service_threads_manager.QueueWork([this]() { service_threads.clear(); });
service_threads.clear();
} }
std::mutex server_ports_lock; std::mutex server_ports_lock;
@ -732,7 +731,6 @@ struct KernelCore::Impl {
std::mutex registered_objects_lock; std::mutex registered_objects_lock;
std::mutex registered_in_use_objects_lock; std::mutex registered_in_use_objects_lock;
std::mutex dummy_thread_lock; std::mutex dummy_thread_lock;
std::mutex service_threads_lock;
std::atomic<u32> next_object_id{0}; std::atomic<u32> next_object_id{0};
std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin}; std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
@ -783,6 +781,7 @@ struct KernelCore::Impl {
// Threads used for services // Threads used for services
std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
Common::ThreadWorker service_threads_manager;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};