From c22bac6398ff1705992fc44b2c29775c84cff662 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 Jan 2015 19:22:50 -0500 Subject: 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(-) (limited to 'src/core/hle/kernel') diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 271190dbe..bf71e9edb 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 d3684896f..07e96e633 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 5e5217b78..a9af9de88 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 853a5dd74..35d829606 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 88ec9a104..af2c465e4 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 bc86a7c59..845672702 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 8c9f63aa5..daaeb26a4 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 3b0452d4d..2d4fa4c01 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; -- cgit v1.2.3