diff options
Diffstat (limited to 'src/core/hle/kernel/address_arbiter.cpp')
-rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 59 |
1 files changed, 55 insertions, 4 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index cfd2c1590..367c4520d 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -5,14 +5,31 @@ #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "core/core.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" +#include "core/hle/lock.h" #include "core/memory.h" namespace Kernel { namespace AddressArbiter { + // Performs actual address waiting logic. + ResultCode WaitForAddress(VAddr address, s64 timeout) { + SharedPtr<Thread> current_thread = GetCurrentThread(); + current_thread->arb_wait_address = address; + current_thread->arb_wait_result = RESULT_TIMEOUT; + current_thread->status = THREADSTATUS_WAIT_ARB; + current_thread->wakeup_callback = nullptr; + + current_thread->WakeAfterDelay(timeout); + + Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); + return RESULT_SUCCESS; + } + // Signals an address being waited on. ResultCode SignalToAddress(VAddr address, s32 value, s32 num_to_wake) { // TODO @@ -33,14 +50,48 @@ namespace Kernel { // Waits on an address if the value passed is less than the argument value, optionally decrementing. ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { - // TODO - return RESULT_SUCCESS; + // Ensure that we can read the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + s32 cur_value; + // Get value, decrementing if less than + { + // Decrement if less than must be an atomic operation. + std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + cur_value = (s32)Memory::Read32(address); + if (cur_value < value) { + Memory::Write32(address, (u32)(cur_value - 1)); + } + } + if (cur_value >= value) { + return ERR_INVALID_STATE; + } + // Short-circuit without rescheduling, if timeout is zero. + if (timeout == 0) { + return RESULT_TIMEOUT; + } + + return WaitForAddress(address, timeout); } // Waits on an address if the value passed is equal to the argument value. ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { - // TODO - return RESULT_SUCCESS; + // Ensure that we can read the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + // Only wait for the address if equal. + if ((s32)Memory::Read32(address) != value) { + return ERR_INVALID_STATE; + } + // Short-circuit without rescheduling, if timeout is zero. + if (timeout == 0) { + return RESULT_TIMEOUT; + } + + return WaitForAddress(address, timeout); } } // namespace AddressArbiter } // namespace Kernel
\ No newline at end of file |