diff options
| -rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/event.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 7 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/semaphore.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 94 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 58 | ||||
| -rw-r--r-- | src/core/hle/kernel/timer.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 77 | 
10 files changed, 189 insertions, 83 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; +    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<SharedPtr<WaitObject>, 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<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) { - -    // 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<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;      } -    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;  } | 
