From 3e75175d0242090902e4b383086e3f5ac6cf3f73 Mon Sep 17 00:00:00 2001
From: Luke Street <luke.street@encounterpc.com>
Date: Mon, 3 Dec 2018 12:25:27 -0500
Subject: [PATCH 1/2] svc: Implement SetThreadActivity (thread suspension)

---
 src/core/hle/kernel/errors.h    |  2 +-
 src/core/hle/kernel/svc.cpp     | 38 +++++++++++++++++++++++++++++----
 src/core/hle/kernel/thread.cpp  | 24 ++++++++++++++++++++-
 src/core/hle/kernel/thread.h    | 14 ++++++++++++
 src/yuzu/debugger/wait_tree.cpp |  4 ++++
 5 files changed, 76 insertions(+), 6 deletions(-)

diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 8b58d701d6..d8240ec6d0 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -27,7 +27,7 @@ constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
 constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
 constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
 constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
-constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
+constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122};
 constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
 constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
 constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 948989b318..72da3eb3dc 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -851,8 +851,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
 }
 
 /// Sets the thread activity
-static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
-    LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown);
+static ResultCode SetThreadActivity(Handle handle, u32 activity) {
+    LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
+    if (activity > static_cast<u32>(ThreadActivity::Paused)) {
+        return ERR_INVALID_ENUM_VALUE;
+    }
+
+    const auto* current_process = Core::CurrentProcess();
+    const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
+    if (!thread) {
+        LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
+        return ERR_INVALID_HANDLE;
+    }
+
+    if (thread->GetOwnerProcess() != current_process) {
+        LOG_ERROR(Kernel_SVC,
+                  "The current process does not own the current thread, thread_handle={:08X} "
+                  "thread_pid={}, "
+                  "current_process_pid={}",
+                  handle, thread->GetOwnerProcess()->GetProcessID(),
+                  current_process->GetProcessID());
+        return ERR_INVALID_HANDLE;
+    }
+
+    if (thread == GetCurrentThread()) {
+        LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
+        return ERR_BUSY;
+    }
+
+    thread->SetActivity(static_cast<ThreadActivity>(activity));
     return RESULT_SUCCESS;
 }
 
@@ -879,7 +906,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
 
     if (thread == GetCurrentThread()) {
         LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
-        return ERR_ALREADY_REGISTERED;
+        return ERR_BUSY;
     }
 
     Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
@@ -1157,7 +1184,10 @@ static ResultCode StartThread(Handle thread_handle) {
     ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
 
     thread->ResumeFromWait();
-    Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
+
+    if (thread->GetStatus() == ThreadStatus::Ready) {
+        Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
+    }
 
     return RESULT_SUCCESS;
 }
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 4ffb768183..748c45dd76 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -50,7 +50,7 @@ void Thread::Stop() {
 
     // Clean up thread from ready queue
     // This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
-    if (status == ThreadStatus::Ready) {
+    if (status == ThreadStatus::Ready || status == ThreadStatus::Paused) {
         scheduler->UnscheduleThread(this, current_priority);
     }
 
@@ -140,6 +140,11 @@ void Thread::ResumeFromWait() {
 
     wakeup_callback = nullptr;
 
+    if (activity == ThreadActivity::Paused) {
+        status = ThreadStatus::Paused;
+        return;
+    }
+
     status = ThreadStatus::Ready;
 
     ChangeScheduler();
@@ -388,6 +393,23 @@ bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> t
     return wakeup_callback(reason, std::move(thread), std::move(object), index);
 }
 
+void Thread::SetActivity(ThreadActivity value) {
+    activity = value;
+
+    if (value == ThreadActivity::Paused) {
+        // Set status if not waiting
+        if (status == ThreadStatus::Ready) {
+            status = ThreadStatus::Paused;
+        } else if (status == ThreadStatus::Running) {
+            status = ThreadStatus::Paused;
+            Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
+        }
+    } else if (status == ThreadStatus::Paused) {
+        // Ready to reschedule
+        ResumeFromWait();
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
 /**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index d384d50dbc..268bf845bb 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -44,6 +44,7 @@ enum ThreadProcessorId : s32 {
 enum class ThreadStatus {
     Running,      ///< Currently running
     Ready,        ///< Ready to run
+    Paused,       ///< Paused by SetThreadActivity or debug
     WaitHLEEvent, ///< Waiting for hle event to finish
     WaitSleep,    ///< Waiting due to a SleepThread SVC
     WaitIPC,      ///< Waiting for the reply from an IPC request
@@ -60,6 +61,11 @@ enum class ThreadWakeupReason {
     Timeout // The thread was woken up due to a wait timeout.
 };
 
+enum class ThreadActivity : u32 {
+    Normal = 0,
+    Paused = 1,
+};
+
 class Thread final : public WaitObject {
 public:
     using TLSMemory = std::vector<u8>;
@@ -370,6 +376,12 @@ public:
         return affinity_mask;
     }
 
+    ThreadActivity GetActivity() const {
+        return activity;
+    }
+
+    void SetActivity(ThreadActivity value);
+
 private:
     explicit Thread(KernelCore& kernel);
     ~Thread() override;
@@ -438,6 +450,8 @@ private:
     TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
 
     std::string name;
+
+    ThreadActivity activity = ThreadActivity::Normal;
 };
 
 /**
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 96c57fe976..293351c759 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -221,6 +221,9 @@ QString WaitTreeThread::GetText() const {
     case Kernel::ThreadStatus::Ready:
         status = tr("ready");
         break;
+    case Kernel::ThreadStatus::Paused:
+        status = tr("paused");
+        break;
     case Kernel::ThreadStatus::WaitHLEEvent:
         status = tr("waiting for HLE return");
         break;
@@ -261,6 +264,7 @@ QColor WaitTreeThread::GetColor() const {
     case Kernel::ThreadStatus::Running:
         return QColor(Qt::GlobalColor::darkGreen);
     case Kernel::ThreadStatus::Ready:
+    case Kernel::ThreadStatus::Paused:
         return QColor(Qt::GlobalColor::darkBlue);
     case Kernel::ThreadStatus::WaitHLEEvent:
     case Kernel::ThreadStatus::WaitIPC:

From a3d78b77f8734cba6e73d3b57b0d6af68e7ca110 Mon Sep 17 00:00:00 2001
From: Luke Street <luke.street@encounterpc.com>
Date: Tue, 4 Dec 2018 02:25:34 -0500
Subject: [PATCH 2/2] debugger: Set paused thread color

---
 src/yuzu/debugger/wait_tree.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 293351c759..fd4ddbd446 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -264,8 +264,9 @@ QColor WaitTreeThread::GetColor() const {
     case Kernel::ThreadStatus::Running:
         return QColor(Qt::GlobalColor::darkGreen);
     case Kernel::ThreadStatus::Ready:
-    case Kernel::ThreadStatus::Paused:
         return QColor(Qt::GlobalColor::darkBlue);
+    case Kernel::ThreadStatus::Paused:
+        return QColor(Qt::GlobalColor::lightGray);
     case Kernel::ThreadStatus::WaitHLEEvent:
     case Kernel::ThreadStatus::WaitIPC:
         return QColor(Qt::GlobalColor::darkRed);