From aa79ca7a7a0099cd208a5afe75d657bb02f97fb3 Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Thu, 10 Jun 2021 13:56:35 -0400
Subject: [PATCH] kernel: KLightConditionVariable: Update implementation to
 12.x

Updates the implementation of KLightConditionVariable to FW 12.x
---
 .../hle/kernel/k_light_condition_variable.h   | 43 +++++++++++++------
 src/core/hle/kernel/k_resource_limit.cpp      |  2 +-
 2 files changed, 31 insertions(+), 14 deletions(-)

diff --git a/src/core/hle/kernel/k_light_condition_variable.h b/src/core/hle/kernel/k_light_condition_variable.h
index ca2e539a79..a95fa41f37 100644
--- a/src/core/hle/kernel/k_light_condition_variable.h
+++ b/src/core/hle/kernel/k_light_condition_variable.h
@@ -18,41 +18,58 @@ class KernelCore;
 
 class KLightConditionVariable {
 public:
-    explicit KLightConditionVariable(KernelCore& kernel_)
-        : thread_queue(kernel_), kernel(kernel_) {}
+    explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
 
-    void Wait(KLightLock* lock, s64 timeout = -1) {
-        WaitImpl(lock, timeout);
-        lock->Lock();
+    void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) {
+        WaitImpl(lock, timeout, allow_terminating_thread);
     }
 
     void Broadcast() {
         KScopedSchedulerLock lk{kernel};
-        while (thread_queue.WakeupFrontThread() != nullptr) {
-            // We want to signal all threads, and so should continue waking up until there's nothing
-            // to wake.
+
+        // Signal all threads.
+        for (auto& thread : wait_list) {
+            thread.SetState(ThreadState::Runnable);
         }
     }
 
 private:
-    void WaitImpl(KLightLock* lock, s64 timeout) {
+    void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
         KThread* owner = GetCurrentThreadPointer(kernel);
 
         // Sleep the thread.
         {
-            KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
-            lock->Unlock();
+            KScopedSchedulerLockAndSleep lk{kernel, owner, timeout};
 
-            if (!thread_queue.SleepThread(owner)) {
+            if (!allow_terminating_thread && owner->IsTerminationRequested()) {
                 lk.CancelSleep();
                 return;
             }
+
+            lock->Unlock();
+
+            // Set the thread as waiting.
+            GetCurrentThread(kernel).SetState(ThreadState::Waiting);
+
+            // Add the thread to the queue.
+            wait_list.push_back(GetCurrentThread(kernel));
+        }
+
+        // Remove the thread from the wait list.
+        {
+            KScopedSchedulerLock sl{kernel};
+
+            wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel)));
         }
 
         // Cancel the task that the sleep setup.
         kernel.TimeManager().UnscheduleTimeEvent(owner);
+
+        // Re-acquire the lock.
+        lock->Lock();
     }
-    KThreadQueue thread_queue;
+
     KernelCore& kernel;
+    KThread::WaiterList wait_list{};
 };
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp
index f91cb65dca..da88f35bc6 100644
--- a/src/core/hle/kernel/k_resource_limit.cpp
+++ b/src/core/hle/kernel/k_resource_limit.cpp
@@ -117,7 +117,7 @@ bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
         if (current_hints[index] + value <= limit_values[index] &&
             (timeout < 0 || core_timing->GetGlobalTimeNs().count() < timeout)) {
             waiter_count++;
-            cond_var.Wait(&lock, timeout);
+            cond_var.Wait(&lock, timeout, false);
             waiter_count--;
         } else {
             break;