summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp184
-rw-r--r--src/core/hle/kernel/k_condition_variable.h2
2 files changed, 65 insertions, 121 deletions
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 34c1eae65..f343e3c2f 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/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();
}
diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h
index 861dbd420..5e4815d08 100644
--- a/src/core/hle/kernel/k_condition_variable.h
+++ b/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;