From c22bac6398ff1705992fc44b2c29775c84cff662 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 Jan 2015 19:22:50 -0500 Subject: [PATCH 01/27] Kernel: Added WaitObject and changed "waitable" objects inherit from it. --- src/core/hle/kernel/event.cpp | 26 +++++++------------------- src/core/hle/kernel/kernel.cpp | 26 ++++++++++++++++++++++++++ src/core/hle/kernel/kernel.h | 25 +++++++++++++++++++++++++ src/core/hle/kernel/mutex.cpp | 29 ++++++----------------------- src/core/hle/kernel/semaphore.cpp | 11 +++-------- src/core/hle/kernel/thread.cpp | 11 ++--------- src/core/hle/kernel/thread.h | 4 +--- src/core/hle/kernel/timer.cpp | 12 +++--------- 8 files changed, 73 insertions(+), 71 deletions(-) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 271190dbe9..bf71e9edbe 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -14,7 +14,7 @@ namespace Kernel { -class Event : public Object { +class Event : public WaitObject { public: std::string GetTypeName() const override { return "Event"; } std::string GetName() const override { return name; } @@ -27,16 +27,12 @@ public: bool locked; ///< Event signal wait bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough) - std::vector waiting_threads; ///< Threads that are waiting for the event std::string name; ///< Name of event (optional) ResultVal WaitSynchronization() override { bool wait = locked; if (locked) { - Handle thread = GetCurrentThread()->GetHandle(); - if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { - waiting_threads.push_back(thread); - } + AddWaitingThread(GetCurrentThread()); Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); } if (reset_type != RESETTYPE_STICKY && !permanent_locked) { @@ -86,20 +82,12 @@ ResultCode SignalEvent(const Handle handle) { if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); // Resume threads waiting for event to signal - bool event_caught = false; - for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { - Thread* thread = Kernel::g_handle_table.Get(evt->waiting_threads[i]).get(); - if (thread != nullptr) - thread->ResumeFromWait(); - - // If any thread is signalled awake by this event, assume the event was "caught" and reset - // the event. This will result in the next thread waiting on the event to block. Otherwise, - // the event will not be reset, and the next thread to call WaitSynchronization on it will - // not block. Not sure if this is correct behavior, but it seems to work. - event_caught = true; - } - evt->waiting_threads.clear(); + bool event_caught = evt->ResumeAllWaitingThreads(); + // If any thread is signalled awake by this event, assume the event was "caught" and reset + // the event. This will result in the next thread waiting on the event to block. Otherwise, + // the event will not be reset, and the next thread to call WaitSynchronization on it will + // not block. Not sure if this is correct behavior, but it seems to work. if (!evt->permanent_locked) { evt->locked = event_caught; } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d3684896f6..07e96e633b 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -18,6 +18,32 @@ SharedPtr g_main_thread = nullptr; HandleTable g_handle_table; u64 g_program_id = 0; +void WaitObject::AddWaitingThread(Thread* thread) { + if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { + waiting_threads.push_back(thread); + } +} + +Thread* WaitObject::ResumeNextThread() { + if (waiting_threads.empty()) return nullptr; + + auto next_thread = waiting_threads.front(); + + next_thread->ResumeFromWait(); + waiting_threads.erase(waiting_threads.begin()); + + return next_thread.get(); +} + +void WaitObject::ReleaseAllWaitingThreads() { + auto waiting_threads_copy = waiting_threads; + + for (auto thread : waiting_threads_copy) + thread->ReleaseWaitObject(this); + + waiting_threads.clear(); +} + HandleTable::HandleTable() { next_generation = 1; Clear(); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 5e5217b78b..a9af9de887 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -8,6 +8,8 @@ #include #include +#include + #include "common/common.h" #include "core/hle/result.h" @@ -92,6 +94,29 @@ inline void intrusive_ptr_release(Object* object) { template using SharedPtr = boost::intrusive_ptr; +/// Class that represents a Kernel object that a thread can be waiting on +class WaitObject : public Object { +public: + + /** + * Add a thread to wait on this object + * @param thread Pointer to thread to add + */ + void AddWaitingThread(Thread* thread); + + /** + * Resumes the next thread waiting on this object + * @return Pointer to the thread that was resumed, nullptr if no threads are waiting + */ + Thread* ResumeNextThread(); + + /// Releases all threads waiting on this object + void ReleaseAllWaitingThreads(); + +private: + std::vector waiting_threads; ///< Threads waiting for this object to become available +}; + /** * This class allows the creation of Handles, which are references to objects that can be tested * for validity and looked up. Here they are used to pass references to kernel objects to/from the diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 853a5dd741..35d8296062 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -13,7 +13,7 @@ namespace Kernel { -class Mutex : public Object { +class Mutex : public WaitObject { public: std::string GetTypeName() const override { return "Mutex"; } std::string GetName() const override { return name; } @@ -24,7 +24,6 @@ public: bool initial_locked; ///< Initial lock state when mutex was created bool locked; ///< Current locked state Handle lock_thread; ///< Handle to thread that currently has mutex - std::vector waiting_threads; ///< Threads that are waiting for the mutex std::string name; ///< Name of mutex (optional) ResultVal WaitSynchronization() override; @@ -45,36 +44,20 @@ void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandl mutex->lock_thread = thread; } -bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) { - MutexAcquireLock(mutex, thread_handle); - - Thread* thread = Kernel::g_handle_table.Get(thread_handle).get(); - if (thread == nullptr) { - LOG_ERROR(Kernel, "Called with invalid handle: %08X", thread_handle); - return false; - } - - thread->ResumeFromWait(); - return true; -} - /** * Resumes a thread waiting for the specified mutex * @param mutex The mutex that some thread is waiting on */ void ResumeWaitingThread(Mutex* mutex) { // Find the next waiting thread for the mutex... - if (mutex->waiting_threads.empty()) { + auto next_thread = mutex->ResumeNextThread(); + if (next_thread != nullptr) { + MutexAcquireLock(mutex, next_thread->GetHandle()); + } else { // Reset mutex lock thread handle, nothing is waiting mutex->locked = false; mutex->lock_thread = -1; } - else { - // Resume the next waiting thread and re-lock the mutex - std::vector::iterator iter = mutex->waiting_threads.begin(); - ReleaseMutexForThread(mutex, *iter); - mutex->waiting_threads.erase(iter); - } } void MutexEraseLock(Mutex* mutex) { @@ -175,7 +158,7 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { ResultVal Mutex::WaitSynchronization() { bool wait = locked; if (locked) { - waiting_threads.push_back(GetCurrentThread()->GetHandle()); + AddWaitingThread(GetCurrentThread()); Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this); } else { // Lock the mutex when the first thread accesses it diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 88ec9a104c..af2c465e49 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -12,7 +12,7 @@ namespace Kernel { -class Semaphore : public Object { +class Semaphore : public WaitObject { public: std::string GetTypeName() const override { return "Semaphore"; } std::string GetName() const override { return name; } @@ -22,7 +22,6 @@ public: s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have s32 available_count; ///< Number of free slots left in the semaphore - std::queue waiting_threads; ///< Threads that are waiting for the semaphore std::string name; ///< Name of semaphore (optional) /** @@ -38,7 +37,7 @@ public: if (wait) { Kernel::WaitCurrentThread(WAITTYPE_SEMA, this); - waiting_threads.push(GetCurrentThread()->GetHandle()); + AddWaitingThread(GetCurrentThread()); } else { --available_count; } @@ -83,11 +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->waiting_threads.empty() && semaphore->IsAvailable()) { - Thread* thread = Kernel::g_handle_table.Get(semaphore->waiting_threads.front()).get(); - if (thread != nullptr) - thread->ResumeFromWait(); - semaphore->waiting_threads.pop(); + while (semaphore->IsAvailable() && semaphore->ResumeNextThread() != nullptr) { --semaphore->available_count; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index bc86a7c598..8456727021 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -25,10 +25,7 @@ namespace Kernel { ResultVal Thread::WaitSynchronization() { const bool wait = status != THREADSTATUS_DORMANT; if (wait) { - Thread* thread = GetCurrentThread(); - if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { - waiting_threads.push_back(thread); - } + AddWaitingThread(GetCurrentThread()); WaitCurrentThread(WAITTYPE_THREADEND, this); } @@ -110,11 +107,7 @@ void Thread::Stop(const char* reason) { ChangeReadyState(this, false); status = THREADSTATUS_DORMANT; - for (auto& waiting_thread : waiting_threads) { - if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this)) - waiting_thread->ResumeFromWait(); - } - waiting_threads.clear(); + ResumeAllWaitingThreads(); // Stopped threads are never waiting. wait_type = WAITTYPE_NONE; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 8c9f63aa5c..daaeb26a40 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -52,7 +52,7 @@ enum WaitType { namespace Kernel { -class Thread : public Kernel::Object { +class Thread : public WaitObject { public: static ResultVal> Create(std::string name, VAddr entry_point, s32 priority, u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); @@ -99,8 +99,6 @@ public: Object* wait_object; VAddr wait_address; - std::vector> waiting_threads; - std::string name; /// Whether this thread is intended to never actually be executed, i.e. always idle diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 3b0452d4d9..2d4fa4c011 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -13,7 +13,7 @@ namespace Kernel { -class Timer : public Object { +class Timer : public WaitObject { public: std::string GetTypeName() const override { return "Timer"; } std::string GetName() const override { return name; } @@ -24,7 +24,6 @@ public: ResetType reset_type; ///< The ResetType of this timer bool signaled; ///< Whether the timer has been signaled or not - std::set waiting_threads; ///< Threads that are waiting for the timer std::string name; ///< Name of timer (optional) u64 initial_delay; ///< The delay until the timer fires for the first time @@ -33,7 +32,7 @@ public: ResultVal WaitSynchronization() override { bool wait = !signaled; if (wait) { - waiting_threads.insert(GetCurrentThread()->GetHandle()); + AddWaitingThread(GetCurrentThread()); Kernel::WaitCurrentThread(WAITTYPE_TIMER, this); } return MakeResult(wait); @@ -92,12 +91,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { timer->signaled = true; // Resume all waiting threads - for (Handle thread_handle : timer->waiting_threads) { - if (SharedPtr thread = Kernel::g_handle_table.Get(thread_handle)) - thread->ResumeFromWait(); - } - - timer->waiting_threads.clear(); + timer->ResumeAllWaitingThreads(); if (timer->reset_type == RESETTYPE_ONESHOT) timer->signaled = false; From 5e77e2e1de73ce7786f52f2a74c28182fa4aa845 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 Jan 2015 23:19:22 -0500 Subject: [PATCH 02/27] WaitObject: Added RemoveWaitingThread, fixed a bug, and cleanup. --- src/core/hle/kernel/kernel.cpp | 13 ++++++++++--- src/core/hle/kernel/kernel.h | 8 +++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 07e96e633b..1dba85939e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -19,13 +19,20 @@ HandleTable g_handle_table; u64 g_program_id = 0; void WaitObject::AddWaitingThread(Thread* thread) { - if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { + auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); + if (itr == waiting_threads.end()) waiting_threads.push_back(thread); - } +} + +void WaitObject::RemoveWaitingThread(Thread* thread) { + auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); + if (itr != waiting_threads.end()) + waiting_threads.erase(itr); } Thread* WaitObject::ResumeNextThread() { - if (waiting_threads.empty()) return nullptr; + if (waiting_threads.empty()) + return nullptr; auto next_thread = waiting_threads.front(); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index a9af9de887..53b3f91438 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -105,7 +105,13 @@ public: void AddWaitingThread(Thread* thread); /** - * Resumes the next thread waiting on this object + * Removes a thread from waiting on this object (e.g. if it was resumed already) + * @param thread Pointer to thread to remove + */ + void RemoveWaitingThread(Thread* thead); + + /** + * Resumes (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(); From 14cbbf4d9b8e07f9f2d679bcf66c2180463ae57c Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 Jan 2015 23:33:37 -0500 Subject: [PATCH 03/27] Event: Get rid of permanent_lock hack. --- src/core/hle/kernel/event.cpp | 37 ++++++++--------------------------- src/core/hle/kernel/event.h | 7 ------- 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index bf71e9edbe..9dd3d0f5df 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -26,7 +26,6 @@ public: ResetType reset_type; ///< Current ResetType bool locked; ///< Event signal wait - bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough) std::string name; ///< Name of event (optional) ResultVal WaitSynchronization() override { @@ -35,27 +34,13 @@ public: AddWaitingThread(GetCurrentThread()); Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); } - if (reset_type != RESETTYPE_STICKY && !permanent_locked) { + if (reset_type != RESETTYPE_STICKY) { locked = true; } return MakeResult(wait); } }; -/** - * Hackish function to set an events permanent lock state, used to pass through synch blocks - * @param handle Handle to event to change - * @param permanent_locked Boolean permanent locked value to set event - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { - Event* evt = g_handle_table.Get(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); - - evt->permanent_locked = permanent_locked; - return RESULT_SUCCESS; -} - /** * Changes whether an event is locked or not * @param handle Handle to event to change @@ -66,9 +51,8 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) { Event* evt = g_handle_table.Get(handle).get(); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); - if (!evt->permanent_locked) { - evt->locked = locked; - } + evt->locked = locked; + return RESULT_SUCCESS; } @@ -81,16 +65,13 @@ ResultCode SignalEvent(const Handle handle) { Event* evt = g_handle_table.Get(handle).get(); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); - // Resume threads waiting for event to signal - bool event_caught = evt->ResumeAllWaitingThreads(); - // If any thread is signalled awake by this event, assume the event was "caught" and reset // the event. This will result in the next thread waiting on the event to block. Otherwise, // the event will not be reset, and the next thread to call WaitSynchronization on it will // not block. Not sure if this is correct behavior, but it seems to work. - if (!evt->permanent_locked) { - evt->locked = event_caught; - } + // TODO(bunnei): Test how this works on hardware + evt->locked = evt->ResumeAllWaitingThreads(); + return RESULT_SUCCESS; } @@ -103,9 +84,8 @@ ResultCode ClearEvent(Handle handle) { Event* evt = g_handle_table.Get(handle).get(); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); - if (!evt->permanent_locked) { - evt->locked = true; - } + evt->locked = true; + return RESULT_SUCCESS; } @@ -123,7 +103,6 @@ Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE); evt->locked = true; - evt->permanent_locked = false; evt->reset_type = evt->intitial_reset_type = reset_type; evt->name = name; diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index da793df1a9..b1b9d4b7b3 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -18,13 +18,6 @@ namespace Kernel { */ ResultCode SetEventLocked(const Handle handle, const bool locked); -/** - * Hackish function to set an events permanent lock state, used to pass through synch blocks - * @param handle Handle to event to change - * @param permanent_locked Boolean permanent locked value to set event - */ -ResultCode SetPermanentLock(Handle handle, const bool permanent_locked); - /** * Signals an event * @param handle Handle to event to signal From 1f7a04f05a488b7d457d356f9bf2dda296cd6b92 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 Jan 2015 23:41:33 -0500 Subject: [PATCH 04/27] Thread: Keep track of multiple wait objects. --- src/core/hle/kernel/address_arbiter.cpp | 5 ++-- src/core/hle/kernel/thread.cpp | 35 +++++++++++++++++-------- src/core/hle/kernel/thread.h | 6 ++--- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index b7434aaf29..ff1472066a 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -15,7 +15,7 @@ namespace Kernel { -class AddressArbiter : public Object { +class AddressArbiter : public WaitObject { public: std::string GetTypeName() const override { return "Arbiter"; } std::string GetName() const override { return name; } @@ -30,7 +30,8 @@ public: /// Arbitrate an address ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value, u64 nanoseconds) { - Object* object = Kernel::g_handle_table.GetGeneric(handle).get(); + WaitObject* object = static_cast(Kernel::g_handle_table.GetGeneric(handle).get()); + if (object == nullptr) return InvalidHandle(ErrorModule::Kernel); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 8456727021..00b72477ef 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -65,7 +65,7 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { t->current_priority = t->initial_priority; } t->wait_type = WAITTYPE_NONE; - t->wait_object = nullptr; + t->wait_objects.clear(); t->wait_address = 0; } @@ -92,7 +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) { - return CheckWaitType(thread, type) && wait_object == thread->wait_object; + auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); + if (itr == thread->wait_objects.end()) { + return false; + } + return CheckWaitType(thread, type); } /// Check if a thread is blocking on a specified wait type with a specified handle and address @@ -111,7 +115,7 @@ void Thread::Stop(const char* reason) { // Stopped threads are never waiting. wait_type = WAITTYPE_NONE; - wait_object = nullptr; + wait_objects.clear(); wait_address = 0; } @@ -216,14 +220,18 @@ static Thread* NextThread() { return next; } -void WaitCurrentThread(WaitType wait_type, Object* wait_object) { +void WaitCurrentThread(WaitType wait_type, WaitObject* wait_object) { Thread* thread = GetCurrentThread(); thread->wait_type = wait_type; - thread->wait_object = wait_object; + + 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, Object* wait_object, VAddr wait_address) { +void WaitCurrentThread(WaitType wait_type, WaitObject* wait_object, VAddr wait_address) { WaitCurrentThread(wait_type, wait_object); GetCurrentThread()->wait_address = wait_address; } @@ -260,7 +268,13 @@ void Thread::ResumeFromWait() { CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); status &= ~THREADSTATUS_WAIT; - wait_object = nullptr; + + // Remove this thread from all other WaitObjects + for (auto wait_object : wait_objects) + wait_object->RemoveWaitingThread(this); + + wait_objects.clear(); + wait_type = WAITTYPE_NONE; if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { ChangeReadyState(this, true); @@ -328,7 +342,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_object = nullptr; + thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); @@ -412,9 +426,8 @@ void Reschedule() { LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); for (auto& thread : thread_list) { - LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", - thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, - (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE)); + LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X", + thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type); } } } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index daaeb26a40..9ec96c18c3 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -96,7 +96,7 @@ public: s32 processor_id; WaitType wait_type; - Object* wait_object; + std::vector> wait_objects; VAddr wait_address; std::string name; @@ -128,7 +128,7 @@ Thread* GetCurrentThread(); * @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, Object* wait_object = GetCurrentThread()); +void WaitCurrentThread(WaitType wait_type, WaitObject* wait_object = GetCurrentThread()); /** * Schedules an event to wake up the specified thread after the specified delay. @@ -143,7 +143,7 @@ void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); * @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, Object* wait_object, VAddr wait_address); +void WaitCurrentThread(WaitType wait_type, WaitObject* wait_object, VAddr wait_address); From e4a5d8ad4f708c9674c9865eb872e3c081d9a8c8 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 15 Jan 2015 19:29:46 -0500 Subject: [PATCH 05/27] Event: Fixed some bugs and cleanup (Subv) --- src/core/hle/kernel/event.cpp | 58 +++++++--------------------------- src/core/hle/kernel/event.h | 9 ++---- src/core/hle/service/apt_u.cpp | 4 +-- src/core/hle/service/srv.cpp | 2 +- 4 files changed, 16 insertions(+), 57 deletions(-) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 9dd3d0f5df..540199e031 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -25,66 +25,36 @@ public: ResetType intitial_reset_type; ///< ResetType specified at Event initialization ResetType reset_type; ///< Current ResetType - bool locked; ///< Event signal wait + bool signaled; ///< Whether the event has already been signaled std::string name; ///< Name of event (optional) ResultVal WaitSynchronization() override { - bool wait = locked; - if (locked) { + bool wait = !signaled; + if (wait) { AddWaitingThread(GetCurrentThread()); Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); } - if (reset_type != RESETTYPE_STICKY) { - locked = true; - } return MakeResult(wait); } }; -/** - * Changes whether an event is locked or not - * @param handle Handle to event to change - * @param locked Boolean locked value to set event - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode SetEventLocked(const Handle handle, const bool locked) { - Event* evt = g_handle_table.Get(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); - - evt->locked = locked; - - return RESULT_SUCCESS; -} - -/** - * Signals an event - * @param handle Handle to event to signal - * @return Result of operation, 0 on success, otherwise error code - */ ResultCode SignalEvent(const Handle handle) { Event* evt = g_handle_table.Get(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); + if (evt == nullptr) + return InvalidHandle(ErrorModule::Kernel); - // If any thread is signalled awake by this event, assume the event was "caught" and reset - // the event. This will result in the next thread waiting on the event to block. Otherwise, - // the event will not be reset, and the next thread to call WaitSynchronization on it will - // not block. Not sure if this is correct behavior, but it seems to work. - // TODO(bunnei): Test how this works on hardware - evt->locked = evt->ResumeAllWaitingThreads(); + evt->signaled = true; + evt->ReleaseAllWaitingThreads(); return RESULT_SUCCESS; } -/** - * Clears an event - * @param handle Handle to event to clear - * @return Result of operation, 0 on success, otherwise error code - */ ResultCode ClearEvent(Handle handle) { Event* evt = g_handle_table.Get(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); + if (evt == nullptr) + return InvalidHandle(ErrorModule::Kernel); - evt->locked = true; + evt->signaled = false; return RESULT_SUCCESS; } @@ -102,19 +72,13 @@ Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string // TOOD(yuriks): Fix error reporting handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE); - evt->locked = true; + evt->signaled = false; evt->reset_type = evt->intitial_reset_type = reset_type; evt->name = name; return evt; } -/** - * Creates an event - * @param reset_type ResetType describing how to create event - * @param name Optional name of event - * @return Handle to newly created Event object - */ Handle CreateEvent(const ResetType reset_type, const std::string& name) { Handle handle; Event* evt = CreateEvent(handle, reset_type, name); diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index b1b9d4b7b3..c08b12ee16 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -11,22 +11,17 @@ namespace Kernel { -/** - * Changes whether an event is locked or not - * @param handle Handle to event to change - * @param locked Boolean locked value to set event - */ -ResultCode SetEventLocked(const Handle handle, const bool locked); - /** * Signals an event * @param handle Handle to event to signal + * @return Result of operation, 0 on success, otherwise error code */ ResultCode SignalEvent(const Handle handle); /** * Clears an event * @param handle Handle to event to clear + * @return Result of operation, 0 on success, otherwise error code */ ResultCode ClearEvent(Handle handle); diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp index 69a7bcf92e..d318de3d2d 100644 --- a/src/core/hle/service/apt_u.cpp +++ b/src/core/hle/service/apt_u.cpp @@ -50,8 +50,8 @@ void Initialize(Service::Interface* self) { cmd_buff[3] = notification_event_handle; cmd_buff[4] = pause_event_handle; - Kernel::SetEventLocked(notification_event_handle, true); - Kernel::SetEventLocked(pause_event_handle, false); // Fire start event + Kernel::ClearEvent(notification_event_handle); + Kernel::SignalEvent(pause_event_handle); // Fire start event _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock"); Kernel::ReleaseMutex(lock_handle); diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index ac5f30a282..082834cfe1 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -24,7 +24,7 @@ static void GetProcSemaphore(Service::Interface* self) { // TODO(bunnei): Change to a semaphore once these have been implemented g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); - Kernel::SetEventLocked(g_event_handle, false); + Kernel::ClearEvent(g_event_handle); cmd_buff[1] = 0; // No error cmd_buff[3] = g_event_handle; From 7faf2d8e06e705d1866fa0d7848ff43541a4b172 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 Jan 2015 02:03:44 -0500 Subject: [PATCH 06/27] 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 ff1472066a..520601455b 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 540199e031..4173a980bd 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 1dba85939e..be3495412f 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 53b3f91438..af4e2f4439 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 35d8296062..78063b8f18 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 af2c465e49..288928441e 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 00b72477ef..0c9ecc091a 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; - - 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, 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> 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 9ec96c18c3..f3dc4eec05 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 2d4fa4c011..c97ae6c5cc 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 a487f757c6..170ac87f3e 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 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; + } + + // 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; } From 064be2b86f166d40e0ba61bc4ec0306631b4be9f Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 Jan 2015 12:17:36 -0500 Subject: [PATCH 07/27] WaitSynchronizationN: Handle case where handle_count=0. --- src/core/hle/svc.cpp | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 170ac87f3e..f8a5b25483 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -148,27 +148,37 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all_succeeded = false; int handle_index = 0; - while (handle_index < handle_count) { - SharedPtr object = Kernel::g_handle_table.GetGeneric(handles[handle_index]); - if (object == nullptr) - return InvalidHandle(ErrorModule::Kernel).raw; + // If handles were passed in, iterate through them and wait/acquire the objects as needed + if (handle_count > 0) { + while (handle_index < handle_count) { + SharedPtr object = Kernel::g_handle_table.GetGeneric(handles[handle_index]); + if (object == nullptr) + return InvalidHandle(ErrorModule::Kernel).raw; - ResultVal wait = object->WaitSynchronization(handle_index); + ResultVal wait = object->WaitSynchronization(handle_index); - wait_thread = (wait.Succeeded() && *wait); + wait_thread = (wait.Succeeded() && *wait); - // 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 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++; + } + }else { + // If no handles were passed in, put the thread to sleep only when wait_all=false + // NOTE: This is supposed to deadlock if no timeout was specified + if (!wait_all) { + wait_thread = true; + Kernel::WaitCurrentThread(WAITTYPE_SLEEP); } - - // 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... From f5c6d367c9634291aeea7604c2a14a80144f7dc0 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 Jan 2015 12:35:55 -0500 Subject: [PATCH 08/27] WaitSynchronizationN: Handle case where handle_count is invalid. --- src/core/hle/svc.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index f8a5b25483..637c63b91f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -148,8 +148,12 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all_succeeded = false; int handle_index = 0; - // If handles were passed in, iterate through them and wait/acquire the objects as needed - if (handle_count > 0) { + // Negative handle_count is invalid + if (handle_count < 0) + return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw; + + // If handle_count is non-zero, iterate through them and wait/acquire the objects as needed + if (handle_count != 0) { while (handle_index < handle_count) { SharedPtr object = Kernel::g_handle_table.GetGeneric(handles[handle_index]); if (object == nullptr) @@ -172,7 +176,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, handle_index++; } - }else { + } else { // If no handles were passed in, put the thread to sleep only when wait_all=false // NOTE: This is supposed to deadlock if no timeout was specified if (!wait_all) { From 627e96fc15f99eea0f1c5ccdb46d85cadb3efd69 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 Jan 2015 14:04:54 -0500 Subject: [PATCH 09/27] WaitSynchronizationN: Handle case where handles=nullptr. --- src/core/hle/svc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 637c63b91f..23885f1291 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -148,6 +148,10 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all_succeeded = false; int handle_index = 0; + // Handles pointer is invalid + if (handles == nullptr) + return ResultCode(ErrorDescription::InvalidPointer, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent).raw; + // Negative handle_count is invalid if (handle_count < 0) return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw; From aa01c57ae9d73e41b65d37860ca6fbb91caba33a Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 Jan 2015 22:23:49 -0500 Subject: [PATCH 10/27] Kernel: Separate WaitSynchronization into Wait and Acquire methods. --- src/core/hle/kernel/event.cpp | 6 +++++- src/core/hle/kernel/kernel.h | 13 +++++++++++-- src/core/hle/kernel/mutex.cpp | 19 ++++++++++++++----- src/core/hle/kernel/semaphore.cpp | 15 ++++++++++++--- src/core/hle/kernel/thread.cpp | 6 +++++- src/core/hle/kernel/thread.h | 3 ++- src/core/hle/kernel/timer.cpp | 6 +++++- src/core/hle/svc.cpp | 9 +++++---- 8 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 4173a980bd..41e1bd6c52 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -28,7 +28,7 @@ public: bool signaled; ///< Whether the event has already been signaled std::string name; ///< Name of event (optional) - ResultVal WaitSynchronization(unsigned index) override { + ResultVal Wait(unsigned index) override { bool wait = !signaled; if (wait) { AddWaitingThread(GetCurrentThread()); @@ -36,6 +36,10 @@ public: } return MakeResult(wait); } + + ResultVal Acquire() override { + return MakeResult(true); + } }; ResultCode SignalEvent(const Handle handle) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index af4e2f4439..d98fd0389e 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -65,11 +65,20 @@ public: virtual Kernel::HandleType GetHandleType() const = 0; /** - * Wait for kernel object to synchronize. + * Wait the current thread 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(unsigned index=0) { + virtual ResultVal Wait(unsigned index = 0) { + LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); + return UnimplementedFunction(ErrorModule::Kernel); + } + + /** + * Acquire/lock the kernel object if it is available + * @return True if we were able to acquire the kernel object, otherwise false + */ + virtual ResultVal Acquire() { LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); return UnimplementedFunction(ErrorModule::Kernel); } diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 78063b8f18..37e7be4e77 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -26,7 +26,8 @@ public: Handle lock_thread; ///< Handle to thread that currently has mutex std::string name; ///< Name of mutex (optional) - ResultVal WaitSynchronization(unsigned index) override; + ResultVal Wait(unsigned index) override; + ResultVal Acquire() override; }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -155,17 +156,25 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { return handle; } -ResultVal Mutex::WaitSynchronization(unsigned index) { - bool wait = locked; +ResultVal Mutex::Wait(unsigned index) { if (locked) { AddWaitingThread(GetCurrentThread()); Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_MUTEX, this, index); - } else { + } + + return MakeResult(locked); +} + +ResultVal Mutex::Acquire() { + bool res = false; + + if (!locked) { // Lock the mutex when the first thread accesses it locked = true; + res = true; MutexAcquireLock(this); } - return MakeResult(wait); + return MakeResult(res); } } // namespace diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 288928441e..6464b25809 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -32,18 +32,27 @@ public: return available_count > 0; } - ResultVal WaitSynchronization(unsigned index) override { + ResultVal Wait(unsigned index) override { bool wait = !IsAvailable(); if (wait) { Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_SEMA, this, index); AddWaitingThread(GetCurrentThread()); - } else { - --available_count; } return MakeResult(wait); } + + ResultVal Acquire() override { + bool res = false; + + if (IsAvailable()) { + --available_count; + res = true; + } + + return MakeResult(res); + } }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 0c9ecc091a..6b0bdebb53 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -22,7 +22,7 @@ namespace Kernel { -ResultVal Thread::WaitSynchronization(unsigned index) { +ResultVal Thread::Wait(unsigned index) { const bool wait = status != THREADSTATUS_DORMANT; if (wait) { AddWaitingThread(GetCurrentThread()); @@ -32,6 +32,10 @@ ResultVal Thread::WaitSynchronization(unsigned index) { return MakeResult(wait); } +ResultVal Thread::Acquire() { + return MakeResult(true); +} + // Lists all thread ids that aren't deleted/etc. static std::vector> thread_list; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f3dc4eec05..9faf89c15e 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -70,7 +70,8 @@ public: inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } inline bool IsIdle() const { return idle; } - ResultVal WaitSynchronization(unsigned index) override; + ResultVal Wait(unsigned index) override; + ResultVal Acquire() override; s32 GetPriority() const { return current_priority; } void SetPriority(s32 priority); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index c97ae6c5cc..6497bb3493 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -29,7 +29,7 @@ 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(unsigned index) override { + ResultVal Wait(unsigned index) override { bool wait = !signaled; if (wait) { AddWaitingThread(GetCurrentThread()); @@ -37,6 +37,10 @@ public: } return MakeResult(wait); } + + ResultVal Acquire() override { + return MakeResult(true); + } }; /** diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 23885f1291..a27aa6269a 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -127,7 +127,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); - ResultVal wait = object->WaitSynchronization(); + ResultVal wait = object->Wait(); // Check for next thread to schedule if (wait.Succeeded() && *wait) { @@ -137,6 +137,8 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { Kernel::GetCurrentThread()->SetWaitAll(false); HLE::Reschedule(__func__); + } else { + object->Acquire(); } return wait.Code().raw; @@ -163,15 +165,14 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; - ResultVal wait = object->WaitSynchronization(handle_index); + ResultVal wait = object->Wait(handle_index); wait_thread = (wait.Succeeded() && *wait); // If this object waited and we are waiting on all objects to synchronize - if (wait_thread && wait_all) { + 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) From 6643673f28b9273149fc945849a13ed832e9ef33 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Jan 2015 01:27:46 -0500 Subject: [PATCH 11/27] WaitSynchronizationN: Refactor to fix several bugs - Separate wait checking from waiting the current thread - Resume thread when wait_all=true only if all objects are available at once - Set output to correct wait object index when there are duplicate handles --- src/core/hle/kernel/event.cpp | 6 +-- src/core/hle/kernel/kernel.h | 12 +++--- src/core/hle/kernel/mutex.cpp | 8 ++-- src/core/hle/kernel/semaphore.cpp | 6 +-- src/core/hle/kernel/thread.cpp | 61 ++++++++++++++----------------- src/core/hle/kernel/thread.h | 4 +- src/core/hle/kernel/timer.cpp | 6 +-- src/core/hle/svc.cpp | 50 +++++++++++++------------ 8 files changed, 75 insertions(+), 78 deletions(-) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 41e1bd6c52..ae9b06b842 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 Wait(unsigned index) override { + ResultVal Wait(bool wait_thread) override { bool wait = !signaled; - if (wait) { + if (wait && wait_thread) { AddWaitingThread(GetCurrentThread()); - Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_EVENT, this, index); + Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_EVENT, this); } return MakeResult(wait); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index d98fd0389e..cfaf0c901b 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -65,18 +65,18 @@ public: virtual Kernel::HandleType GetHandleType() const = 0; /** - * Wait the current thread 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 + * Check if this object is available, (optionally) wait the current thread if not + * @param wait_thread If true, wait the current thread if this object is unavailable + * @return True if the current thread should wait due to this object being unavailable */ - virtual ResultVal Wait(unsigned index = 0) { + virtual ResultVal Wait(bool wait_thread) { LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); return UnimplementedFunction(ErrorModule::Kernel); } /** - * Acquire/lock the kernel object if it is available - * @return True if we were able to acquire the kernel object, otherwise false + * Acquire/lock the this object if it is available + * @return True if we were able to acquire this object, otherwise false */ virtual ResultVal Acquire() { LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 37e7be4e77..f97c69a788 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 Wait(unsigned index) override; + ResultVal Wait(bool wait_thread) override; ResultVal Acquire() override; }; @@ -156,10 +156,10 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { return handle; } -ResultVal Mutex::Wait(unsigned index) { - if (locked) { +ResultVal Mutex::Wait(bool wait_thread) { + if (locked && wait_thread) { AddWaitingThread(GetCurrentThread()); - Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_MUTEX, this, index); + Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_MUTEX, this); } return MakeResult(locked); diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 6464b25809..42b5cf704c 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 Wait(unsigned index) override { + ResultVal Wait(bool wait_thread) override { bool wait = !IsAvailable(); - if (wait) { - Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_SEMA, this, index); + if (wait && wait_thread) { + Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_SEMA, this); AddWaitingThread(GetCurrentThread()); } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 6b0bdebb53..62b85f56a5 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -22,11 +22,11 @@ namespace Kernel { -ResultVal Thread::Wait(unsigned index) { +ResultVal Thread::Wait(bool wait_thread) { const bool wait = status != THREADSTATUS_DORMANT; - if (wait) { + if (wait && wait_thread) { AddWaitingThread(GetCurrentThread()); - WaitCurrentThread_WaitSynchronization(WAITTYPE_THREADEND, this, index); + WaitCurrentThread_WaitSynchronization(WAITTYPE_THREADEND, this); } return MakeResult(wait); @@ -97,7 +97,7 @@ 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) { for (auto itr = thread->wait_objects.begin(); itr != thread->wait_objects.end(); ++itr) { - if (itr->first == wait_object) + if (*itr == wait_object) return CheckWaitType(thread, type); } return false; @@ -234,16 +234,7 @@ void WaitCurrentThread_WaitSynchronization(WaitType wait_type, WaitObject* wait_ 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, unsigned>(wait_object, index)); + thread->wait_objects.push_back(wait_object); ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } @@ -288,31 +279,35 @@ void Thread::ReleaseFromWait(WaitObject* wait_object) { return; } - // Remove this thread from the wait_object + // Remove this thread from the waiting object's thread list 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 = 0; + bool wait_all_failed = false; // Will be set to true if any object is unavailable + + // Iterate through all waiting objects to check availability... + for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) { + auto res = (*itr)->Wait(false); + + if (*res && res.Succeeded()) + wait_all_failed = true; + + // The output should be the last index of wait_object + if (*itr == wait_object) + index = itr - wait_objects.begin(); } - 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()) { + // If we are waiting on all objects... + if (wait_all) { + // Resume the thread only if all are available... + if (!wait_all_failed) { SetReturnValue(RESULT_SUCCESS, -1); ResumeFromWait(); } + } else { + // Otherwise, resume + SetReturnValue(RESULT_SUCCESS, index); + ResumeFromWait(); } } @@ -324,7 +319,7 @@ void Thread::ResumeFromWait() { // Remove this thread from all other WaitObjects for (auto wait_object : wait_objects) - wait_object.first->RemoveWaitingThread(this); + wait_object->RemoveWaitingThread(this); wait_objects.clear(); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 9faf89c15e..dff6bbaec0 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 Wait(unsigned index) override; + ResultVal Wait(bool wait_thread) override; ResultVal Acquire() override; s32 GetPriority() const { return current_priority; } @@ -117,7 +117,7 @@ public: s32 processor_id; WaitType wait_type; - std::vector, unsigned>> wait_objects; + std::vector> wait_objects; VAddr wait_address; std::string name; diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 6497bb3493..9f0fbafe24 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 Wait(unsigned index) override { + ResultVal Wait(bool wait_thread) override { bool wait = !signaled; - if (wait) { + if (wait && wait_thread) { AddWaitingThread(GetCurrentThread()); - Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_TIMER, this, index); + Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_TIMER, this); } return MakeResult(wait); } diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index a27aa6269a..059451100e 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -127,7 +127,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); - ResultVal wait = object->Wait(); + ResultVal wait = object->Wait(true); // Check for next thread to schedule if (wait.Succeeded() && *wait) { @@ -146,8 +146,7 @@ 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) { - bool wait_thread = false; - bool wait_all_succeeded = false; + bool wait_thread = !wait_all; int handle_index = 0; // Handles pointer is invalid @@ -158,40 +157,43 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, if (handle_count < 0) return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw; - // If handle_count is non-zero, iterate through them and wait/acquire the objects as needed + // If handle_count is non-zero, iterate through them and wait the current thread on the objects if (handle_count != 0) { - while (handle_index < handle_count) { - SharedPtr object = Kernel::g_handle_table.GetGeneric(handles[handle_index]); + bool selected = false; // True once an object has been selected + for (int i = 0; i < handle_count; ++i) { + SharedPtr object = Kernel::g_handle_table.GetGeneric(handles[i]); if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; - ResultVal wait = object->Wait(handle_index); + ResultVal wait = object->Wait(true); - wait_thread = (wait.Succeeded() && *wait); - - // 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++; + // Check if the current thread should wait on the object... + if (wait.Succeeded() && *wait) { + // Check we are waiting on all objects... + if (wait_all) + // Wait the thread + wait_thread = true; + } else { + // Do not wait on this object, check if this object should be selected... + if (!wait_all && !selected) { + // Do not wait the thread + wait_thread = false; + handle_index = i; + selected = true; + } + } } } else { // If no handles were passed in, put the thread to sleep only when wait_all=false - // NOTE: This is supposed to deadlock if no timeout was specified + // NOTE: This is supposed to deadlock the current thread if no timeout was specified if (!wait_all) { wait_thread = true; Kernel::WaitCurrentThread(WAITTYPE_SLEEP); } } - // Change the thread state to waiting if blocking on all handles... - if (wait_thread || wait_all_succeeded) { + // If thread should block, then set its state to waiting and then reschedule... + if (wait_thread) { // 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); @@ -199,7 +201,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, HLE::Reschedule(__func__); // NOTE: output of this SVC will be set later depending on how the thread resumes - return RESULT_DUMMY.raw; + return 0xDEADBEEF; } // Acquire objects if we did not wait... From 6deb1a0119eb4f17490ed2603d5a608bcf71413a Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Jan 2015 02:16:54 -0500 Subject: [PATCH 12/27] WaitSynchronizationN: Improved comments --- src/core/hle/svc.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 059451100e..01b536084d 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -149,15 +149,20 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_thread = !wait_all; int handle_index = 0; - // Handles pointer is invalid + // Check if 'handles' is invalid if (handles == nullptr) return ResultCode(ErrorDescription::InvalidPointer, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent).raw; - // Negative handle_count is invalid + // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If + // this happens, the running application will crash. + _assert_msg_(Kernel, out != nullptr, "invalid output pointer specified!"); + + // Check if 'handle_count' is invalid if (handle_count < 0) return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw; - // If handle_count is non-zero, iterate through them and wait the current thread on the objects + // If 'handle_count' is non-zero, iterate through each handle and wait the current thread if + // necessary if (handle_count != 0) { bool selected = false; // True once an object has been selected for (int i = 0; i < handle_count; ++i) { @@ -167,7 +172,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, ResultVal wait = object->Wait(true); - // Check if the current thread should wait on the object... + // Check if the current thread should wait on this object... if (wait.Succeeded() && *wait) { // Check we are waiting on all objects... if (wait_all) @@ -184,15 +189,15 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, } } } else { - // If no handles were passed in, put the thread to sleep only when wait_all=false - // NOTE: This is supposed to deadlock the current thread if no timeout was specified + // If no handles were passed in, put the thread to sleep only when 'wait_all' is false + // NOTE: This should deadlock the current thread if no timeout was specified if (!wait_all) { wait_thread = true; Kernel::WaitCurrentThread(WAITTYPE_SLEEP); } } - // If thread should block, then set its state to waiting and then reschedule... + // If thread should wait, then set its state to waiting and then reschedule... if (wait_thread) { // Create an event to wake the thread up after the specified nanosecond delay has passed Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); From e5a9f1c64483e01b7856c581ae5685d0c5ad88dc Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Jan 2015 13:25:51 -0500 Subject: [PATCH 13/27] Kernel: Get rid of WaitTypes and simplify lots of code, removing hacks. --- src/core/hle/kernel/event.cpp | 9 +---- src/core/hle/kernel/kernel.cpp | 6 ++- src/core/hle/kernel/kernel.h | 5 +-- src/core/hle/kernel/mutex.cpp | 9 +---- src/core/hle/kernel/semaphore.cpp | 11 +----- src/core/hle/kernel/thread.cpp | 65 +++++++++---------------------- src/core/hle/kernel/thread.h | 41 +++++-------------- src/core/hle/kernel/timer.cpp | 9 +---- src/core/hle/svc.cpp | 30 +++++++++----- 9 files changed, 63 insertions(+), 122 deletions(-) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index ae9b06b842..399730cb19 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -28,13 +28,8 @@ public: bool signaled; ///< Whether the event has already been signaled std::string name; ///< Name of event (optional) - ResultVal Wait(bool wait_thread) override { - bool wait = !signaled; - if (wait && wait_thread) { - AddWaitingThread(GetCurrentThread()); - Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_EVENT, this); - } - return MakeResult(wait); + ResultVal Wait() override { + return MakeResult(!signaled); } ResultVal Acquire() override { diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index be3495412f..57e0e8df7c 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -42,13 +42,15 @@ Thread* WaitObject::ReleaseNextThread() { return next_thread.get(); } -void WaitObject::ReleaseAllWaitingThreads() { +void WaitObject::WakeupAllWaitingThreads() { auto waiting_threads_copy = waiting_threads; + // We use a copy because ReleaseWaitObject will remove the thread from this object's + // waiting_threads list for (auto thread : waiting_threads_copy) thread->ReleaseWaitObject(this); - waiting_threads.clear(); + _assert_msg_(Kernel, waiting_threads.empty(), "failed to awaken all waiting threads!"); } HandleTable::HandleTable() { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index cfaf0c901b..5bf9a2bfc8 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -65,11 +65,10 @@ public: virtual Kernel::HandleType GetHandleType() const = 0; /** - * Check if this object is available, (optionally) wait the current thread if not - * @param wait_thread If true, wait the current thread if this object is unavailable + * Check if this object is available * @return True if the current thread should wait due to this object being unavailable */ - virtual ResultVal Wait(bool wait_thread) { + virtual ResultVal Wait() { LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); return UnimplementedFunction(ErrorModule::Kernel); } diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index f97c69a788..4a1eaca37c 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 Wait(bool wait_thread) override; + ResultVal Wait() override; ResultVal Acquire() override; }; @@ -156,12 +156,7 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { return handle; } -ResultVal Mutex::Wait(bool wait_thread) { - if (locked && wait_thread) { - AddWaitingThread(GetCurrentThread()); - Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_MUTEX, this); - } - +ResultVal Mutex::Wait() { return MakeResult(locked); } diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 42b5cf704c..6ccdb2a8ff 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -32,15 +32,8 @@ public: return available_count > 0; } - ResultVal Wait(bool wait_thread) override { - bool wait = !IsAvailable(); - - if (wait && wait_thread) { - Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_SEMA, this); - AddWaitingThread(GetCurrentThread()); - } - - return MakeResult(wait); + ResultVal Wait() override { + return MakeResult(!IsAvailable()); } ResultVal Acquire() override { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 62b85f56a5..601e0eb205 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -22,14 +22,8 @@ namespace Kernel { -ResultVal Thread::Wait(bool wait_thread) { - const bool wait = status != THREADSTATUS_DORMANT; - if (wait && wait_thread) { - AddWaitingThread(GetCurrentThread()); - WaitCurrentThread_WaitSynchronization(WAITTYPE_THREADEND, this); - } - - return MakeResult(wait); +ResultVal Thread::Wait() { + return MakeResult(status != THREADSTATUS_DORMANT); } ResultVal Thread::Acquire() { @@ -68,7 +62,7 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { if (t->current_priority < lowest_priority) { t->current_priority = t->initial_priority; } - t->wait_type = WAITTYPE_NONE; + t->wait_objects.clear(); t->wait_address = 0; } @@ -89,23 +83,18 @@ static void ChangeReadyState(Thread* t, bool ready) { } } -/// Check if a thread is blocking on a specified wait type -static bool CheckWaitType(const Thread* thread, WaitType type) { - return (type == thread->wait_type) && (thread->IsWaiting()); -} - -/// 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) { +/// Check if a thread is blocking on a the specified object +static bool CheckWaitType(const Thread* thread, Object* wait_object) { for (auto itr = thread->wait_objects.begin(); itr != thread->wait_objects.end(); ++itr) { if (*itr == wait_object) - return CheckWaitType(thread, type); + return (thread->IsWaiting()); } return false; } -/// Check if a thread is blocking on a specified wait type with a specified handle and address -static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) { - return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address); +/// Check if a thread is blocking on a the specified object and an address +static bool CheckWaitType(const Thread* thread, Object* wait_object, VAddr wait_address) { + return CheckWaitType(thread, wait_object) && (wait_address == thread->wait_address); } /// Stops the current thread @@ -118,7 +107,6 @@ void Thread::Stop(const char* reason) { ReleaseAllWaitingThreads(); // Stopped threads are never waiting. - wait_type = WAITTYPE_NONE; wait_objects.clear(); wait_address = 0; } @@ -130,12 +118,6 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) { } ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); t->status = new_status; - - if (new_status == THREADSTATUS_WAIT) { - if (t->wait_type == WAITTYPE_NONE) { - LOG_ERROR(Kernel, "Waittype none not allowed"); - } - } } /// Arbitrate the highest priority thread that is waiting @@ -145,7 +127,7 @@ Thread* ArbitrateHighestPriorityThread(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)) + if (!CheckWaitType(thread.get(), arbiter, address)) continue; if (thread == nullptr) @@ -170,7 +152,7 @@ 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)) + if (CheckWaitType(thread.get(), arbiter, address)) thread->ReleaseFromWait(arbiter); } } @@ -178,9 +160,6 @@ void ArbitrateAllThreads(WaitObject* arbiter, u32 address) { /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) static void CallThread(Thread* t) { // Stop waiting - if (t->wait_type != WAITTYPE_NONE) { - t->wait_type = WAITTYPE_NONE; - } ChangeThreadState(t, THREADSTATUS_READY); } @@ -201,7 +180,6 @@ static void SwitchContext(Thread* t) { current_thread = t; ChangeReadyState(t, false); t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; - t->wait_type = WAITTYPE_NONE; Core::g_app_core->LoadContext(t->context); } else { current_thread = nullptr; @@ -224,23 +202,20 @@ static Thread* NextThread() { return next; } -void WaitCurrentThread(WaitType wait_type) { +void WaitCurrentThread() { 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) { +void WaitCurrentThread_WaitSynchronization(WaitObject* wait_object, bool wait_all) { Thread* thread = GetCurrentThread(); - thread->wait_type = wait_type; - + thread->wait_all = wait_all; thread->wait_objects.push_back(wait_object); - 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); + WaitCurrentThread_WaitSynchronization(wait_object); GetCurrentThread()->wait_address = wait_address; } @@ -287,7 +262,7 @@ void Thread::ReleaseFromWait(WaitObject* wait_object) { // Iterate through all waiting objects to check availability... for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) { - auto res = (*itr)->Wait(false); + auto res = (*itr)->Wait(); if (*res && res.Succeeded()) wait_all_failed = true; @@ -322,9 +297,8 @@ void Thread::ResumeFromWait() { wait_object->RemoveWaitingThread(this); wait_objects.clear(); - - wait_type = WAITTYPE_NONE; wait_all = false; + if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { ChangeReadyState(this, true); } @@ -390,7 +364,6 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, thread->stack_size = stack_size; 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; @@ -476,8 +449,8 @@ void Reschedule() { LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); for (auto& thread : thread_list) { - LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X", - thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type); + LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X", thread->GetHandle(), + thread->current_priority, thread->status); } } } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index dff6bbaec0..cb48fcadc5 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -38,18 +38,6 @@ enum ThreadStatus { THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND }; -enum WaitType { - WAITTYPE_NONE, - WAITTYPE_SLEEP, - WAITTYPE_SEMA, - WAITTYPE_EVENT, - WAITTYPE_THREADEND, - WAITTYPE_MUTEX, - WAITTYPE_SYNCH, - WAITTYPE_ARB, - WAITTYPE_TIMER, -}; - namespace Kernel { class Thread : public WaitObject { @@ -70,7 +58,7 @@ public: inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } inline bool IsIdle() const { return idle; } - ResultVal Wait(bool wait_thread) override; + ResultVal Wait() override; ResultVal Acquire() override; s32 GetPriority() const { return current_priority; } @@ -89,12 +77,6 @@ public: /// 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 @@ -116,9 +98,10 @@ public: s32 processor_id; - WaitType wait_type; - std::vector> wait_objects; - VAddr wait_address; + std::vector> wait_objects; ///< Objects that the thread is waiting on + + VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address + bool wait_all; ///< True if the thread is waiting on all objects before resuming std::string name; @@ -126,7 +109,6 @@ public: bool idle = false; private: - bool wait_all = false; Thread() = default; }; @@ -146,19 +128,15 @@ void ArbitrateAllThreads(WaitObject* arbiter, u32 address); /// Gets the current thread Thread* GetCurrentThread(); -/** - * Waits the current thread for the given type - * @param wait_type Type of wait - */ -void WaitCurrentThread(WaitType wait_type); +/// Waits the current thread +void WaitCurrentThread(); /** * 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) + * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) */ -void WaitCurrentThread_WaitSynchronization(WaitType wait_type, WaitObject* wait_object, unsigned index=0); +void WaitCurrentThread_WaitSynchronization(WaitObject* wait_object, bool wait_all=false); /** * Waits the current thread from an ArbitrateAddress call @@ -181,6 +159,7 @@ void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); * @returns The handle of the idle thread */ Handle SetupIdleThread(); + /// Initialize threading void ThreadingInit(); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 9f0fbafe24..62bdf07c74 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -29,13 +29,8 @@ 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 Wait(bool wait_thread) override { - bool wait = !signaled; - if (wait && wait_thread) { - AddWaitingThread(GetCurrentThread()); - Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_TIMER, this); - } - return MakeResult(wait); + ResultVal Wait() override { + return MakeResult(!signaled); } ResultVal Acquire() override { diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 01b536084d..2d5f41af6b 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -105,7 +105,7 @@ static Result SendSyncRequest(Handle handle) { ResultVal wait = session->SyncRequest(); if (wait.Succeeded() && *wait) { - Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? + Kernel::WaitCurrentThread(); // TODO(bunnei): Is this correct? } return wait.Code().raw; @@ -120,22 +120,24 @@ static Result CloseHandle(Handle handle) { /// Wait for a handle to synchronize, timeout after the specified nanoseconds static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { - SharedPtr object = Kernel::g_handle_table.GetGeneric(handle); + Kernel::WaitObject* object = static_cast(Kernel::g_handle_table.GetGeneric(handle).get()); if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); - ResultVal wait = object->Wait(true); + ResultVal wait = object->Wait(); // Check for next thread to schedule if (wait.Succeeded() && *wait) { + + object->AddWaitingThread(Kernel::GetCurrentThread()); + Kernel::WaitCurrentThread_WaitSynchronization(object); + // 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__); } else { object->Acquire(); @@ -166,14 +168,15 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, if (handle_count != 0) { bool selected = false; // True once an object has been selected for (int i = 0; i < handle_count; ++i) { - SharedPtr object = Kernel::g_handle_table.GetGeneric(handles[i]); + Kernel::WaitObject* object = static_cast(Kernel::g_handle_table.GetGeneric(handles[i]).get()); if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; - ResultVal wait = object->Wait(true); + ResultVal wait = object->Wait(); // Check if the current thread should wait on this object... if (wait.Succeeded() && *wait) { + // Check we are waiting on all objects... if (wait_all) // Wait the thread @@ -193,15 +196,22 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, // NOTE: This should deadlock the current thread if no timeout was specified if (!wait_all) { wait_thread = true; - Kernel::WaitCurrentThread(WAITTYPE_SLEEP); + Kernel::WaitCurrentThread(); } } // If thread should wait, then set its state to waiting and then reschedule... if (wait_thread) { + + // Actually wait the current thread on each object if we decided to wait... + for (int i = 0; i < handle_count; ++i) { + auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); + object->AddWaitingThread(Kernel::GetCurrentThread()); + Kernel::WaitCurrentThread_WaitSynchronization(object, wait_all); + } + // 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__); @@ -440,7 +450,7 @@ static void SleepThread(s64 nanoseconds) { LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); // Sleep current thread and check for next thread to schedule - Kernel::WaitCurrentThread(WAITTYPE_SLEEP); + Kernel::WaitCurrentThread(); // Create an event to wake the thread up after the specified nanosecond delay has passed Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds); From 254e4ebd58a31e8462b70799f95f096d0d0038f2 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Jan 2015 13:56:40 -0500 Subject: [PATCH 14/27] AddressArbiter: Changed to Kernel::Object, big cleanup, removed code that made no sense. --- src/core/hle/kernel/address_arbiter.cpp | 16 +++++----- src/core/hle/kernel/kernel.cpp | 2 +- src/core/hle/kernel/thread.cpp | 42 +++++++++++++++---------- src/core/hle/kernel/thread.h | 17 +++++----- src/core/hle/svc.cpp | 6 ++-- 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 520601455b..9e855b0bf2 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -15,7 +15,7 @@ namespace Kernel { -class AddressArbiter : public WaitObject { +class AddressArbiter : public Object { public: std::string GetTypeName() const override { return "Arbiter"; } std::string GetName() const override { return name; } @@ -30,7 +30,7 @@ public: /// Arbitrate an address ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value, u64 nanoseconds) { - WaitObject* object = static_cast(Kernel::g_handle_table.GetGeneric(handle).get()); + AddressArbiter* object = Kernel::g_handle_table.Get(handle).get(); if (object == nullptr) return InvalidHandle(ErrorModule::Kernel); @@ -41,24 +41,24 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 case ArbitrationType::Signal: // Negative value means resume all threads if (value < 0) { - ArbitrateAllThreads(object, address); + ArbitrateAllThreads(address); } else { // Resume first N threads for(int i = 0; i < value; i++) - ArbitrateHighestPriorityThread(object, address); + ArbitrateHighestPriorityThread(address); } break; // Wait current thread (acquire the arbiter)... case ArbitrationType::WaitIfLessThan: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread_ArbitrateAddress(object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(address); HLE::Reschedule(__func__); } break; case ArbitrationType::WaitIfLessThanWithTimeout: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread_ArbitrateAddress(object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(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_ArbitrateAddress(object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(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_ArbitrateAddress(object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(address); Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); HLE::Reschedule(__func__); } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 57e0e8df7c..b3ca78ed69 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -36,7 +36,7 @@ Thread* WaitObject::ReleaseNextThread() { auto next_thread = waiting_threads.front(); - next_thread->ReleaseFromWait(this); + next_thread->ReleaseWaitObject(this); waiting_threads.erase(waiting_threads.begin()); return next_thread.get(); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 601e0eb205..16865ccc40 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -83,8 +83,8 @@ static void ChangeReadyState(Thread* t, bool ready) { } } -/// Check if a thread is blocking on a the specified object -static bool CheckWaitType(const Thread* thread, Object* wait_object) { +/// Check if a thread is waiting on a the specified wait object +static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { for (auto itr = thread->wait_objects.begin(); itr != thread->wait_objects.end(); ++itr) { if (*itr == wait_object) return (thread->IsWaiting()); @@ -92,9 +92,9 @@ static bool CheckWaitType(const Thread* thread, Object* wait_object) { return false; } -/// Check if a thread is blocking on a the specified object and an address -static bool CheckWaitType(const Thread* thread, Object* wait_object, VAddr wait_address) { - return CheckWaitType(thread, wait_object) && (wait_address == thread->wait_address); +/// Check if the specified thread is waiting on the specified address to be arbitrated +static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { + return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address; } /// Stops the current thread @@ -121,17 +121,17 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) { } /// Arbitrate the highest priority thread that is waiting -Thread* ArbitrateHighestPriorityThread(WaitObject* arbiter, u32 address) { +Thread* ArbitrateHighestPriorityThread(u32 address) { Thread* highest_priority_thread = nullptr; s32 priority = THREADPRIO_LOWEST; // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { - if (!CheckWaitType(thread.get(), arbiter, address)) + if (!CheckWait_AddressArbiter(thread.get(), address)) continue; if (thread == nullptr) - continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. + continue; if(thread->current_priority <= priority) { highest_priority_thread = thread.get(); @@ -141,19 +141,19 @@ Thread* ArbitrateHighestPriorityThread(WaitObject* arbiter, u32 address) { // If a thread was arbitrated, resume it if (nullptr != highest_priority_thread) { - highest_priority_thread->ReleaseFromWait(arbiter); + highest_priority_thread->ResumeFromWait(); } return highest_priority_thread; } /// Arbitrate all threads currently waiting -void ArbitrateAllThreads(WaitObject* arbiter, u32 address) { +void ArbitrateAllThreads(u32 address) { // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { - if (CheckWaitType(thread.get(), arbiter, address)) - thread->ReleaseFromWait(arbiter); + if (CheckWait_AddressArbiter(thread.get(), address)) + thread->ResumeFromWait(); } } @@ -202,21 +202,28 @@ static Thread* NextThread() { return next; } -void WaitCurrentThread() { +void WaitCurrentThread_Sleep() { Thread* thread = GetCurrentThread(); + thread->wait_all = false; + thread->wait_address = 0; + thread->wait_objects.clear(); ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } void WaitCurrentThread_WaitSynchronization(WaitObject* wait_object, bool wait_all) { Thread* thread = GetCurrentThread(); thread->wait_all = wait_all; + thread->wait_address = 0; thread->wait_objects.push_back(wait_object); ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } -void WaitCurrentThread_ArbitrateAddress(WaitObject* wait_object, VAddr wait_address) { - WaitCurrentThread_WaitSynchronization(wait_object); - GetCurrentThread()->wait_address = wait_address; +void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { + Thread* thread = GetCurrentThread(); + thread->wait_all = false; + thread->wait_address = wait_address; + thread->wait_objects.clear(); + ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } /// Event type for the thread wake up event @@ -248,7 +255,7 @@ void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); } -void Thread::ReleaseFromWait(WaitObject* wait_object) { +void Thread::ReleaseWaitObject(WaitObject* wait_object) { if (wait_objects.empty()) { LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); return; @@ -298,6 +305,7 @@ void Thread::ResumeFromWait() { wait_objects.clear(); wait_all = false; + wait_address = 0; if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { ChangeReadyState(this, true); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index cb48fcadc5..9907aa6e15 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -69,10 +69,10 @@ public: void Stop(const char* reason); /** - * Release an object from the thread's wait list - * @param wait_object WaitObject to release from the thread's wait list + * Release an acquired wait object + * @param wait_object WaitObject to release */ - void ReleaseFromWait(WaitObject* wait_object); + void ReleaseWaitObject(WaitObject* wait_object); /// Resumes a thread from waiting by marking it as "ready" void ResumeFromWait(); @@ -120,16 +120,16 @@ SharedPtr SetupMainThread(s32 priority, u32 stack_size); void Reschedule(); /// Arbitrate the highest priority thread that is waiting -Thread* ArbitrateHighestPriorityThread(WaitObject* arbiter, u32 address); +Thread* ArbitrateHighestPriorityThread(u32 address); /// Arbitrate all threads currently waiting... -void ArbitrateAllThreads(WaitObject* arbiter, u32 address); +void ArbitrateAllThreads(u32 address); /// Gets the current thread Thread* GetCurrentThread(); -/// Waits the current thread -void WaitCurrentThread(); +/// Waits the current thread on a sleep +void WaitCurrentThread_Sleep(); /** * Waits the current thread from a WaitSynchronization call @@ -140,10 +140,9 @@ void WaitCurrentThread_WaitSynchronization(WaitObject* wait_object, bool wait_al /** * 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_ArbitrateAddress(WaitObject* wait_object, VAddr wait_address); +void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); /** * Schedules an event to wake up the specified thread after the specified delay. diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 2d5f41af6b..cf029bf692 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -105,7 +105,7 @@ static Result SendSyncRequest(Handle handle) { ResultVal wait = session->SyncRequest(); if (wait.Succeeded() && *wait) { - Kernel::WaitCurrentThread(); // TODO(bunnei): Is this correct? + Kernel::WaitCurrentThread_Sleep(); // TODO(bunnei): Is this correct? } return wait.Code().raw; @@ -196,7 +196,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, // NOTE: This should deadlock the current thread if no timeout was specified if (!wait_all) { wait_thread = true; - Kernel::WaitCurrentThread(); + Kernel::WaitCurrentThread_Sleep(); } } @@ -450,7 +450,7 @@ static void SleepThread(s64 nanoseconds) { LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); // Sleep current thread and check for next thread to schedule - Kernel::WaitCurrentThread(); + Kernel::WaitCurrentThread_Sleep(); // Create an event to wake the thread up after the specified nanosecond delay has passed Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds); From c06d64528a666f450e42cb1792ac995fc1697e76 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Jan 2015 14:00:10 -0500 Subject: [PATCH 15/27] SVC: Removed a Sleep that made no sense - Would deadlock the calling thread - Code would never get hit anyways --- src/core/hle/svc.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index cf029bf692..5e9c38973c 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -103,12 +103,7 @@ static Result SendSyncRequest(Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); - ResultVal wait = session->SyncRequest(); - if (wait.Succeeded() && *wait) { - Kernel::WaitCurrentThread_Sleep(); // TODO(bunnei): Is this correct? - } - - return wait.Code().raw; + return session->SyncRequest().Code().raw; } /// Close a handle From 9412996c8f86f5da5a9052f7533b05e9780c4eb0 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Jan 2015 14:33:11 -0500 Subject: [PATCH 16/27] Kernel: Moved Wait and Acquire to WaitObject, added way to retrieve a WaitObject safely. --- src/core/hle/kernel/kernel.h | 71 ++++++++++++++++++++++++++-------- src/core/hle/kernel/thread.cpp | 2 +- src/core/hle/kernel/thread.h | 2 +- src/core/hle/svc.cpp | 4 +- 4 files changed, 59 insertions(+), 20 deletions(-) diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 5bf9a2bfc8..a9a893f41a 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -60,26 +60,34 @@ class Object : NonCopyable { public: virtual ~Object() {} Handle GetHandle() const { return handle; } + virtual std::string GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; } virtual std::string GetName() const { return "[UNKNOWN KERNEL OBJECT]"; } virtual Kernel::HandleType GetHandleType() const = 0; /** - * Check if this object is available - * @return True if the current thread should wait due to this object being unavailable + * Check if a thread can wait on the object + * @return True if a thread can wait on the object, otherwise false */ - virtual ResultVal Wait() { - LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::Kernel); - } + bool IsWaitable() const { + switch (GetHandleType()) { + case HandleType::Event: + case HandleType::Mutex: + case HandleType::Thread: + case HandleType::Semaphore: + case HandleType::Timer: + return true; - /** - * Acquire/lock the this object if it is available - * @return True if we were able to acquire this object, otherwise false - */ - virtual ResultVal Acquire() { - LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::Kernel); + case HandleType::Unknown: + case HandleType::Port: + case HandleType::SharedMemory: + case HandleType::Redirection: + case HandleType::Process: + case HandleType::AddressArbiter: + return false; + } + + return false; } private: @@ -107,6 +115,24 @@ using SharedPtr = boost::intrusive_ptr; class WaitObject : public Object { public: + /** + * Check if this object is available + * @return True if the current thread should wait due to this object being unavailable + */ + virtual ResultVal Wait() { + LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); + return UnimplementedFunction(ErrorModule::Kernel); + } + + /** + * Acquire/lock the this object if it is available + * @return True if we were able to acquire this object, otherwise false + */ + virtual ResultVal Acquire() { + LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); + return UnimplementedFunction(ErrorModule::Kernel); + } + /** * Add a thread to wait on this object * @param thread Pointer to thread to add @@ -186,14 +212,14 @@ public: /** * Looks up a handle. - * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid. + * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. */ SharedPtr GetGeneric(Handle handle) const; /** * Looks up a handle while verifying its type. - * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its - * type differs from the handle type `T::HANDLE_TYPE`. + * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its + * type differs from the handle type `T::HANDLE_TYPE`. */ template SharedPtr Get(Handle handle) const { @@ -204,6 +230,19 @@ public: return nullptr; } + /** + * Looks up a handle while verifying that it is an object that a thread can wait on + * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or it is + * not a waitable object. + */ + SharedPtr GetWaitObject(Handle handle) const { + SharedPtr object = GetGeneric(handle); + if (object != nullptr && object->IsWaitable()) { + return boost::static_pointer_cast(std::move(object)); + } + return nullptr; + } + /// Closes all handles held in this table. void Clear(); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 16865ccc40..271828ea7a 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -210,7 +210,7 @@ void WaitCurrentThread_Sleep() { ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } -void WaitCurrentThread_WaitSynchronization(WaitObject* wait_object, bool wait_all) { +void WaitCurrentThread_WaitSynchronization(SharedPtr wait_object, bool wait_all) { Thread* thread = GetCurrentThread(); thread->wait_all = wait_all; thread->wait_address = 0; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 9907aa6e15..a3a17e6c06 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -136,7 +136,7 @@ void WaitCurrentThread_Sleep(); * @param wait_object Kernel object that we are waiting on * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) */ -void WaitCurrentThread_WaitSynchronization(WaitObject* wait_object, bool wait_all=false); +void WaitCurrentThread_WaitSynchronization(SharedPtr wait_object, bool wait_all = false); /** * Waits the current thread from an ArbitrateAddress call diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 5e9c38973c..8df8616694 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -115,7 +115,7 @@ static Result CloseHandle(Handle handle) { /// Wait for a handle to synchronize, timeout after the specified nanoseconds static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { - Kernel::WaitObject* object = static_cast(Kernel::g_handle_table.GetGeneric(handle).get()); + auto object = Kernel::g_handle_table.GetWaitObject(handle); if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; @@ -163,7 +163,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, if (handle_count != 0) { bool selected = false; // True once an object has been selected for (int i = 0; i < handle_count; ++i) { - Kernel::WaitObject* object = static_cast(Kernel::g_handle_table.GetGeneric(handles[i]).get()); + auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; From dde02f79af45fc01d747254eefbe680e580c3d45 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Jan 2015 15:45:01 -0500 Subject: [PATCH 17/27] Mutex: Fix a bug where the thread should not wait if it already has the mutex. --- src/core/hle/kernel/mutex.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 4a1eaca37c..6cd140376d 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -25,6 +25,7 @@ public: bool locked; ///< Current locked state Handle lock_thread; ///< Handle to thread that currently has mutex std::string name; ///< Name of mutex (optional) + SharedPtr current_thread; ///< Thread that has acquired the mutex ResultVal Wait() override; ResultVal Acquire() override; @@ -43,6 +44,7 @@ static MutexMap g_mutex_held_locks; void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) { g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); mutex->lock_thread = thread; + mutex->current_thread = Kernel::g_handle_table.Get(thread); } /** @@ -132,6 +134,7 @@ Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) mutex->locked = mutex->initial_locked = initial_locked; mutex->name = name; + mutex->current_thread = nullptr; // Acquire mutex with current thread if initialized as locked... if (mutex->locked) { @@ -157,7 +160,7 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { } ResultVal Mutex::Wait() { - return MakeResult(locked); + return MakeResult(locked && (current_thread != GetCurrentThread())); } ResultVal Mutex::Acquire() { From d2759c578e8cf24277767f701d5682f7b1792a9f Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Jan 2015 18:01:58 -0500 Subject: [PATCH 18/27] Kernel: Reschedule on SignalEvent and SendSyncRequest, fix some bugs. --- src/core/hle/kernel/kernel.cpp | 2 +- src/core/hle/svc.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b3ca78ed69..6f1dced706 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -35,9 +35,9 @@ Thread* WaitObject::ReleaseNextThread() { return nullptr; auto next_thread = waiting_threads.front(); + waiting_threads.erase(waiting_threads.begin()); next_thread->ReleaseWaitObject(this); - waiting_threads.erase(waiting_threads.begin()); return next_thread.get(); } diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 8df8616694..d728391727 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -405,6 +405,7 @@ static Result DuplicateHandle(Handle* out, Handle handle) { /// Signals an event static Result SignalEvent(Handle evt) { LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt); + HLE::Reschedule(__func__); return Kernel::SignalEvent(evt).raw; } From 9e6ec3b6cd23a7cef80a1d62fda515018f080083 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Jan 2015 20:40:53 -0500 Subject: [PATCH 19/27] Session: Change to a WaitObject. --- src/core/hle/kernel/kernel.cpp | 2 +- src/core/hle/kernel/kernel.h | 1 + src/core/hle/kernel/session.h | 8 +++++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 6f1dced706..6923498578 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -39,7 +39,7 @@ Thread* WaitObject::ReleaseNextThread() { next_thread->ReleaseWaitObject(this); - return next_thread.get(); + return next_thread; } void WaitObject::WakeupAllWaitingThreads() { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index a9a893f41a..ca9ccf4bf3 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -71,6 +71,7 @@ public: */ bool IsWaitable() const { switch (GetHandleType()) { + case HandleType::Session: case HandleType::Event: case HandleType::Mutex: case HandleType::Thread: diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 91f3ffc2c3..e11f727a50 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -41,7 +41,7 @@ inline static u32* GetCommandBuffer(const int offset=0) { * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as * opposed to HLE simulations. */ -class Session : public Object { +class Session : public WaitObject { public: std::string GetTypeName() const override { return "Session"; } @@ -53,6 +53,12 @@ public: * aren't supported yet. */ virtual ResultVal SyncRequest() = 0; + + ResultVal Wait() override { + // TODO(bunnei): This function exists to satisfy a hardware test with a Session object + // passed into WaitSynchronization. Not sure if it's possible for this to ever be false? + return MakeResult(true); + } }; } From 69c5830ef2a0190803e176615d5cb16d5462b971 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 19 Jan 2015 03:30:55 -0500 Subject: [PATCH 20/27] Event: Fix implementation of "non-sticky" events. --- src/core/hle/kernel/event.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 399730cb19..37f01652e8 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -33,6 +33,10 @@ public: } ResultVal Acquire() override { + // Release the event if it's not sticky... + if (reset_type != RESETTYPE_STICKY) + signaled = false; + return MakeResult(true); } }; From c68eb1569549ae49ae25c6c29cec2e10d8329f2d Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 Jan 2015 17:41:12 -0500 Subject: [PATCH 21/27] WaitObject: Renamed "Wait" to "ShouldWait", made "ShouldWait" and "Acquire" pure virtual. --- src/core/hle/kernel/event.cpp | 2 +- src/core/hle/kernel/kernel.h | 14 ++++---------- src/core/hle/kernel/mutex.cpp | 4 ++-- src/core/hle/kernel/semaphore.cpp | 2 +- src/core/hle/kernel/session.h | 11 ++++++++--- src/core/hle/kernel/thread.cpp | 4 ++-- src/core/hle/kernel/thread.h | 2 +- src/core/hle/kernel/timer.cpp | 2 +- src/core/hle/svc.cpp | 4 ++-- 9 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 37f01652e8..bed856020e 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -28,7 +28,7 @@ public: bool signaled; ///< Whether the event has already been signaled std::string name; ///< Name of event (optional) - ResultVal Wait() override { + ResultVal ShouldWait() override { return MakeResult(!signaled); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index ca9ccf4bf3..1bb0b55bdc 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -117,22 +117,16 @@ class WaitObject : public Object { public: /** - * Check if this object is available + * Check if the current thread should wait until the object is available * @return True if the current thread should wait due to this object being unavailable */ - virtual ResultVal Wait() { - LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::Kernel); - } + virtual ResultVal ShouldWait() = 0; /** - * Acquire/lock the this object if it is available + * Acquire/lock the object if it is available * @return True if we were able to acquire this object, otherwise false */ - virtual ResultVal Acquire() { - LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::Kernel); - } + virtual ResultVal Acquire() = 0; /** * Add a thread to wait on this object diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 6cd140376d..01d2263ff1 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -27,7 +27,7 @@ public: std::string name; ///< Name of mutex (optional) SharedPtr current_thread; ///< Thread that has acquired the mutex - ResultVal Wait() override; + ResultVal ShouldWait() override; ResultVal Acquire() override; }; @@ -159,7 +159,7 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { return handle; } -ResultVal Mutex::Wait() { +ResultVal Mutex::ShouldWait() { return MakeResult(locked && (current_thread != GetCurrentThread())); } diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 6ccdb2a8ff..24d41c0b37 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -32,7 +32,7 @@ public: return available_count > 0; } - ResultVal Wait() override { + ResultVal ShouldWait() override { return MakeResult(!IsAvailable()); } diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index e11f727a50..f0343d9b24 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -54,11 +54,16 @@ public: */ virtual ResultVal SyncRequest() = 0; - ResultVal Wait() override { - // TODO(bunnei): This function exists to satisfy a hardware test with a Session object - // passed into WaitSynchronization. Not sure if it's possible for this to ever be false? + // TODO(bunnei): These functions exist to satisfy a hardware test with a Session object + // passed into WaitSynchronization. Figure out the meaning of them. + + ResultVal ShouldWait() override { return MakeResult(true); } + + ResultVal Acquire() override { + return MakeResult(false); + } }; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 271828ea7a..8a2cf8bf45 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -22,7 +22,7 @@ namespace Kernel { -ResultVal Thread::Wait() { +ResultVal Thread::ShouldWait() { return MakeResult(status != THREADSTATUS_DORMANT); } @@ -269,7 +269,7 @@ void Thread::ReleaseWaitObject(WaitObject* wait_object) { // Iterate through all waiting objects to check availability... for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) { - auto res = (*itr)->Wait(); + auto res = (*itr)->ShouldWait(); if (*res && res.Succeeded()) wait_all_failed = true; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index a3a17e6c06..b23638bd17 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -58,7 +58,7 @@ public: inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } inline bool IsIdle() const { return idle; } - ResultVal Wait() override; + ResultVal ShouldWait() override; ResultVal Acquire() override; s32 GetPriority() const { return current_priority; } diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 62bdf07c74..1729cca0f7 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -29,7 +29,7 @@ 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 Wait() override { + ResultVal ShouldWait() override { return MakeResult(!signaled); } diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index d728391727..fd2d22727f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -122,7 +122,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); - ResultVal wait = object->Wait(); + ResultVal wait = object->ShouldWait(); // Check for next thread to schedule if (wait.Succeeded() && *wait) { @@ -167,7 +167,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; - ResultVal wait = object->Wait(); + ResultVal wait = object->ShouldWait(); // Check if the current thread should wait on this object... if (wait.Succeeded() && *wait) { From 15b6a4d9add6b260a2a1a84ab6228addced4f851 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 Jan 2015 18:16:45 -0500 Subject: [PATCH 22/27] Kernel: Changed "ShouldWait" to return bool and "Acquire" to return void. --- src/core/hle/kernel/event.cpp | 10 +++++----- src/core/hle/kernel/kernel.h | 9 +++------ src/core/hle/kernel/mutex.cpp | 24 +++++++++--------------- src/core/hle/kernel/semaphore.cpp | 28 +++++++--------------------- src/core/hle/kernel/session.h | 8 ++++---- src/core/hle/kernel/thread.cpp | 12 +++++------- src/core/hle/kernel/thread.h | 4 ++-- src/core/hle/kernel/timer.cpp | 8 ++++---- src/core/hle/svc.cpp | 10 +++------- 9 files changed, 42 insertions(+), 71 deletions(-) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index bed856020e..cdacba1d9d 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -28,16 +28,16 @@ public: bool signaled; ///< Whether the event has already been signaled std::string name; ///< Name of event (optional) - ResultVal ShouldWait() override { - return MakeResult(!signaled); + bool ShouldWait() override { + return !signaled; } - ResultVal Acquire() override { + void Acquire() override { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); + // Release the event if it's not sticky... if (reset_type != RESETTYPE_STICKY) signaled = false; - - return MakeResult(true); } }; diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 1bb0b55bdc..c267262239 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -120,13 +120,10 @@ public: * Check if the current thread should wait until the object is available * @return True if the current thread should wait due to this object being unavailable */ - virtual ResultVal ShouldWait() = 0; + virtual bool ShouldWait() = 0; - /** - * Acquire/lock the object if it is available - * @return True if we were able to acquire this object, otherwise false - */ - virtual ResultVal Acquire() = 0; + /// Acquire/lock the object if it is available + virtual void Acquire() = 0; /** * Add a thread to wait on this object diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 01d2263ff1..355824e605 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -27,8 +27,8 @@ public: std::string name; ///< Name of mutex (optional) SharedPtr current_thread; ///< Thread that has acquired the mutex - ResultVal ShouldWait() override; - ResultVal Acquire() override; + bool ShouldWait() override; + void Acquire() override; }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -159,20 +159,14 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { return handle; } -ResultVal Mutex::ShouldWait() { - return MakeResult(locked && (current_thread != GetCurrentThread())); +bool Mutex::ShouldWait() { + return locked && current_thread != GetCurrentThread(); } -ResultVal Mutex::Acquire() { - bool res = false; - - if (!locked) { - // Lock the mutex when the first thread accesses it - locked = true; - res = true; - MutexAcquireLock(this); - } - - return MakeResult(res); +void Mutex::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); + locked = true; + MutexAcquireLock(this); } + } // namespace diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 24d41c0b37..274680568e 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -24,27 +24,13 @@ public: s32 available_count; ///< Number of free slots left in the semaphore std::string name; ///< Name of semaphore (optional) - /** - * Tests whether a semaphore still has free slots - * @return Whether the semaphore is available - */ - bool IsAvailable() const { - return available_count > 0; + bool ShouldWait() override { + return available_count <= 0; } - ResultVal ShouldWait() override { - return MakeResult(!IsAvailable()); - } - - ResultVal Acquire() override { - bool res = false; - - if (IsAvailable()) { - --available_count; - res = true; - } - - return MakeResult(res); + void Acquire() override { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); + --available_count; } }; @@ -84,8 +70,8 @@ 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->ReleaseNextThread() != nullptr) { - --semaphore->available_count; + while (!semaphore->ShouldWait() && semaphore->ReleaseNextThread() != nullptr) { + semaphore->Acquire(); } return RESULT_SUCCESS; diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index f0343d9b24..1788e4375d 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -57,12 +57,12 @@ public: // TODO(bunnei): These functions exist to satisfy a hardware test with a Session object // passed into WaitSynchronization. Figure out the meaning of them. - ResultVal ShouldWait() override { - return MakeResult(true); + bool ShouldWait() override { + return true; } - ResultVal Acquire() override { - return MakeResult(false); + void Acquire() override { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); } }; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 8a2cf8bf45..7a7f430cfb 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -22,12 +22,12 @@ namespace Kernel { -ResultVal Thread::ShouldWait() { - return MakeResult(status != THREADSTATUS_DORMANT); +bool Thread::ShouldWait() { + return status != THREADSTATUS_DORMANT; } -ResultVal Thread::Acquire() { - return MakeResult(true); +void Thread::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); } // Lists all thread ids that aren't deleted/etc. @@ -269,9 +269,7 @@ void Thread::ReleaseWaitObject(WaitObject* wait_object) { // Iterate through all waiting objects to check availability... for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) { - auto res = (*itr)->ShouldWait(); - - if (*res && res.Succeeded()) + if ((*itr)->ShouldWait()) wait_all_failed = true; // The output should be the last index of wait_object diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index b23638bd17..bed9f714a5 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -58,8 +58,8 @@ public: inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } inline bool IsIdle() const { return idle; } - ResultVal ShouldWait() override; - ResultVal Acquire() override; + bool ShouldWait() override; + void Acquire() override; s32 GetPriority() const { return current_priority; } void SetPriority(s32 priority); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 1729cca0f7..8d9db92a4d 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -29,12 +29,12 @@ 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 ShouldWait() override { - return MakeResult(!signaled); + bool ShouldWait() override { + return !signaled; } - ResultVal Acquire() override { - return MakeResult(true); + void Acquire() override { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); } }; diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index fd2d22727f..f6c912502f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -122,10 +122,8 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); - ResultVal wait = object->ShouldWait(); - // Check for next thread to schedule - if (wait.Succeeded() && *wait) { + if (object->ShouldWait()) { object->AddWaitingThread(Kernel::GetCurrentThread()); Kernel::WaitCurrentThread_WaitSynchronization(object); @@ -138,7 +136,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { object->Acquire(); } - return wait.Code().raw; + return RESULT_SUCCESS.raw; } /// Wait for the given handles to synchronize, timeout after the specified nanoseconds @@ -167,10 +165,8 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; - ResultVal wait = object->ShouldWait(); - // Check if the current thread should wait on this object... - if (wait.Succeeded() && *wait) { + if (object->ShouldWait()) { // Check we are waiting on all objects... if (wait_all) From f09806aed24b2f7de7d969cbfdb3b9d18ab90c61 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 Jan 2015 18:20:47 -0500 Subject: [PATCH 23/27] Kernel: Renamed some functions for clarity. - ReleaseNextThread->WakeupNextThread - ReleaseAllWaitingThreads->WakeupAllWaitingThreads. --- src/core/hle/kernel/event.cpp | 2 +- src/core/hle/kernel/kernel.cpp | 2 +- src/core/hle/kernel/kernel.h | 8 ++++---- src/core/hle/kernel/mutex.cpp | 2 +- src/core/hle/kernel/semaphore.cpp | 2 +- src/core/hle/kernel/thread.cpp | 2 +- src/core/hle/kernel/timer.cpp | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index cdacba1d9d..a481259651 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -47,7 +47,7 @@ ResultCode SignalEvent(const Handle handle) { return InvalidHandle(ErrorModule::Kernel); evt->signaled = true; - evt->ReleaseAllWaitingThreads(); + evt->WakeupAllWaitingThreads(); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 6923498578..d7fa4dcea0 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -30,7 +30,7 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { waiting_threads.erase(itr); } -Thread* WaitObject::ReleaseNextThread() { +Thread* WaitObject::WakeupNextThread() { if (waiting_threads.empty()) return nullptr; diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index c267262239..3828efbeaf 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -138,13 +138,13 @@ public: void RemoveWaitingThread(Thread* thead); /** - * Releases (and removes) the next thread waiting on this object + * Wake up the next thread waiting on this object * @return Pointer to the thread that was resumed, nullptr if no threads are waiting */ - Thread* ReleaseNextThread(); + Thread* WakeupNextThread(); - /// Releases all threads waiting on this object - void ReleaseAllWaitingThreads(); + /// Wake up all threads waiting on this object + void WakeupAllWaitingThreads(); private: std::vector waiting_threads; ///< Threads waiting for this object to become available diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 355824e605..c170e55ff2 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -53,7 +53,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->ReleaseNextThread(); + auto next_thread = mutex->WakeupNextThread(); if (next_thread != nullptr) { MutexAcquireLock(mutex, next_thread->GetHandle()); } else { diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 274680568e..135d8fb2a9 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -70,7 +70,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->ShouldWait() && semaphore->ReleaseNextThread() != nullptr) { + while (!semaphore->ShouldWait() && semaphore->WakeupNextThread() != nullptr) { semaphore->Acquire(); } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 7a7f430cfb..ab1126a360 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -104,7 +104,7 @@ void Thread::Stop(const char* reason) { ChangeReadyState(this, false); status = THREADSTATUS_DORMANT; - ReleaseAllWaitingThreads(); + WakeupAllWaitingThreads(); // Stopped threads are never waiting. wait_objects.clear(); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 8d9db92a4d..ec0b2c3239 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -90,7 +90,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { timer->signaled = true; // Resume all waiting threads - timer->ReleaseAllWaitingThreads(); + timer->WakeupAllWaitingThreads(); if (timer->reset_type == RESETTYPE_ONESHOT) timer->signaled = false; From 2f3020a10247a0cb47848a6f8c19fbde50a7e0a6 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 Jan 2015 18:33:23 -0500 Subject: [PATCH 24/27] Mutex: Cleanup and remove redundant code. --- src/core/hle/kernel/mutex.cpp | 74 +++++++++++++--------------------- src/core/hle/kernel/mutex.h | 2 +- src/core/hle/kernel/thread.cpp | 2 +- 3 files changed, 30 insertions(+), 48 deletions(-) diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index c170e55ff2..cd05a1397c 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -23,9 +23,8 @@ public: bool initial_locked; ///< Initial lock state when mutex was created bool locked; ///< Current locked state - Handle lock_thread; ///< Handle to thread that currently has mutex std::string name; ///< Name of mutex (optional) - SharedPtr current_thread; ///< Thread that has acquired the mutex + SharedPtr holding_thread; ///< Thread that has acquired the mutex bool ShouldWait() override; void Acquire() override; @@ -33,18 +32,17 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////////// -typedef std::multimap MutexMap; +typedef std::multimap, SharedPtr> MutexMap; static MutexMap g_mutex_held_locks; /** * Acquires the specified mutex for the specified thread * @param mutex Mutex that is to be acquired - * @param thread Thread that will acquired + * @param thread Thread that will acquire the mutex */ -void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) { - g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); - mutex->lock_thread = thread; - mutex->current_thread = Kernel::g_handle_table.Get(thread); +void MutexAcquireLock(Mutex* mutex, Thread* thread) { + g_mutex_held_locks.insert(std::make_pair(thread, mutex)); + mutex->holding_thread = thread; } /** @@ -55,51 +53,39 @@ void ResumeWaitingThread(Mutex* mutex) { // Find the next waiting thread for the mutex... auto next_thread = mutex->WakeupNextThread(); if (next_thread != nullptr) { - MutexAcquireLock(mutex, next_thread->GetHandle()); + MutexAcquireLock(mutex, next_thread); } else { // Reset mutex lock thread handle, nothing is waiting mutex->locked = false; - mutex->lock_thread = -1; + mutex->holding_thread = nullptr; } } -void MutexEraseLock(Mutex* mutex) { - Handle handle = mutex->GetHandle(); - auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); - for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - if (iter->second == handle) { - g_mutex_held_locks.erase(iter); - break; - } - } - mutex->lock_thread = -1; -} - -void ReleaseThreadMutexes(Handle thread) { +void ReleaseThreadMutexes(Thread* thread) { auto locked = g_mutex_held_locks.equal_range(thread); // Release every mutex that the thread holds, and resume execution on the waiting threads - for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - Mutex* mutex = g_handle_table.Get(iter->second).get(); - ResumeWaitingThread(mutex); + for (auto iter = locked.first; iter != locked.second; ++iter) { + ResumeWaitingThread(iter->second.get()); } // Erase all the locks that this thread holds g_mutex_held_locks.erase(thread); } -bool LockMutex(Mutex* mutex) { - // Mutex alread locked? - if (mutex->locked) { - return false; - } - MutexAcquireLock(mutex); - return true; -} - bool ReleaseMutex(Mutex* mutex) { - MutexEraseLock(mutex); - ResumeWaitingThread(mutex); + if (mutex->locked) { + auto locked = g_mutex_held_locks.equal_range(mutex->holding_thread); + + for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { + if (iter->second == mutex) { + g_mutex_held_locks.erase(iter); + break; + } + } + + ResumeWaitingThread(mutex); + } return true; } @@ -134,16 +120,12 @@ Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) mutex->locked = mutex->initial_locked = initial_locked; mutex->name = name; - mutex->current_thread = nullptr; + mutex->holding_thread = nullptr; // Acquire mutex with current thread if initialized as locked... - if (mutex->locked) { - MutexAcquireLock(mutex); + if (mutex->locked) + MutexAcquireLock(mutex, GetCurrentThread()); - // Otherwise, reset lock thread handle - } else { - mutex->lock_thread = -1; - } return mutex; } @@ -160,13 +142,13 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { } bool Mutex::ShouldWait() { - return locked && current_thread != GetCurrentThread(); + return locked && holding_thread != GetCurrentThread(); } void Mutex::Acquire() { _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); locked = true; - MutexAcquireLock(this); + MutexAcquireLock(this, GetCurrentThread()); } } // namespace diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index a8ca97014e..bb8778c989 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -28,6 +28,6 @@ Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); * Releases all the mutexes held by the specified thread * @param thread Thread that is holding the mutexes */ -void ReleaseThreadMutexes(Handle thread); +void ReleaseThreadMutexes(Thread* thread); } // namespace diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index ab1126a360..3ca9603c26 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -100,7 +100,7 @@ static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { /// Stops the current thread void Thread::Stop(const char* reason) { // Release all the mutexes that this thread holds - ReleaseThreadMutexes(GetHandle()); + ReleaseThreadMutexes(this); ChangeReadyState(this, false); status = THREADSTATUS_DORMANT; From 4255f25647dee3ae6098d14adbb3db0749935120 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 Jan 2015 18:40:01 -0500 Subject: [PATCH 25/27] Thread: Use std::find in CheckWait_WaitObject. --- src/core/hle/kernel/thread.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 3ca9603c26..58523e1455 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -85,10 +85,11 @@ static void ChangeReadyState(Thread* t, bool ready) { /// Check if a thread is waiting on a the specified wait object static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { - for (auto itr = thread->wait_objects.begin(); itr != thread->wait_objects.end(); ++itr) { - if (*itr == wait_object) - return (thread->IsWaiting()); - } + auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); + + if (itr != thread->wait_objects.end()) + return thread->IsWaiting(); + return false; } From 68ddaaa2f5726e3619accee77b488ec285f3a2d7 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 Jan 2015 20:53:52 -0500 Subject: [PATCH 26/27] Thread: Fix WaitSynchronization1 to not set register 1 on thread wakeup. --- src/core/hle/kernel/thread.cpp | 43 ++++++++++++++++++++++------------ src/core/hle/kernel/thread.h | 21 +++++++++++------ src/core/hle/svc.cpp | 6 ++--- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 58523e1455..03b492c751 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -205,25 +205,24 @@ static Thread* NextThread() { void WaitCurrentThread_Sleep() { Thread* thread = GetCurrentThread(); - thread->wait_all = false; - thread->wait_address = 0; - thread->wait_objects.clear(); ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } -void WaitCurrentThread_WaitSynchronization(SharedPtr wait_object, bool wait_all) { +void WaitCurrentThread_WaitSynchronization(SharedPtr wait_object, bool wait_set_output, bool wait_all) { Thread* thread = GetCurrentThread(); + thread->wait_set_output = wait_set_output; thread->wait_all = wait_all; - thread->wait_address = 0; - thread->wait_objects.push_back(wait_object); + + // It's possible to call WaitSynchronizationN without any objects passed in... + if (wait_object != nullptr) + thread->wait_objects.push_back(wait_object); + ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { Thread* thread = GetCurrentThread(); - thread->wait_all = false; thread->wait_address = wait_address; - thread->wait_objects.clear(); ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } @@ -239,8 +238,11 @@ static void ThreadWakeupCallback(u64 parameter, int cycles_late) { return; } - thread->SetReturnValue(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, - ErrorSummary::StatusChanged, ErrorLevel::Info), -1); + thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, + ErrorSummary::StatusChanged, ErrorLevel::Info)); + + if (thread->wait_set_output) + thread->SetWaitSynchronizationOutput(-1); thread->ResumeFromWait(); } @@ -282,12 +284,18 @@ void Thread::ReleaseWaitObject(WaitObject* wait_object) { if (wait_all) { // Resume the thread only if all are available... if (!wait_all_failed) { - SetReturnValue(RESULT_SUCCESS, -1); + SetWaitSynchronizationResult(RESULT_SUCCESS); + SetWaitSynchronizationOutput(-1); + ResumeFromWait(); } } else { // Otherwise, resume - SetReturnValue(RESULT_SUCCESS, index); + SetWaitSynchronizationResult(RESULT_SUCCESS); + + if (wait_set_output) + SetWaitSynchronizationOutput(index); + ResumeFromWait(); } } @@ -303,6 +311,7 @@ void Thread::ResumeFromWait() { wait_object->RemoveWaitingThread(this); wait_objects.clear(); + wait_set_output = false; wait_all = false; wait_address = 0; @@ -371,6 +380,7 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, thread->stack_size = stack_size; thread->initial_priority = thread->current_priority = priority; thread->processor_id = processor_id; + thread->wait_set_output = false; thread->wait_all = false; thread->wait_objects.clear(); thread->wait_address = 0; @@ -462,9 +472,12 @@ 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 Thread::SetWaitSynchronizationResult(ResultCode result) { + context.cpu_registers[0] = result.raw; +} + +void Thread::SetWaitSynchronizationOutput(s32 output) { + context.cpu_registers[1] = output; } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index bed9f714a5..5fab1ab588 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -78,11 +78,16 @@ public: void ResumeFromWait(); /** - * 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 + * Sets the result after the thread awakens (from either WaitSynchronization SVC) + * @param result Value to set to the returned result */ - void SetReturnValue(ResultCode return_val, s32 out_val); + void SetWaitSynchronizationResult(ResultCode result); + + /** + * Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only) + * @param output Value to set to the output parameter + */ + void SetWaitSynchronizationOutput(s32 output); Core::ThreadContext context; @@ -100,8 +105,9 @@ public: std::vector> wait_objects; ///< Objects that the thread is waiting on - VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address - bool wait_all; ///< True if the thread is waiting on all objects before resuming + VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address + bool wait_all; ///< True if the thread is waiting on all objects before resuming + bool wait_set_output; ///< True if the output parameter should be set on thread wakeup std::string name; @@ -134,9 +140,10 @@ void WaitCurrentThread_Sleep(); /** * Waits the current thread from a WaitSynchronization call * @param wait_object Kernel object that we are waiting on + * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only) * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) */ -void WaitCurrentThread_WaitSynchronization(SharedPtr wait_object, bool wait_all = false); +void WaitCurrentThread_WaitSynchronization(SharedPtr wait_object, bool wait_set_output, bool wait_all); /** * Waits the current thread from an ArbitrateAddress call diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index f6c912502f..89095ac91e 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -126,7 +126,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { if (object->ShouldWait()) { object->AddWaitingThread(Kernel::GetCurrentThread()); - Kernel::WaitCurrentThread_WaitSynchronization(object); + Kernel::WaitCurrentThread_WaitSynchronization(object, false, false); // Create an event to wake the thread up after the specified nanosecond delay has passed Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); @@ -187,7 +187,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, // NOTE: This should deadlock the current thread if no timeout was specified if (!wait_all) { wait_thread = true; - Kernel::WaitCurrentThread_Sleep(); + Kernel::WaitCurrentThread_WaitSynchronization(nullptr, true, wait_all); } } @@ -198,7 +198,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, for (int i = 0; i < handle_count; ++i) { auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); object->AddWaitingThread(Kernel::GetCurrentThread()); - Kernel::WaitCurrentThread_WaitSynchronization(object, wait_all); + Kernel::WaitCurrentThread_WaitSynchronization(object, true, wait_all); } // Create an event to wake the thread up after the specified nanosecond delay has passed From 731154f79e4f2e417a9db97e125eadb26b6b6f06 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 21 Jan 2015 18:27:01 -0500 Subject: [PATCH 27/27] WaitSynchronization: Added a result code for invalid result, fixed bug. --- src/core/hle/svc.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 89095ac91e..2d922046ef 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -29,6 +29,9 @@ using Kernel::SharedPtr; namespace SVC { +/// An invalid result code that is meant to be overwritten when a thread resumes from waiting +const ResultCode RESULT_INVALID(0xDEADC0DE); + enum ControlMemoryOperation { MEMORY_OPERATION_HEAP = 0x00000003, MEMORY_OPERATION_GSP_HEAP = 0x00010003, @@ -132,10 +135,13 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); HLE::Reschedule(__func__); - } else { - object->Acquire(); + + // NOTE: output of this SVC will be set later depending on how the thread resumes + return RESULT_INVALID.raw; } + object->Acquire(); + return RESULT_SUCCESS.raw; } @@ -207,7 +213,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, HLE::Reschedule(__func__); // NOTE: output of this SVC will be set later depending on how the thread resumes - return 0xDEADBEEF; + return RESULT_INVALID.raw; } // Acquire objects if we did not wait...