From 7faf2d8e06e705d1866fa0d7848ff43541a4b172 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 Jan 2015 02:03:44 -0500 Subject: WaitSynchronizationN: Implement return values --- src/core/hle/kernel/address_arbiter.cpp | 8 +-- src/core/hle/kernel/event.cpp | 4 +- src/core/hle/kernel/kernel.cpp | 4 +- src/core/hle/kernel/kernel.h | 7 +-- src/core/hle/kernel/mutex.cpp | 8 +-- src/core/hle/kernel/semaphore.cpp | 6 +-- src/core/hle/kernel/thread.cpp | 94 ++++++++++++++++++++++++++------- src/core/hle/kernel/thread.h | 58 ++++++++++++++------ src/core/hle/kernel/timer.cpp | 6 +-- src/core/hle/svc.cpp | 77 +++++++++++++++++---------- 10 files changed, 189 insertions(+), 83 deletions(-) (limited to 'src/core/hle') diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index ff1472066..520601455 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -52,13 +52,13 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 // Wait current thread (acquire the arbiter)... case ArbitrationType::WaitIfLessThan: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(object, address); HLE::Reschedule(__func__); } break; case ArbitrationType::WaitIfLessThanWithTimeout: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(object, address); Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); HLE::Reschedule(__func__); } @@ -68,7 +68,7 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 s32 memory_value = Memory::Read32(address) - 1; Memory::Write32(address, memory_value); if (memory_value <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(object, address); HLE::Reschedule(__func__); } break; @@ -78,7 +78,7 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 s32 memory_value = Memory::Read32(address) - 1; Memory::Write32(address, memory_value); if (memory_value <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(object, address); Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); HLE::Reschedule(__func__); } diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 540199e03..4173a980b 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -28,11 +28,11 @@ public: bool signaled; ///< Whether the event has already been signaled std::string name; ///< Name of event (optional) - ResultVal WaitSynchronization() override { + ResultVal WaitSynchronization(unsigned index) override { bool wait = !signaled; if (wait) { AddWaitingThread(GetCurrentThread()); - Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); + Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_EVENT, this, index); } return MakeResult(wait); } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1dba85939..be3495412 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -30,13 +30,13 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { waiting_threads.erase(itr); } -Thread* WaitObject::ResumeNextThread() { +Thread* WaitObject::ReleaseNextThread() { if (waiting_threads.empty()) return nullptr; auto next_thread = waiting_threads.front(); - next_thread->ResumeFromWait(); + next_thread->ReleaseFromWait(this); waiting_threads.erase(waiting_threads.begin()); return next_thread.get(); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 53b3f9143..af4e2f443 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -66,9 +66,10 @@ public: /** * Wait for kernel object to synchronize. + * @param index Index of wait object (only applies to WaitSynchronizationN) * @return True if the current thread should wait as a result of the wait */ - virtual ResultVal WaitSynchronization() { + virtual ResultVal WaitSynchronization(unsigned index=0) { LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); return UnimplementedFunction(ErrorModule::Kernel); } @@ -111,10 +112,10 @@ public: void RemoveWaitingThread(Thread* thead); /** - * Resumes (and removes) the next thread waiting on this object + * Releases (and removes) the next thread waiting on this object * @return Pointer to the thread that was resumed, nullptr if no threads are waiting */ - Thread* ResumeNextThread(); + Thread* ReleaseNextThread(); /// Releases all threads waiting on this object void ReleaseAllWaitingThreads(); diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 35d829606..78063b8f1 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -26,7 +26,7 @@ public: Handle lock_thread; ///< Handle to thread that currently has mutex std::string name; ///< Name of mutex (optional) - ResultVal WaitSynchronization() override; + ResultVal WaitSynchronization(unsigned index) override; }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -50,7 +50,7 @@ void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandl */ void ResumeWaitingThread(Mutex* mutex) { // Find the next waiting thread for the mutex... - auto next_thread = mutex->ResumeNextThread(); + auto next_thread = mutex->ReleaseNextThread(); if (next_thread != nullptr) { MutexAcquireLock(mutex, next_thread->GetHandle()); } else { @@ -155,11 +155,11 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { return handle; } -ResultVal Mutex::WaitSynchronization() { +ResultVal Mutex::WaitSynchronization(unsigned index) { bool wait = locked; if (locked) { AddWaitingThread(GetCurrentThread()); - Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this); + Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_MUTEX, this, index); } else { // Lock the mutex when the first thread accesses it locked = true; diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index af2c465e4..288928441 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -32,11 +32,11 @@ public: return available_count > 0; } - ResultVal WaitSynchronization() override { + ResultVal WaitSynchronization(unsigned index) override { bool wait = !IsAvailable(); if (wait) { - Kernel::WaitCurrentThread(WAITTYPE_SEMA, this); + Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_SEMA, this, index); AddWaitingThread(GetCurrentThread()); } else { --available_count; @@ -82,7 +82,7 @@ ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { // Notify some of the threads that the semaphore has been released // stop once the semaphore is full again or there are no more waiting threads - while (semaphore->IsAvailable() && semaphore->ResumeNextThread() != nullptr) { + while (semaphore->IsAvailable() && semaphore->ReleaseNextThread() != nullptr) { --semaphore->available_count; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 00b72477e..0c9ecc091 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -22,11 +22,11 @@ namespace Kernel { -ResultVal Thread::WaitSynchronization() { +ResultVal Thread::WaitSynchronization(unsigned index) { const bool wait = status != THREADSTATUS_DORMANT; if (wait) { AddWaitingThread(GetCurrentThread()); - WaitCurrentThread(WAITTYPE_THREADEND, this); + WaitCurrentThread_WaitSynchronization(WAITTYPE_THREADEND, this, index); } return MakeResult(wait); @@ -92,11 +92,11 @@ static bool CheckWaitType(const Thread* thread, WaitType type) { /// Check if a thread is blocking on a specified wait type with a specified handle static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { - auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); - if (itr == thread->wait_objects.end()) { - return false; + for (auto itr = thread->wait_objects.begin(); itr != thread->wait_objects.end(); ++itr) { + if (itr->first == wait_object) + return CheckWaitType(thread, type); } - return CheckWaitType(thread, type); + return false; } /// Check if a thread is blocking on a specified wait type with a specified handle and address @@ -111,7 +111,7 @@ void Thread::Stop(const char* reason) { ChangeReadyState(this, false); status = THREADSTATUS_DORMANT; - ResumeAllWaitingThreads(); + ReleaseAllWaitingThreads(); // Stopped threads are never waiting. wait_type = WAITTYPE_NONE; @@ -135,7 +135,7 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) { } /// Arbitrate the highest priority thread that is waiting -Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { +Thread* ArbitrateHighestPriorityThread(WaitObject* arbiter, u32 address) { Thread* highest_priority_thread = nullptr; s32 priority = THREADPRIO_LOWEST; @@ -155,19 +155,19 @@ Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { // If a thread was arbitrated, resume it if (nullptr != highest_priority_thread) { - highest_priority_thread->ResumeFromWait(); + highest_priority_thread->ReleaseFromWait(arbiter); } return highest_priority_thread; } /// Arbitrate all threads currently waiting -void ArbitrateAllThreads(Object* arbiter, u32 address) { +void ArbitrateAllThreads(WaitObject* arbiter, u32 address) { // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) - thread->ResumeFromWait(); + thread->ReleaseFromWait(arbiter); } } @@ -220,19 +220,32 @@ static Thread* NextThread() { return next; } -void WaitCurrentThread(WaitType wait_type, WaitObject* wait_object) { +void WaitCurrentThread(WaitType wait_type) { + Thread* thread = GetCurrentThread(); + thread->wait_type = wait_type; + ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); +} + +void WaitCurrentThread_WaitSynchronization(WaitType wait_type, WaitObject* wait_object, unsigned index) { Thread* thread = GetCurrentThread(); thread->wait_type = wait_type; - auto res = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); - if (res == thread->wait_objects.end()) { - thread->wait_objects.push_back(wait_object); + bool insert_wait_object = true; + for (auto itr = thread->wait_objects.begin(); itr < thread->wait_objects.end(); ++itr) { + if (itr->first == wait_object) { + insert_wait_object = false; + break; + } } + + if (insert_wait_object) + thread->wait_objects.push_back(std::pair, unsigned>(wait_object, index)); + ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } -void WaitCurrentThread(WaitType wait_type, WaitObject* wait_object, VAddr wait_address) { - WaitCurrentThread(wait_type, wait_object); +void WaitCurrentThread_ArbitrateAddress(WaitObject* wait_object, VAddr wait_address) { + WaitCurrentThread_WaitSynchronization(WaitType::WAITTYPE_ARB, wait_object, 0); GetCurrentThread()->wait_address = wait_address; } @@ -248,6 +261,9 @@ static void ThreadWakeupCallback(u64 parameter, int cycles_late) { return; } + thread->SetReturnValue(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, + ErrorSummary::StatusChanged, ErrorLevel::Info), -1); + thread->ResumeFromWait(); } @@ -262,7 +278,40 @@ void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); } -/// Resumes a thread from waiting by marking it as "ready" +void Thread::ReleaseFromWait(WaitObject* wait_object) { + if (wait_objects.empty()) { + LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); + return; + } + + // Remove this thread from the wait_object + wait_object->RemoveWaitingThread(this); + + // Find the waiting object + auto itr = wait_objects.begin(); + for (; itr != wait_objects.end(); ++itr) { + if (wait_object == itr->first) + break; + } + unsigned index = itr->second; + + // Remove the wait_object from this thread + if (itr != wait_objects.end()) + wait_objects.erase(itr); + + // If wait_all=false, resume the thread on a release wait_object from wait + if (!wait_all) { + SetReturnValue(RESULT_SUCCESS, index); + ResumeFromWait(); + } else { + // Otherwise, wait_all=true, only resume the thread if all wait_object's have been released + if (wait_objects.empty()) { + SetReturnValue(RESULT_SUCCESS, -1); + ResumeFromWait(); + } + } +} + void Thread::ResumeFromWait() { // Cancel any outstanding wakeup events CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); @@ -271,11 +320,12 @@ void Thread::ResumeFromWait() { // Remove this thread from all other WaitObjects for (auto wait_object : wait_objects) - wait_object->RemoveWaitingThread(this); + wait_object.first->RemoveWaitingThread(this); wait_objects.clear(); wait_type = WAITTYPE_NONE; + wait_all = false; if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { ChangeReadyState(this, true); } @@ -342,6 +392,7 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, thread->initial_priority = thread->current_priority = priority; thread->processor_id = processor_id; thread->wait_type = WAITTYPE_NONE; + thread->wait_all = false; thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); @@ -432,6 +483,11 @@ void Reschedule() { } } +void Thread::SetReturnValue(ResultCode return_val, s32 out_val) { + context.cpu_registers[0] = return_val.raw; + context.cpu_registers[1] = out_val; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 9ec96c18c..f3dc4eec0 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -70,7 +70,7 @@ public: inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } inline bool IsIdle() const { return idle; } - ResultVal WaitSynchronization() override; + ResultVal WaitSynchronization(unsigned index) override; s32 GetPriority() const { return current_priority; } void SetPriority(s32 priority); @@ -78,9 +78,29 @@ public: u32 GetThreadId() const { return thread_id; } void Stop(const char* reason); - /// Resumes a thread from waiting by marking it as "ready". + + /** + * Release an object from the thread's wait list + * @param wait_object WaitObject to release from the thread's wait list + */ + void ReleaseFromWait(WaitObject* wait_object); + + /// Resumes a thread from waiting by marking it as "ready" void ResumeFromWait(); + /** + * Sets the waiting mode of the thread + * @param wait_all If true, wait for all objects, otherwise just wait for the first one + */ + void SetWaitAll(bool wait_all) { this->wait_all = wait_all; } + + /** + * Sets the output values after the thread awakens from WaitSynchronization + * @param return_val Value returned + * @param out_val Value to set to the output parameter + */ + void SetReturnValue(ResultCode return_val, s32 out_val); + Core::ThreadContext context; u32 thread_id; @@ -96,7 +116,7 @@ public: s32 processor_id; WaitType wait_type; - std::vector> wait_objects; + std::vector, unsigned>> wait_objects; VAddr wait_address; std::string name; @@ -105,6 +125,8 @@ public: bool idle = false; private: + bool wait_all = false; + Thread() = default; }; @@ -115,37 +137,41 @@ SharedPtr SetupMainThread(s32 priority, u32 stack_size); void Reschedule(); /// Arbitrate the highest priority thread that is waiting -Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address); +Thread* ArbitrateHighestPriorityThread(WaitObject* arbiter, u32 address); /// Arbitrate all threads currently waiting... -void ArbitrateAllThreads(Object* arbiter, u32 address); +void ArbitrateAllThreads(WaitObject* arbiter, u32 address); /// Gets the current thread Thread* GetCurrentThread(); /** - * Puts the current thread in the wait state for the given type + * Waits the current thread for the given type * @param wait_type Type of wait - * @param wait_object Kernel object that we are waiting on, defaults to current thread */ -void WaitCurrentThread(WaitType wait_type, WaitObject* wait_object = GetCurrentThread()); +void WaitCurrentThread(WaitType wait_type); /** - * Schedules an event to wake up the specified thread after the specified delay. - * @param thread The thread to wake after the delay. - * @param nanoseconds The time this thread will be allowed to sleep for. + * Waits the current thread from a WaitSynchronization call + * @param wait_type Type of wait + * @param wait_object Kernel object that we are waiting on + * @param index Index of calling object (for WaitSynchronizationN only) */ -void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); +void WaitCurrentThread_WaitSynchronization(WaitType wait_type, WaitObject* wait_object, unsigned index=0); /** - * Puts the current thread in the wait state for the given type - * @param wait_type Type of wait + * Waits the current thread from an ArbitrateAddress call * @param wait_object Kernel object that we are waiting on * @param wait_address Arbitration address used to resume from wait */ -void WaitCurrentThread(WaitType wait_type, WaitObject* wait_object, VAddr wait_address); - +void WaitCurrentThread_ArbitrateAddress(WaitObject* wait_object, VAddr wait_address); +/** + * Schedules an event to wake up the specified thread after the specified delay. + * @param handle The thread handle. + * @param nanoseconds The time this thread will be allowed to sleep for. + */ +void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); /** * Sets up the idle thread, this is a thread that is intended to never execute instructions, diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 2d4fa4c01..c97ae6c5c 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -29,11 +29,11 @@ public: u64 initial_delay; ///< The delay until the timer fires for the first time u64 interval_delay; ///< The delay until the timer fires after the first time - ResultVal WaitSynchronization() override { + ResultVal WaitSynchronization(unsigned index) override { bool wait = !signaled; if (wait) { AddWaitingThread(GetCurrentThread()); - Kernel::WaitCurrentThread(WAITTYPE_TIMER, this); + Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_TIMER, this, index); } return MakeResult(wait); } @@ -91,7 +91,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { timer->signaled = true; // Resume all waiting threads - timer->ResumeAllWaitingThreads(); + timer->ReleaseAllWaitingThreads(); if (timer->reset_type == RESETTYPE_ONESHOT) timer->signaled = false; diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index a487f757c..170ac87f3 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -133,6 +133,9 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { if (wait.Succeeded() && *wait) { // Create an event to wake the thread up after the specified nanosecond delay has passed Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); + + Kernel::GetCurrentThread()->SetWaitAll(false); + HLE::Reschedule(__func__); } @@ -140,44 +143,64 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { } /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, - s64 nano_seconds) { - - // TODO(bunnei): Do something with nano_seconds, currently ignoring this - bool unlock_all = true; - bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated +static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { + bool wait_thread = false; + bool wait_all_succeeded = false; + int handle_index = 0; - LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", - handle_count, (wait_all ? "true" : "false"), nano_seconds); - - // Iterate through each handle, synchronize kernel object - for (s32 i = 0; i < handle_count; i++) { - SharedPtr object = Kernel::g_handle_table.GetGeneric(handles[i]); + while (handle_index < handle_count) { + SharedPtr object = Kernel::g_handle_table.GetGeneric(handles[handle_index]); if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; - LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], - object->GetTypeName().c_str(), object->GetName().c_str()); + ResultVal wait = object->WaitSynchronization(handle_index); - // TODO(yuriks): Verify how the real function behaves when an error happens here - ResultVal wait_result = object->WaitSynchronization(); - bool wait = wait_result.Succeeded() && *wait_result; + wait_thread = (wait.Succeeded() && *wait); - if (!wait && !wait_all) { - *out = i; - return RESULT_SUCCESS.raw; - } else { - unlock_all = false; + // If this object waited and we are waiting on all objects to synchronize + if (wait_thread && wait_all) { + // Enforce later on that this thread does not continue + wait_all_succeeded = true; } + + // If this object synchronized and we are not waiting on all objects to synchronize + if (!wait_thread && !wait_all) + // We're done, the thread will continue + break; + + handle_index++; + } + + // Change the thread state to waiting if blocking on all handles... + if (wait_thread || wait_all_succeeded) { + // Create an event to wake the thread up after the specified nanosecond delay has passed + Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); + Kernel::GetCurrentThread()->SetWaitAll(wait_all); + + HLE::Reschedule(__func__); + + // NOTE: output of this SVC will be set later depending on how the thread resumes + return RESULT_DUMMY.raw; } - if (wait_all && unlock_all) { - *out = handle_count; - return RESULT_SUCCESS.raw; + // Acquire objects if we did not wait... + for (int i = 0; i < handle_count; ++i) { + auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); + + // Acquire the object if it is not waiting... + if (!object->ShouldWait()) { + object->Acquire(); + + // If this was the first non-waiting object and 'wait_all' is false, don't acquire + // any other objects + if (!wait_all) + break; + } } - // Check for next thread to schedule - HLE::Reschedule(__func__); + // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does + // not seem to set it to any meaningful value. + *out = wait_all ? 0 : handle_index; return RESULT_SUCCESS.raw; } -- cgit v1.2.3