From ccdd84a778244eb1ebbf7ea6e39d5fca79f16e2a Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 26 Aug 2020 01:55:44 +0000
Subject: [PATCH] hle/scheduler: Fix data race in is_context_switch_pending

As reported by tsan, SelectThreads could write to
is_context_switch_pending holding a mutex while SwitchToCurrent reads it
without holding any.

It is assumed that the author didn't want an atomic here, so the code is
reordered so that whenever is_context_switch_pending is read inside
SwitchToContext, the mutex is locked.
---
 src/core/hle/kernel/scheduler.cpp | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index a4b2344243..5cbd3b912c 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -756,7 +756,11 @@ void Scheduler::SwitchToCurrent() {
             current_thread = selected_thread;
             is_context_switch_pending = false;
         }
-        while (!is_context_switch_pending) {
+        const auto is_switch_pending = [this] {
+            std::scoped_lock lock{guard};
+            return is_context_switch_pending;
+        };
+        do {
             if (current_thread != nullptr && !current_thread->IsHLEThread()) {
                 current_thread->context_guard.lock();
                 if (!current_thread->IsRunnable()) {
@@ -775,7 +779,7 @@ void Scheduler::SwitchToCurrent() {
                 next_context = &idle_thread->GetHostContext();
             }
             Common::Fiber::YieldTo(switch_fiber, *next_context);
-        }
+        } while (!is_switch_pending());
     }
 }