diff options
| author | bunnei <bunneidev@gmail.com> | 2022-01-21 17:10:11 -0800 | 
|---|---|---|
| committer | bunnei <bunneidev@gmail.com> | 2022-01-21 17:12:06 -0800 | 
| commit | 615fb40416b9ee10abf40a036b31d1540886a9b8 (patch) | |
| tree | 8220ac1a9435933b995c4eab0e2e334daad3af11 | |
| parent | f6815086a163029771cad51713601df93738d393 (diff) | |
hle: kernel: KThread: Ensure host (dummy) threads block on locking.
- But do not enter the priority queue, as otherwise they will be scheduled.
- Allows dummy threads to use guest synchronization primitives.
| -rw-r--r-- | src/core/hle/kernel/k_priority_queue.h | 36 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_scheduler.cpp | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.cpp | 37 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.h | 13 | 
4 files changed, 89 insertions, 0 deletions
| diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h index f4d71ad7e..0b894c8cf 100644 --- a/src/core/hle/kernel/k_priority_queue.h +++ b/src/core/hle/kernel/k_priority_queue.h @@ -45,6 +45,7 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {      { t.GetActiveCore() } -> Common::ConvertibleTo<s32>;      { t.GetPriority() } -> Common::ConvertibleTo<s32>; +    { t.IsDummyThread() } -> Common::ConvertibleTo<bool>;  };  template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority> @@ -349,24 +350,49 @@ public:      // Mutators.      constexpr void PushBack(Member* member) { +        // This is for host (dummy) threads that we do not want to enter the priority queue. +        if (member->IsDummyThread()) { +            return; +        } +          this->PushBack(member->GetPriority(), member);      }      constexpr void Remove(Member* member) { +        // This is for host (dummy) threads that we do not want to enter the priority queue. +        if (member->IsDummyThread()) { +            return; +        } +          this->Remove(member->GetPriority(), member);      }      constexpr void MoveToScheduledFront(Member* member) { +        // This is for host (dummy) threads that we do not want to enter the priority queue. +        if (member->IsDummyThread()) { +            return; +        } +          this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);      }      constexpr KThread* MoveToScheduledBack(Member* member) { +        // This is for host (dummy) threads that we do not want to enter the priority queue. +        if (member->IsDummyThread()) { +            return {}; +        } +          return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),                                                  member);      }      // First class fancy operations.      constexpr void ChangePriority(s32 prev_priority, bool is_running, Member* member) { +        // This is for host (dummy) threads that we do not want to enter the priority queue. +        if (member->IsDummyThread()) { +            return; +        } +          ASSERT(IsValidPriority(prev_priority));          // Remove the member from the queues. @@ -383,6 +409,11 @@ public:      constexpr void ChangeAffinityMask(s32 prev_core, const AffinityMaskType& prev_affinity,                                        Member* member) { +        // This is for host (dummy) threads that we do not want to enter the priority queue. +        if (member->IsDummyThread()) { +            return; +        } +          // Get the new information.          const s32 priority = member->GetPriority();          const AffinityMaskType& new_affinity = member->GetAffinityMask(); @@ -412,6 +443,11 @@ public:      }      constexpr void ChangeCore(s32 prev_core, Member* member, bool to_front = false) { +        // This is for host (dummy) threads that we do not want to enter the priority queue. +        if (member->IsDummyThread()) { +            return; +        } +          // Get the new information.          const s32 new_core = member->GetActiveCore();          const s32 priority = member->GetPriority(); diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 1b2a01869..b32d4f285 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -406,6 +406,9 @@ void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduli      } else {          RescheduleCores(kernel, cores_needing_scheduling);      } + +    // Special case to ensure dummy threads that are waiting block. +    current_thread->IfDummyThreadTryWait();  }  u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 4cbf12c11..f42abb8a1 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -1075,12 +1075,46 @@ ResultCode KThread::Sleep(s64 timeout) {      return ResultSuccess;  } +void KThread::IfDummyThreadTryWait() { +    if (!IsDummyThread()) { +        return; +    } + +    if (GetState() != ThreadState::Waiting) { +        return; +    } + +    // Block until we can grab the lock. +    KScopedSpinLock lk{dummy_wait_lock}; +} + +void KThread::IfDummyThreadBeginWait() { +    if (!IsDummyThread()) { +        return; +    } + +    // Ensure the thread will block when IfDummyThreadTryWait is called. +    dummy_wait_lock.Lock(); +} + +void KThread::IfDummyThreadEndWait() { +    if (!IsDummyThread()) { +        return; +    } + +    // Ensure the thread will no longer block. +    dummy_wait_lock.Unlock(); +} +  void KThread::BeginWait(KThreadQueue* queue) {      // Set our state as waiting.      SetState(ThreadState::Waiting);      // Set our wait queue.      wait_queue = queue; + +    // Special case for dummy threads to ensure they block. +    IfDummyThreadBeginWait();  }  void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) { @@ -1106,6 +1140,9 @@ void KThread::EndWait(ResultCode wait_result_) {          }          wait_queue->EndWait(this, wait_result_); + +        // Special case for dummy threads to wakeup if necessary. +        IfDummyThreadEndWait();      }  } diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 77b53a198..d058db62c 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -558,6 +558,10 @@ public:          return thread_type;      } +    [[nodiscard]] bool IsDummyThread() const { +        return GetThreadType() == ThreadType::Dummy; +    } +      void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {          wait_objects_for_debugging.clear();          wait_objects_for_debugging.reserve(objects.size()); @@ -632,6 +636,14 @@ public:          return condvar_key;      } +    // Dummy threads (used for HLE host threads) cannot wait based on the guest scheduler, and +    // therefore will not block on guest kernel synchronization primitives. These methods handle +    // blocking as needed. + +    void IfDummyThreadTryWait(); +    void IfDummyThreadBeginWait(); +    void IfDummyThreadEndWait(); +  private:      static constexpr size_t PriorityInheritanceCountMax = 10;      union SyncObjectBuffer { @@ -750,6 +762,7 @@ private:      bool resource_limit_release_hint{};      StackParameters stack_parameters{};      KSpinLock context_guard{}; +    KSpinLock dummy_wait_lock{};      // For emulation      std::shared_ptr<Common::Fiber> host_context{}; | 
