mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-11-04 04:34:07 +01:00 
			
		
		
		
	hle: kernel: KConditionVariable: Various updates & simplifications.
This commit is contained in:
		
							parent
							
								
									f62c7091a2
								
							
						
					
					
						commit
						f3d6e31e78
					
				@ -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.
 | 
			
		||||
    {
 | 
			
		||||
        KScopedAutoObject<KThread> owner_thread;
 | 
			
		||||
        ASSERT(owner_thread.IsNull());
 | 
			
		||||
        {
 | 
			
		||||
            KScopedSchedulerLock sl(kernel);
 | 
			
		||||
            cur_thread->SetWaitResult(ResultSuccess);
 | 
			
		||||
 | 
			
		||||
            // 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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Remove the thread as a waiter from the lock owner.
 | 
			
		||||
    KThread* owner_thread{};
 | 
			
		||||
    {
 | 
			
		||||
        KScopedSchedulerLock sl(kernel);
 | 
			
		||||
        KThread* owner_thread = cur_thread->GetLockOwner();
 | 
			
		||||
        if (owner_thread != nullptr) {
 | 
			
		||||
            owner_thread->RemoveWaiter(cur_thread);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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_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)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If timeout is zero, time out.
 | 
			
		||||
        R_UNLESS(timeout != 0, ResultTimedOut);
 | 
			
		||||
 | 
			
		||||
        // Update condition variable tracking.
 | 
			
		||||
        {
 | 
			
		||||
            cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
 | 
			
		||||
            thread_tree.insert(*cur_thread);
 | 
			
		||||
        }
 | 
			
		||||
        cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
 | 
			
		||||
        thread_tree.insert(*cur_thread);
 | 
			
		||||
 | 
			
		||||
        // 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);
 | 
			
		||||
        }
 | 
			
		||||
        // Begin waiting.
 | 
			
		||||
        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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (cur_thread->IsWaitingForConditionVariable()) {
 | 
			
		||||
            thread_tree.erase(thread_tree.iterator_to(*cur_thread));
 | 
			
		||||
            cur_thread->ClearConditionVariable();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the result.
 | 
			
		||||
    // Get the wait result.
 | 
			
		||||
    return cur_thread->GetWaitResult();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user