diff options
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 110 | ||||
-rw-r--r-- | src/core/hle/kernel/address_arbiter.h | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/svc.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.h | 6 |
4 files changed, 110 insertions, 10 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 367c4520d..4556199ab 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -27,24 +27,122 @@ namespace Kernel { current_thread->WakeAfterDelay(timeout); Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); - return RESULT_SUCCESS; + return current_thread->arb_wait_result; + } + + // Gets the threads waiting on an address. + void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>> &waiting_threads, VAddr address) { + auto RetrieveWaitingThreads = + [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { + const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); + auto& thread_list = scheduler->GetThreadList(); + + for (auto& thread : thread_list) { + if (thread->arb_wait_address == arb_addr) + waiting_threads.push_back(thread); + } + }; + + // Retrieve a list of all threads that are waiting for this address. + RetrieveWaitingThreads(0, waiting_threads, address); + RetrieveWaitingThreads(1, waiting_threads, address); + RetrieveWaitingThreads(2, waiting_threads, address); + RetrieveWaitingThreads(3, waiting_threads, address); + // Sort them by priority, such that the highest priority ones come first. + std::sort(waiting_threads.begin(), waiting_threads.end(), + [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { + return lhs->current_priority < rhs->current_priority; + }); + } + + // Wake up num_to_wake (or all) threads in a vector. + void WakeThreads(std::vector<SharedPtr<Thread>> &waiting_threads, s32 num_to_wake) { + // Only process up to 'target' threads, unless 'target' is <= 0, in which case process + // them all. + size_t last = waiting_threads.size(); + if (num_to_wake > 0) + last = num_to_wake; + + // Signal the waiting threads. + // TODO: Rescheduling should not occur while waking threads. How can it be prevented? + for (size_t i = 0; i < last; i++) { + ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB); + waiting_threads[i]->arb_wait_result = RESULT_SUCCESS; + waiting_threads[i]->ResumeFromWait(); + } + } // Signals an address being waited on. - ResultCode SignalToAddress(VAddr address, s32 value, s32 num_to_wake) { - // TODO + ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { + // Get threads waiting on the address. + std::vector<SharedPtr<Thread>> waiting_threads; + GetThreadsWaitingOnAddress(waiting_threads, address); + + WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; } // Signals an address being waited on and increments its value if equal to the value argument. ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { - // TODO - return RESULT_SUCCESS; + // Ensure that we can write to the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + s32 cur_value; + // Get value, incrementing if equal. + { + // Increment if Equal 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; + } + + return SignalToAddress(address, num_to_wake); } // Signals an address being waited on and modifies its value based on waiting thread count if equal to the value argument. ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { - // TODO + // Ensure that we can write to the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + // Get threads waiting on the address. + std::vector<SharedPtr<Thread>> waiting_threads; + GetThreadsWaitingOnAddress(waiting_threads, address); + + // Determine the modified value depending on the waiting count. + s32 updated_value; + if (waiting_threads.size() == 0) { + updated_value = value - 1; + } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) { + updated_value = value + 1; + } else { + updated_value = value; + } + s32 cur_value; + // Perform an atomic update if equal. + { + 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)(updated_value)); + } + } + + // Only continue if equal. + if (cur_value != value) { + return ERR_INVALID_STATE; + } + + WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 3eba103dd..32d4a77a9 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -21,7 +21,7 @@ namespace Kernel { ModifyByWaitingCountAndSignalIfEqual = 2, }; - ResultCode SignalToAddress(VAddr address, s32 value, s32 num_to_wake); + ResultCode SignalToAddress(VAddr address, s32 num_to_wake); ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4b4831c08..24dd50938 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -726,7 +726,7 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to switch ((AddressArbiter::SignalType)type) { case AddressArbiter::SignalType::Signal: - return AddressArbiter::SignalToAddress(address, value, num_to_wake); + return AddressArbiter::SignalToAddress(address, num_to_wake); case AddressArbiter::SignalType::IncrementAndSignalIfEqual: return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake); case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual: diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 79e5d6e5c..0a76bd222 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -230,8 +230,10 @@ public: VAddr condvar_wait_address; VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address Handle wait_handle; ///< The handle used to wait for the mutex. - VAddr arb_wait_address; ///< If waiting for an AddressArbiter, this is the address - ResultCode arb_wait_result; ///< If waiting for an AddressArbiter, this is the result that will be returned. + + // If waiting for an AddressArbiter, this is the address being waited on. + VAddr arb_wait_address; + ResultCode arb_wait_result{RESULT_SUCCESS}; ///< Result returned when done waiting on AddressArbiter. std::string name; |