From 7faf2d8e06e705d1866fa0d7848ff43541a4b172 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 17 Jan 2015 02:03:44 -0500
Subject: [PATCH] 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          | 98 +++++++++++++++++++------
 src/core/hle/kernel/thread.h            | 58 +++++++++++----
 src/core/hle/kernel/timer.cpp           |  6 +-
 src/core/hle/svc.cpp                    | 81 ++++++++++++--------
 10 files changed, 193 insertions(+), 87 deletions(-)

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<bool> WaitSynchronization() override {
+    ResultVal<bool> 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<bool>(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<bool> WaitSynchronization() {
+    virtual ResultVal<bool> 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<bool> WaitSynchronization() override;
+    ResultVal<bool> 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<bool> Mutex::WaitSynchronization() {
+ResultVal<bool> 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<bool> WaitSynchronization() override {
+    ResultVal<bool> 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<bool> Thread::WaitSynchronization() {
+ResultVal<bool> 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<bool>(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;
-
-    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);
-    }
     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_WaitSynchronization(WaitType wait_type, WaitObject* wait_object, unsigned index) {
+    Thread* thread = GetCurrentThread();
+    thread->wait_type = wait_type;
+
+    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<SharedPtr<WaitObject>, unsigned>(wait_object, index));
+
+    ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
+}
+
+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<SharedPtr<Thread>> 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<bool> WaitSynchronization() override;
+    ResultVal<bool> 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<SharedPtr<WaitObject>> wait_objects;
+    std::vector<std::pair<SharedPtr<WaitObject>, 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<Thread> 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<bool> WaitSynchronization() override {
+    ResultVal<bool> 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<bool>(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) {
+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;
 
-    // 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
-
-    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<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]);
+    while (handle_index < handle_count) {
+        SharedPtr<Kernel::Object> 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<bool> wait = object->WaitSynchronization(handle_index);
 
-        // TODO(yuriks): Verify how the real function behaves when an error happens here
-        ResultVal<bool> 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;
+    }
+
+    // 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;
         }
     }
 
-    if (wait_all && unlock_all) {
-        *out = handle_count;
-        return RESULT_SUCCESS.raw;
-    }
-
-    // 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;
 }