From 83c7ba1ef700eff17f30b6c2782db77710dc322e Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Sat, 7 Mar 2020 12:44:35 -0400
Subject: [PATCH] SVC: Correct SetThreadActivity.

---
 src/core/hle/kernel/scheduler.cpp |  9 ++---
 src/core/hle/kernel/svc.cpp       |  5 +--
 src/core/hle/kernel/thread.cpp    | 61 ++++++++++++++++++++-----------
 src/core/hle/kernel/thread.h      | 22 +++++++----
 4 files changed, 59 insertions(+), 38 deletions(-)

diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index f020438fb5..a37b992ec1 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -417,8 +417,7 @@ void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) {
     }
     ASSERT(is_locked);
 
-    if (static_cast<ThreadSchedStatus>(old_flags & static_cast<u32>(ThreadSchedMasks::LowMask)) ==
-        ThreadSchedStatus::Runnable) {
+    if (old_flags == static_cast<u32>(ThreadSchedStatus::Runnable)) {
         // In this case the thread was running, now it's pausing/exitting
         if (thread->processor_id >= 0) {
             Unschedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
@@ -430,7 +429,7 @@ void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) {
                 Unsuggest(thread->current_priority, core, thread);
             }
         }
-    } else if (thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable) {
+    } else if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
         // The thread is now set to running from being stopped
         if (thread->processor_id >= 0) {
             Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
@@ -448,7 +447,7 @@ void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) {
 }
 
 void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) {
-    if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) {
+    if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable)) {
         return;
     }
     ASSERT(is_locked);
@@ -486,7 +485,7 @@ void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priorit
 
 void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask,
                                                  s32 old_core) {
-    if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable ||
+    if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable) ||
         thread->current_priority >= THREADPRIO_COUNT) {
         return;
     }
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 718462b2b1..da2f90a1d1 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1012,7 +1012,6 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
 /// Sets the thread activity
 static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
     LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
-    UNIMPLEMENTED();
     if (activity > static_cast<u32>(ThreadActivity::Paused)) {
         return ERR_INVALID_ENUM_VALUE;
     }
@@ -1039,9 +1038,7 @@ static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 act
         return ERR_BUSY;
     }
 
-    thread->SetActivity(static_cast<ThreadActivity>(activity));
-
-    return RESULT_SUCCESS;
+    return thread->SetActivity(static_cast<ThreadActivity>(activity));
 }
 
 /// Gets the thread context
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index e8962a0d8f..b99e3b7a55 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -113,20 +113,11 @@ void Thread::ResumeFromWait() {
         return;
     }
 
-    if (activity == ThreadActivity::Paused) {
-        SetStatus(ThreadStatus::Paused);
-        return;
-    }
-
     SetStatus(ThreadStatus::Ready);
 }
 
 void Thread::OnWakeUp() {
     SchedulerLock lock(kernel);
-    if (activity == ThreadActivity::Paused) {
-        SetStatus(ThreadStatus::Paused);
-        return;
-    }
 
     SetStatus(ThreadStatus::Ready);
 }
@@ -143,7 +134,7 @@ void Thread::CancelWait() {
         is_sync_cancelled = true;
         return;
     }
-    //TODO(Blinkhawk): Implement cancel of server session
+    // TODO(Blinkhawk): Implement cancel of server session
     is_sync_cancelled = false;
     SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED);
     SetStatus(ThreadStatus::Ready);
@@ -407,19 +398,31 @@ bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) {
     return hle_callback(std::move(thread));
 }
 
-void Thread::SetActivity(ThreadActivity value) {
-    activity = value;
+ResultCode Thread::SetActivity(ThreadActivity value) {
+    SchedulerLock lock(kernel);
+
+    auto sched_status = GetSchedulingStatus();
+
+    if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) {
+        return ERR_INVALID_STATE;
+    }
+
+    if (IsPendingTermination()) {
+        return RESULT_SUCCESS;
+    }
 
     if (value == ThreadActivity::Paused) {
-        // Set status if not waiting
-        if (status == ThreadStatus::Ready || status == ThreadStatus::Running) {
-            SetStatus(ThreadStatus::Paused);
-            kernel.PrepareReschedule(processor_id);
+        if (pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag) != 0) {
+            return ERR_INVALID_STATE;
         }
-    } else if (status == ThreadStatus::Paused) {
-        // Ready to reschedule
-        ResumeFromWait();
+        AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
+    } else {
+        if (pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag) == 0) {
+            return ERR_INVALID_STATE;
+        }
+        RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
     }
+    return RESULT_SUCCESS;
 }
 
 ResultCode Thread::Sleep(s64 nanoseconds) {
@@ -460,11 +463,27 @@ ResultCode Thread::YieldAndWaitForLoadBalancing() {
     return RESULT_SUCCESS;
 }
 
+void Thread::AddSchedulingFlag(ThreadSchedFlags flag) {
+    const u32 old_state = scheduling_state;
+    pausing_state |= static_cast<u32>(flag);
+    const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus());
+    scheduling_state = base_scheduling | pausing_state;
+    kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state);
+}
+
+void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) {
+    const u32 old_state = scheduling_state;
+    pausing_state &= ~static_cast<u32>(flag);
+    const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus());
+    scheduling_state = base_scheduling | pausing_state;
+    kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state);
+}
+
 void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) {
-    const u32 old_flags = scheduling_state;
+    const u32 old_state = scheduling_state;
     scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) |
                        static_cast<u32>(new_status);
-    kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_flags);
+    kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state);
 }
 
 void Thread::SetCurrentPriority(u32 new_priority) {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index d8a983200c..0a8f7bb654 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -497,11 +497,7 @@ public:
         return affinity_mask;
     }
 
-    ThreadActivity GetActivity() const {
-        return activity;
-    }
-
-    void SetActivity(ThreadActivity value);
+    ResultCode SetActivity(ThreadActivity value);
 
     /// Sleeps this thread for the given amount of nanoseconds.
     ResultCode Sleep(s64 nanoseconds);
@@ -564,11 +560,22 @@ public:
         is_waiting_on_sync = is_waiting;
     }
 
+    bool IsPendingTermination() const {
+        return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited;
+    }
+
+    bool IsPaused() const {
+        return pausing_state != 0;
+    }
+
 private:
     friend class GlobalScheduler;
     friend class Scheduler;
 
     void SetSchedulingStatus(ThreadSchedStatus new_status);
+    void AddSchedulingFlag(ThreadSchedFlags flag);
+    void RemoveSchedulingFlag(ThreadSchedFlags flag);
+
     void SetCurrentPriority(u32 new_priority);
 
     void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
@@ -650,18 +657,17 @@ private:
     u32 ideal_core{0xFFFFFFFF};
     u64 affinity_mask{0x1};
 
-    ThreadActivity activity = ThreadActivity::Normal;
-
     s32 ideal_core_override = -1;
     u64 affinity_mask_override = 0x1;
     u32 affinity_override_count = 0;
 
     u32 scheduling_state = 0;
+    u32 pausing_state = 0;
     bool is_running = false;
     bool is_waiting_on_sync = false;
     bool is_sync_cancelled = false;
 
-    bool will_be_terminated{};
+    bool will_be_terminated = false;
 
     std::string name;
 };