Parcourir la source

hle: kernel: KConditionVariable: Various updates & simplifications.

bunnei il y a 4 ans
Parent
commit
f3d6e31e78

+ 64 - 120
src/core/hle/kernel/k_condition_variable.cpp

@@ -121,26 +121,31 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
 
         // Determine the next tag.
         u32 next_value{};
-        if (next_owner_thread) {
+        if (next_owner_thread != nullptr) {
             next_value = next_owner_thread->GetAddressKeyValue();
             if (num_waiters > 1) {
                 next_value |= Svc::HandleWaitMask;
             }
 
-            next_owner_thread->EndWait(ResultSuccess);
-        }
-
-        // Write the value to userspace.
-        if (!WriteToUser(system, addr, std::addressof(next_value))) {
-            if (next_owner_thread) {
-                next_owner_thread->SetWaitResult(ResultInvalidCurrentMemory);
+            // Write the value to userspace.
+            ResultCode result{ResultSuccess};
+            if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
+                result = ResultSuccess;
+            } else {
+                result = ResultInvalidCurrentMemory;
             }
 
-            return ResultInvalidCurrentMemory;
+            // Signal the next owner thread.
+            next_owner_thread->EndWait(result);
+            return result;
+        } else {
+            // Just write the value to userspace.
+            R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
+                     ResultInvalidCurrentMemory);
+
+            return ResultSuccess;
         }
     }
-
-    return ResultSuccess;
 }
 
 ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
@@ -148,58 +153,45 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
     ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
 
     // Wait for the address.
+    KThread* owner_thread{};
     {
-        KScopedAutoObject<KThread> owner_thread;
-        ASSERT(owner_thread.IsNull());
-        {
-            KScopedSchedulerLock sl(kernel);
-            cur_thread->SetWaitResult(ResultSuccess);
+        KScopedSchedulerLock sl(kernel);
 
-            // Check if the thread should terminate.
-            R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
+        // Check if the thread should terminate.
+        R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
 
-            {
-                // Read the tag from userspace.
-                u32 test_tag{};
-                R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
-                         ResultInvalidCurrentMemory);
-
-                // If the tag isn't the handle (with wait mask), we're done.
-                R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess);
-
-                // Get the lock owner thread.
-                owner_thread =
-                    kernel.CurrentProcess()->GetHandleTable().GetObjectWithoutPseudoHandle<KThread>(
-                        handle);
-                R_UNLESS(owner_thread.IsNotNull(), ResultInvalidHandle);
-
-                // Update the lock.
-                cur_thread->SetAddressKey(addr, value);
-                owner_thread->AddWaiter(cur_thread);
-
-                // Begin waiting.
-                cur_thread->BeginWait(std::addressof(wait_queue));
-                cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
-                cur_thread->SetMutexWaitAddressForDebugging(addr);
-            }
-        }
-        ASSERT(owner_thread.IsNotNull());
-    }
+        // Read the tag from userspace.
+        u32 test_tag{};
+        R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
 
-    // Remove the thread as a waiter from the lock owner.
-    {
-        KScopedSchedulerLock sl(kernel);
-        KThread* owner_thread = cur_thread->GetLockOwner();
-        if (owner_thread != nullptr) {
-            owner_thread->RemoveWaiter(cur_thread);
-        }
+        // If the tag isn't the handle (with wait mask), we're done.
+        R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
+
+        // Get the lock owner thread.
+        owner_thread = kernel.CurrentProcess()
+                           ->GetHandleTable()
+                           .GetObjectWithoutPseudoHandle<KThread>(handle)
+                           .ReleasePointerUnsafe();
+        R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
+
+        // Update the lock.
+        cur_thread->SetAddressKey(addr, value);
+        owner_thread->AddWaiter(cur_thread);
+
+        // Begin waiting.
+        cur_thread->BeginWait(std::addressof(wait_queue));
+        cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
+        cur_thread->SetMutexWaitAddressForDebugging(addr);
     }
 
+    // Close our reference to the owner thread, now that the wait is over.
+    owner_thread->Close();
+
     // Get the wait result.
     return cur_thread->GetWaitResult();
 }
 
