diff options
-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{}; |