summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/k_condition_variable.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp177
1 files changed, 93 insertions, 84 deletions
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 124149697..efbac0e6a 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -4,7 +4,6 @@
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/k_condition_variable.h"
-#include "core/hle/kernel/k_linked_list.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
@@ -19,36 +18,41 @@ namespace Kernel {
namespace {
-bool ReadFromUser(Core::System& system, u32* out, VAddr address) {
- *out = system.Memory().Read32(address);
+bool ReadFromUser(KernelCore& kernel, u32* out, KProcessAddress address) {
+ *out = GetCurrentMemory(kernel).Read32(GetInteger(address));
return true;
}
-bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
- system.Memory().Write32(address, *p);
+bool WriteToUser(KernelCore& kernel, KProcessAddress address, const u32* p) {
+ GetCurrentMemory(kernel).Write32(GetInteger(address), *p);
return true;
}
-bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
+bool UpdateLockAtomic(Core::System& system, u32* out, KProcessAddress address, u32 if_zero,
u32 new_orr_mask) {
auto& monitor = system.Monitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
- // Load the value from the address.
- const auto expected = monitor.ExclusiveRead32(current_core, address);
+ u32 expected{};
- // Orr in the new mask.
- u32 value = expected | new_orr_mask;
+ while (true) {
+ // Load the value from the address.
+ expected = monitor.ExclusiveRead32(current_core, GetInteger(address));
- // If the value is zero, use the if_zero value, otherwise use the newly orr'd value.
- if (!expected) {
- value = if_zero;
- }
+ // Orr in the new mask.
+ u32 value = expected | new_orr_mask;
+
+ // If the value is zero, use the if_zero value, otherwise use the newly orr'd value.
+ if (!expected) {
+ value = if_zero;
+ }
+
+ // Try to store.
+ if (monitor.ExclusiveWrite32(current_core, GetInteger(address), value)) {
+ break;
+ }
- // Try to store.
- if (!monitor.ExclusiveWrite32(current_core, address, value)) {
// If we failed to store, try again.
- return UpdateLockAtomic(system, out, address, if_zero, new_orr_mask);
}
// We're done.
@@ -58,8 +62,8 @@ bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero
class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
public:
- explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
- : KThreadQueue(kernel_) {}
+ explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel)
+ : KThreadQueue(kernel) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
@@ -76,8 +80,8 @@ private:
public:
explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
- KernelCore& kernel_, KConditionVariable::ThreadTree* t)
- : KThreadQueue(kernel_), m_tree(t) {}
+ KernelCore& kernel, KConditionVariable::ThreadTree* t)
+ : KThreadQueue(kernel), m_tree(t) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
@@ -98,111 +102,113 @@ public:
} // namespace
-KConditionVariable::KConditionVariable(Core::System& system_)
- : system{system_}, kernel{system.Kernel()} {}
+KConditionVariable::KConditionVariable(Core::System& system)
+ : m_system{system}, m_kernel{system.Kernel()} {}
KConditionVariable::~KConditionVariable() = default;
-Result KConditionVariable::SignalToAddress(VAddr addr) {
- KThread* owner_thread = GetCurrentThreadPointer(kernel);
+Result KConditionVariable::SignalToAddress(KProcessAddress addr) {
+ KThread* owner_thread = GetCurrentThreadPointer(m_kernel);
// Signal the address.
{
- KScopedSchedulerLock sl(kernel);
+ KScopedSchedulerLock sl(m_kernel);
// Remove waiter thread.
- s32 num_waiters{};
- KThread* next_owner_thread =
- owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
+ bool has_waiters{};
+ KThread* const next_owner_thread =
+ owner_thread->RemoveUserWaiterByKey(std::addressof(has_waiters), addr);
// Determine the next tag.
u32 next_value{};
if (next_owner_thread != nullptr) {
next_value = next_owner_thread->GetAddressKeyValue();
- if (num_waiters > 1) {
+ if (has_waiters) {
next_value |= Svc::HandleWaitMask;
}
+ }
- // Write the value to userspace.
- Result result{ResultSuccess};
- if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
- result = ResultSuccess;
- } else {
- result = ResultInvalidCurrentMemory;
- }
+ // Synchronize memory before proceeding.
+ std::atomic_thread_fence(std::memory_order_seq_cst);
- // Signal the next owner thread.
- next_owner_thread->EndWait(result);
- return result;
+ // Write the value to userspace.
+ Result result{ResultSuccess};
+ if (WriteToUser(m_kernel, addr, std::addressof(next_value))) [[likely]] {
+ result = ResultSuccess;
} else {
- // Just write the value to userspace.
- R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
- ResultInvalidCurrentMemory);
+ result = ResultInvalidCurrentMemory;
+ }
- return ResultSuccess;
+ // If necessary, signal the next owner thread.
+ if (next_owner_thread != nullptr) {
+ next_owner_thread->EndWait(result);
}
+
+ R_RETURN(result);
}
}
-Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
- KThread* cur_thread = GetCurrentThreadPointer(kernel);
- ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
+Result KConditionVariable::WaitForAddress(Handle handle, KProcessAddress addr, u32 value) {
+ KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
+ ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(m_kernel);
// Wait for the address.
KThread* owner_thread{};
{
- KScopedSchedulerLock sl(kernel);
+ KScopedSchedulerLock sl(m_kernel);
// 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);
+ R_UNLESS(ReadFromUser(m_kernel, 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()
+ owner_thread = GetCurrentProcess(m_kernel)
+ .GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(handle)
.ReleasePointerUnsafe();
R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
// Update the lock.
- cur_thread->SetAddressKey(addr, value);
+ cur_thread->SetUserAddressKey(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();
+ R_RETURN(cur_thread->GetWaitResult());
}
void KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions.
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
// Update the tag.
- VAddr address = thread->GetAddressKey();
+ KProcessAddress address = thread->GetAddressKey();
u32 own_tag = thread->GetAddressKeyValue();
u32 prev_tag{};
bool can_access{};
{
- // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
+ // NOTE: If scheduler lock is not held here, interrupt disable is required.
+ // KScopedInterruptDisable di;
+
// TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true;
if (can_access) [[likely]] {
- UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
+ UpdateLockAtomic(m_system, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask);
}
}
@@ -213,8 +219,8 @@ void KConditionVariable::SignalImpl(KThread* thread) {
thread->EndWait(ResultSuccess);
} else {
// Get the previous owner.
- KThread* owner_thread = kernel.CurrentProcess()
- ->GetHandleTable()
+ KThread* owner_thread = GetCurrentProcess(m_kernel)
+ .GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
.ReleasePointerUnsafe();
@@ -238,55 +244,58 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
- KScopedSchedulerLock sl(kernel);
+ KScopedSchedulerLock sl(m_kernel);
- auto it = thread_tree.nfind_key({cv_key, -1});
- while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
+ auto it = m_tree.nfind_key({cv_key, -1});
+ while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);
- this->SignalImpl(target_thread);
- it = thread_tree.erase(it);
+ it = m_tree.erase(it);
target_thread->ClearConditionVariable();
+
+ this->SignalImpl(target_thread);
+
++num_waiters;
}
// If we have no waiters, clear the has waiter flag.
- if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) {
+ if (it == m_tree.end() || it->GetConditionVariableKey() != cv_key) {
const u32 has_waiter_flag{};
- WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
+ WriteToUser(m_kernel, cv_key, std::addressof(has_waiter_flag));
}
}
}
-Result KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
+Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
- KThread* cur_thread = GetCurrentThreadPointer(kernel);
- ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
- kernel, std::addressof(thread_tree));
+ KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
+ KHardwareTimer* timer{};
+ ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(m_kernel,
+ std::addressof(m_tree));
{
- KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
+ KScopedSchedulerLockAndSleep slp(m_kernel, std::addressof(timer), cur_thread, timeout);
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
- return ResultTerminationRequested;
+ R_THROW(ResultTerminationRequested);
}
// Update the value and process for the next owner.
{
// Remove waiter thread.
- s32 num_waiters{};
+ bool has_waiters{};
KThread* next_owner_thread =
- cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
+ cur_thread->RemoveUserWaiterByKey(std::addressof(has_waiters), addr);
// Update for the next owner thread.
u32 next_value{};
if (next_owner_thread != nullptr) {
// Get the next tag value.
next_value = next_owner_thread->GetAddressKeyValue();
- if (num_waiters > 1) {
+ if (has_waiters) {
next_value |= Svc::HandleWaitMask;
}
@@ -297,14 +306,14 @@ Result KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Write to the cv key.
{
const u32 has_waiter_flag = 1;
- WriteToUser(system, key, std::addressof(has_waiter_flag));
- // TODO(bunnei): We should call DataMemoryBarrier(..) here.
+ WriteToUser(m_kernel, key, std::addressof(has_waiter_flag));
+ std::atomic_thread_fence(std::memory_order_seq_cst);
}
// Write the value to userspace.
- if (!WriteToUser(system, addr, std::addressof(next_value))) {
+ if (!WriteToUser(m_kernel, addr, std::addressof(next_value))) {
slp.CancelSleep();
- return ResultInvalidCurrentMemory;
+ R_THROW(ResultInvalidCurrentMemory);
}
}
@@ -312,17 +321,17 @@ Result KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
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(m_tree), addr, key, value);
+ m_tree.insert(*cur_thread);
// Begin waiting.
+ wait_queue.SetHardwareTimer(timer);
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
- cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Get the wait result.
- return cur_thread->GetWaitResult();
+ R_RETURN(cur_thread->GetWaitResult());
}
} // namespace Kernel