-KThread* KConditionVariable::SignalImpl(KThread* thread) {
+void KConditionVariable::SignalImpl(KThread* thread) {
     // Check pre-conditions.
     ASSERT(kernel.GlobalSchedulerContext().IsLocked());
 
@@ -213,14 +205,13 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
         // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
         // TODO(bunnei): We should call CanAccessAtomic(..) here.
         can_access = true;
-        if (can_access) {
+        if (can_access) [[likely]] {
             UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
                              Svc::HandleWaitMask);
         }
     }
 
-    KThread* thread_to_close = nullptr;
-    if (can_access) {
+    if (can_access) [[likely]] {
         if (prev_tag == Svc::InvalidHandle) {
             // If nobody held the lock previously, we're all good.
             thread->EndWait(ResultSuccess);
@@ -232,10 +223,10 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
                                             static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
                                         .ReleasePointerUnsafe();
 
-            if (owner_thread) {
+            if (owner_thread) [[likely]] {
                 // Add the thread as a waiter on the owner.
                 owner_thread->AddWaiter(thread);
-                thread_to_close = owner_thread;
+                owner_thread->Close();
             } else {
                 // The lock was tagged with a thread that doesn't exist.
                 thread->EndWait(ResultInvalidState);
@@ -245,20 +236,11 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
         // If the address wasn't accessible, note so.
         thread->EndWait(ResultInvalidCurrentMemory);
     }
-
-    return thread_to_close;
 }
 
 void KConditionVariable::Signal(u64 cv_key, s32 count) {
-    // Prepare for signaling.
-    constexpr int MaxThreads = 16;
-
-    KLinkedList<KThread> thread_list{kernel};
-    std::array<KThread*, MaxThreads> thread_array;
-    s32 num_to_close{};
-
     // Perform signaling.
-    s32 num_waiters{};
+    int num_waiters = 0;
     {
         KScopedSchedulerLock sl(kernel);
 
@@ -267,14 +249,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
                (it->GetConditionVariableKey() == cv_key)) {
             KThread* target_thread = std::addressof(*it);
 
-            if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
-                if (num_to_close < MaxThreads) {
-                    thread_array[num_to_close++] = thread;
-                } else {
-                    thread_list.push_back(*thread);
-                }
-            }
-
+            this->SignalImpl(target_thread);
             it = thread_tree.erase(it);
             target_thread->ClearConditionVariable();
             ++num_waiters;
@@ -282,33 +257,20 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
 
         // If we have no waiters, clear the has waiter flag.
         if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) {
-            const u32 has_waiter_flag{};
+            const u32 has_waiter_flag = 0;
             WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
         }
     }
-
-    // Close threads in the array.
-    for (auto i = 0; i < num_to_close; ++i) {
-        thread_array[i]->Close();
-    }
-
-    // Close threads in the list.
-    for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
-        (*it).Close();
-    }
 }
 
 ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
     // Prepare to wait.
-    KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+    KThread* cur_thread = GetCurrentThreadPointer(kernel);
     ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
         kernel, std::addressof(thread_tree));
 
     {
-        KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
-
-        // Set the synced object.
-        cur_thread->SetWaitResult(ResultTimedOut);
+        KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
 
         // Check that the thread isn't terminating.
         if (cur_thread->IsTerminationRequested()) {
@@ -350,38 +312,20 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
             }
         }
 
-        // Update condition variable tracking.
-        {
-            cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
-            thread_tree.insert(*cur_thread);
-        }
+        // If timeout is zero, time out.
+        R_UNLESS(timeout != 0, ResultTimedOut);
 
-        // If the timeout is non-zero, set the thread as waiting.
-        if (timeout != 0) {
-            cur_thread->BeginWait(std::addressof(wait_queue));
-            cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
-            cur_thread->SetMutexWaitAddressForDebugging(addr);
-        }
-    }
-
-    // Cancel the timer wait.
-    kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
-
-    // Remove from the condition variable.
-    {
-        KScopedSchedulerLock sl(kernel);
-
-        if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
-            owner->RemoveWaiter(cur_thread);
-        }
+        // Update condition variable tracking.
+        cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
+        thread_tree.insert(*cur_thread);
 
-        if (cur_thread->IsWaitingForConditionVariable()) {
-            thread_tree.erase(thread_tree.iterator_to(*cur_thread));
-            cur_thread->ClearConditionVariable();
-        }
+        // Begin waiting.
+        cur_thread->BeginWait(std::addressof(wait_queue));
+        cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
+        cur_thread->SetMutexWaitAddressForDebugging(addr);
     }
 
-    // Get the result.
+    // Get the wait result.
     return cur_thread->GetWaitResult();
 }
 

+ 1 - 1
src/core/hle/kernel/k_condition_variable.h

@@ -34,7 +34,7 @@ public:
     [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
 
 private:
-    [[nodiscard]] KThread* SignalImpl(KThread* thread);
+    void SignalImpl(KThread* thread);
 
     ThreadTree thread_tree;