mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Merge pull request #495 from bunnei/fix-waitsynch
Fix WaitSynchronization
This commit is contained in:
		
						commit
						24a63662ba
					
				
					 15 changed files with 474 additions and 371 deletions
				
			
		|  | @ -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(); | ||||
|     AddressArbiter* object = Kernel::g_handle_table.Get<AddressArbiter>(handle).get(); | ||||
| 
 | ||||
|     if (object == nullptr) | ||||
|         return InvalidHandle(ErrorModule::Kernel); | ||||
| 
 | ||||
|  | @ -40,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(WAITTYPE_ARB, object, address); | ||||
|             Kernel::WaitCurrentThread_ArbitrateAddress(address); | ||||
|             HLE::Reschedule(__func__); | ||||
|         } | ||||
|         break; | ||||
|     case ArbitrationType::WaitIfLessThanWithTimeout: | ||||
|         if ((s32)Memory::Read32(address) <= value) { | ||||
|             Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); | ||||
|             Kernel::WaitCurrentThread_ArbitrateAddress(address); | ||||
|             Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); | ||||
|             HLE::Reschedule(__func__); | ||||
|         } | ||||
|  | @ -67,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(address); | ||||
|             HLE::Reschedule(__func__); | ||||
|         } | ||||
|         break; | ||||
|  | @ -77,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(address); | ||||
|             Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); | ||||
|             HLE::Reschedule(__func__); | ||||
|         } | ||||
|  |  | |||
|  | @ -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; } | ||||
|  | @ -25,99 +25,40 @@ public: | |||
|     ResetType intitial_reset_type;          ///< ResetType specified at Event initialization
 | ||||
|     ResetType reset_type;                   ///< Current ResetType
 | ||||
| 
 | ||||
|     bool locked;                            ///< Event signal wait
 | ||||
|     bool permanent_locked;                  ///< Hack - to set event permanent state (for easy passthrough)
 | ||||
|     std::vector<Handle> waiting_threads;    ///< Threads that are waiting for the event
 | ||||
|     bool signaled;                          ///< Whether the event has already been signaled
 | ||||
|     std::string name;                       ///< Name of event (optional)
 | ||||
| 
 | ||||
|     ResultVal<bool> 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); | ||||
|             } | ||||
|             Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); | ||||
|         } | ||||
|         if (reset_type != RESETTYPE_STICKY && !permanent_locked) { | ||||
|             locked = true; | ||||
|         } | ||||
|         return MakeResult<bool>(wait); | ||||
|     bool ShouldWait() override { | ||||
|         return !signaled; | ||||
|     } | ||||
| 
 | ||||
|     void Acquire() override { | ||||
|         _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||
| 
 | ||||
|         // Release the event if it's not sticky...
 | ||||
|         if (reset_type != RESETTYPE_STICKY) | ||||
|             signaled = false; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * 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<Event>(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 | ||||
|  * @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<Event>(handle).get(); | ||||
|     if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | ||||
| 
 | ||||
|     if (!evt->permanent_locked) { | ||||
|         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<Event>(handle).get(); | ||||
|     if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | ||||
|     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<Thread>(evt->waiting_threads[i]).get(); | ||||
|         if (thread != nullptr) | ||||
|             thread->ResumeFromWait(); | ||||
|     evt->signaled = true; | ||||
|     evt->WakeupAllWaitingThreads(); | ||||
| 
 | ||||
|         // 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(); | ||||
| 
 | ||||
|     if (!evt->permanent_locked) { | ||||
|         evt->locked = event_caught; | ||||
|     } | ||||
|     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<Event>(handle).get(); | ||||
|     if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | ||||
|     if (evt == nullptr) | ||||
|         return InvalidHandle(ErrorModule::Kernel); | ||||
| 
 | ||||
|     evt->signaled = false; | ||||
| 
 | ||||
|     if (!evt->permanent_locked) { | ||||
|         evt->locked = true; | ||||
|     } | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
|  | @ -134,20 +75,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->permanent_locked = false; | ||||
|     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); | ||||
|  |  | |||
|  | @ -11,29 +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); | ||||
| 
 | ||||
| /**
 | ||||
|  * 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 | ||||
|  * @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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,6 +18,41 @@ SharedPtr<Thread> g_main_thread = nullptr; | |||
| HandleTable g_handle_table; | ||||
| u64 g_program_id = 0; | ||||
| 
 | ||||
| void WaitObject::AddWaitingThread(Thread* thread) { | ||||
|     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::WakeupNextThread() { | ||||
|     if (waiting_threads.empty()) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     auto next_thread = waiting_threads.front(); | ||||
|     waiting_threads.erase(waiting_threads.begin()); | ||||
| 
 | ||||
|     next_thread->ReleaseWaitObject(this); | ||||
| 
 | ||||
|     return next_thread; | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
|     _assert_msg_(Kernel, waiting_threads.empty(), "failed to awaken all waiting threads!"); | ||||
| } | ||||
| 
 | ||||
| HandleTable::HandleTable() { | ||||
|     next_generation = 1; | ||||
|     Clear(); | ||||
|  |  | |||
|  | @ -8,6 +8,8 @@ | |||
| 
 | ||||
| #include <array> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
|  | @ -58,17 +60,35 @@ 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; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Wait for kernel object to synchronize. | ||||
|      * @return True if the current thread should wait as a result of the wait | ||||
|      * Check if a thread can wait on the object | ||||
|      * @return True if a thread can wait on the object, otherwise false | ||||
|      */ | ||||
|     virtual ResultVal<bool> WaitSynchronization() { | ||||
|         LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); | ||||
|         return UnimplementedFunction(ErrorModule::Kernel); | ||||
|     bool IsWaitable() const { | ||||
|         switch (GetHandleType()) { | ||||
|         case HandleType::Session: | ||||
|         case HandleType::Event: | ||||
|         case HandleType::Mutex: | ||||
|         case HandleType::Thread: | ||||
|         case HandleType::Semaphore: | ||||
|         case HandleType::Timer: | ||||
|             return true; | ||||
| 
 | ||||
|         case HandleType::Unknown: | ||||
|         case HandleType::Port: | ||||
|         case HandleType::SharedMemory: | ||||
|         case HandleType::Redirection: | ||||
|         case HandleType::Process: | ||||
|         case HandleType::AddressArbiter: | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|  | @ -92,6 +112,44 @@ inline void intrusive_ptr_release(Object* object) { | |||
| template <typename T> | ||||
| using SharedPtr = boost::intrusive_ptr<T>; | ||||
| 
 | ||||
| /// Class that represents a Kernel object that a thread can be waiting on
 | ||||
| class WaitObject : public Object { | ||||
| 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 bool ShouldWait() = 0; | ||||
| 
 | ||||
|     /// Acquire/lock the object if it is available
 | ||||
|     virtual void Acquire() = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Add a thread to wait on this object | ||||
|      * @param thread Pointer to thread to add | ||||
|      */ | ||||
|     void AddWaitingThread(Thread* thread); | ||||
| 
 | ||||
|     /**
 | ||||
|      * 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); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Wake up the next thread waiting on this object | ||||
|      * @return Pointer to the thread that was resumed, nullptr if no threads are waiting | ||||
|      */ | ||||
|     Thread* WakeupNextThread(); | ||||
| 
 | ||||
|     /// Wake up all threads waiting on this object
 | ||||
|     void WakeupAllWaitingThreads(); | ||||
| 
 | ||||
| private: | ||||
|     std::vector<Thread*> 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 | ||||
|  | @ -146,14 +204,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<Object> 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 <class T> | ||||
|     SharedPtr<T> Get(Handle handle) const { | ||||
|  | @ -164,6 +222,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<WaitObject> GetWaitObject(Handle handle) const { | ||||
|         SharedPtr<Object> object = GetGeneric(handle); | ||||
|         if (object != nullptr && object->IsWaitable()) { | ||||
|             return boost::static_pointer_cast<WaitObject>(std::move(object)); | ||||
|         } | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     /// Closes all handles held in this table.
 | ||||
|     void Clear(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; } | ||||
|  | @ -23,39 +23,26 @@ 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<Handle> waiting_threads;        ///< Threads that are waiting for the mutex
 | ||||
|     std::string name;                           ///< Name of mutex (optional)
 | ||||
|     SharedPtr<Thread> holding_thread;           ///< Thread that has acquired the mutex
 | ||||
| 
 | ||||
|     ResultVal<bool> WaitSynchronization() override; | ||||
|     bool ShouldWait() override; | ||||
|     void Acquire() override; | ||||
| }; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| typedef std::multimap<Handle, Handle> MutexMap; | ||||
| typedef std::multimap<SharedPtr<Thread>, SharedPtr<Mutex>> 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; | ||||
| } | ||||
| 
 | ||||
| bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) { | ||||
|     MutexAcquireLock(mutex, thread_handle); | ||||
| 
 | ||||
|     Thread* thread = Kernel::g_handle_table.Get<Thread>(thread_handle).get(); | ||||
|     if (thread == nullptr) { | ||||
|         LOG_ERROR(Kernel, "Called with invalid handle: %08X", thread_handle); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     thread->ResumeFromWait(); | ||||
|     return true; | ||||
| void MutexAcquireLock(Mutex* mutex, Thread* thread) { | ||||
|     g_mutex_held_locks.insert(std::make_pair(thread, mutex)); | ||||
|     mutex->holding_thread = thread; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -64,56 +51,41 @@ bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) { | |||
|  */ | ||||
| void ResumeWaitingThread(Mutex* mutex) { | ||||
|     // Find the next waiting thread for the mutex...
 | ||||
|     if (mutex->waiting_threads.empty()) { | ||||
|     auto next_thread = mutex->WakeupNextThread(); | ||||
|     if (next_thread != nullptr) { | ||||
|         MutexAcquireLock(mutex, next_thread); | ||||
|     } 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<Handle>::iterator iter = mutex->waiting_threads.begin(); | ||||
|         ReleaseMutexForThread(mutex, *iter); | ||||
|         mutex->waiting_threads.erase(iter); | ||||
|         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<Mutex>(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; | ||||
| } | ||||
| 
 | ||||
|  | @ -148,15 +120,12 @@ Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) | |||
| 
 | ||||
|     mutex->locked = mutex->initial_locked = initial_locked; | ||||
|     mutex->name = name; | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
|  | @ -172,17 +141,14 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { | |||
|     return handle; | ||||
| } | ||||
| 
 | ||||
| ResultVal<bool> Mutex::WaitSynchronization() { | ||||
|     bool wait = locked; | ||||
|     if (locked) { | ||||
|         waiting_threads.push_back(GetCurrentThread()->GetHandle()); | ||||
|         Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this); | ||||
|     } else { | ||||
|         // Lock the mutex when the first thread accesses it
 | ||||
|         locked = true; | ||||
|         MutexAcquireLock(this); | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<bool>(wait); | ||||
| bool Mutex::ShouldWait() { | ||||
|     return locked && holding_thread != GetCurrentThread(); | ||||
| } | ||||
| 
 | ||||
| void Mutex::Acquire() { | ||||
|     _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||
|     locked = true; | ||||
|     MutexAcquireLock(this, GetCurrentThread()); | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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,28 +22,15 @@ 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<Handle> waiting_threads;         ///< Threads that are waiting for 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<bool> WaitSynchronization() override { | ||||
|         bool wait = !IsAvailable(); | ||||
| 
 | ||||
|         if (wait) { | ||||
|             Kernel::WaitCurrentThread(WAITTYPE_SEMA, this); | ||||
|             waiting_threads.push(GetCurrentThread()->GetHandle()); | ||||
|         } else { | ||||
|             --available_count; | ||||
|         } | ||||
| 
 | ||||
|         return MakeResult<bool>(wait); | ||||
|     void Acquire() override { | ||||
|         _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||
|         --available_count; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  | @ -83,12 +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->waiting_threads.empty() && semaphore->IsAvailable()) { | ||||
|         Thread* thread = Kernel::g_handle_table.Get<Thread>(semaphore->waiting_threads.front()).get(); | ||||
|         if (thread != nullptr) | ||||
|             thread->ResumeFromWait(); | ||||
|         semaphore->waiting_threads.pop(); | ||||
|         --semaphore->available_count; | ||||
|     while (!semaphore->ShouldWait() && semaphore->WakeupNextThread() != nullptr) { | ||||
|         semaphore->Acquire(); | ||||
|     } | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
|  |  | |||
|  | @ -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,17 @@ public: | |||
|      * aren't supported yet. | ||||
|      */ | ||||
|     virtual ResultVal<bool> SyncRequest() = 0; | ||||
| 
 | ||||
|     // TODO(bunnei): These functions exist to satisfy a hardware test with a Session object
 | ||||
|     // passed into WaitSynchronization. Figure out the meaning of them.
 | ||||
| 
 | ||||
|     bool ShouldWait() override { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     void Acquire() override { | ||||
|         _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -22,17 +22,12 @@ | |||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| ResultVal<bool> 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); | ||||
|         } | ||||
|         WaitCurrentThread(WAITTYPE_THREADEND, this); | ||||
|     } | ||||
| bool Thread::ShouldWait() { | ||||
|     return status != THREADSTATUS_DORMANT; | ||||
| } | ||||
| 
 | ||||
|     return MakeResult<bool>(wait); | ||||
| void Thread::Acquire() { | ||||
|     _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||
| } | ||||
| 
 | ||||
| // Lists all thread ids that aren't deleted/etc.
 | ||||
|  | @ -67,8 +62,8 @@ 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_object = nullptr; | ||||
| 
 | ||||
|     t->wait_objects.clear(); | ||||
|     t->wait_address = 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -88,37 +83,32 @@ 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 waiting on a the specified wait object
 | ||||
| static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| /// 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; | ||||
| } | ||||
| 
 | ||||
| /// 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 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
 | ||||
| void Thread::Stop(const char* reason) { | ||||
|     // Release all the mutexes that this thread holds
 | ||||
|     ReleaseThreadMutexes(GetHandle()); | ||||
|     ReleaseThreadMutexes(this); | ||||
| 
 | ||||
|     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(); | ||||
|     WakeupAllWaitingThreads(); | ||||
| 
 | ||||
|     // Stopped threads are never waiting.
 | ||||
|     wait_type = WAITTYPE_NONE; | ||||
|     wait_object = nullptr; | ||||
|     wait_objects.clear(); | ||||
|     wait_address = 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -129,26 +119,20 @@ 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
 | ||||
| Thread* ArbitrateHighestPriorityThread(Object* 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(), WAITTYPE_ARB, 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(); | ||||
|  | @ -165,11 +149,11 @@ Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { | |||
| } | ||||
| 
 | ||||
| /// Arbitrate all threads currently waiting
 | ||||
| void ArbitrateAllThreads(Object* 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(), WAITTYPE_ARB, arbiter, address)) | ||||
|         if (CheckWait_AddressArbiter(thread.get(), address)) | ||||
|             thread->ResumeFromWait(); | ||||
|     } | ||||
| } | ||||
|  | @ -177,9 +161,6 @@ void ArbitrateAllThreads(Object* 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); | ||||
| } | ||||
| 
 | ||||
|  | @ -200,7 +181,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; | ||||
|  | @ -223,16 +203,27 @@ static Thread* NextThread() { | |||
|     return next; | ||||
| } | ||||
| 
 | ||||
| void WaitCurrentThread(WaitType wait_type, Object* wait_object) { | ||||
| void WaitCurrentThread_Sleep() { | ||||
|     Thread* thread = GetCurrentThread(); | ||||
|     thread->wait_type = wait_type; | ||||
|     thread->wait_object = wait_object; | ||||
|     ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | ||||
| } | ||||
| 
 | ||||
| void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { | ||||
|     WaitCurrentThread(wait_type, wait_object); | ||||
|     GetCurrentThread()->wait_address = wait_address; | ||||
| void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { | ||||
|     Thread* thread = GetCurrentThread(); | ||||
|     thread->wait_set_output = wait_set_output; | ||||
|     thread->wait_all = wait_all; | ||||
| 
 | ||||
|     // 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_address = wait_address; | ||||
|     ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | ||||
| } | ||||
| 
 | ||||
| /// Event type for the thread wake up event
 | ||||
|  | @ -247,6 +238,12 @@ static void ThreadWakeupCallback(u64 parameter, int cycles_late) { | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | ||||
|         ErrorSummary::StatusChanged, ErrorLevel::Info)); | ||||
| 
 | ||||
|     if (thread->wait_set_output) | ||||
|         thread->SetWaitSynchronizationOutput(-1); | ||||
| 
 | ||||
|     thread->ResumeFromWait(); | ||||
| } | ||||
| 
 | ||||
|  | @ -261,14 +258,63 @@ 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::ReleaseWaitObject(WaitObject* wait_object) { | ||||
|     if (wait_objects.empty()) { | ||||
|         LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Remove this thread from the waiting object's thread list
 | ||||
|     wait_object->RemoveWaitingThread(this); | ||||
| 
 | ||||
|     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) { | ||||
|         if ((*itr)->ShouldWait()) | ||||
|             wait_all_failed = true; | ||||
| 
 | ||||
|         // The output should be the last index of wait_object
 | ||||
|         if (*itr == wait_object) | ||||
|             index = itr - wait_objects.begin(); | ||||
|     } | ||||
| 
 | ||||
|     // If we are waiting on all objects...
 | ||||
|     if (wait_all) { | ||||
|         // Resume the thread only if all are available...
 | ||||
|         if (!wait_all_failed) { | ||||
|             SetWaitSynchronizationResult(RESULT_SUCCESS); | ||||
|             SetWaitSynchronizationOutput(-1); | ||||
| 
 | ||||
|             ResumeFromWait(); | ||||
|         } | ||||
|     } else { | ||||
|         // Otherwise, resume
 | ||||
|         SetWaitSynchronizationResult(RESULT_SUCCESS); | ||||
| 
 | ||||
|         if (wait_set_output) | ||||
|             SetWaitSynchronizationOutput(index); | ||||
| 
 | ||||
|         ResumeFromWait(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Thread::ResumeFromWait() { | ||||
|     // Cancel any outstanding wakeup events
 | ||||
|     CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); | ||||
| 
 | ||||
|     status &= ~THREADSTATUS_WAIT; | ||||
|     wait_object = nullptr; | ||||
|     wait_type = WAITTYPE_NONE; | ||||
| 
 | ||||
|     // Remove this thread from all other WaitObjects
 | ||||
|     for (auto wait_object : wait_objects) | ||||
|         wait_object->RemoveWaitingThread(this); | ||||
| 
 | ||||
|     wait_objects.clear(); | ||||
|     wait_set_output = false; | ||||
|     wait_all = false; | ||||
|     wait_address = 0; | ||||
| 
 | ||||
|     if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | ||||
|         ChangeReadyState(this, true); | ||||
|     } | ||||
|  | @ -334,8 +380,9 @@ ResultVal<SharedPtr<Thread>> 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_object = nullptr; | ||||
|     thread->wait_set_output = false; | ||||
|     thread->wait_all = false; | ||||
|     thread->wait_objects.clear(); | ||||
|     thread->wait_address = 0; | ||||
|     thread->name = std::move(name); | ||||
| 
 | ||||
|  | @ -419,13 +466,20 @@ 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", thread->GetHandle(),  | ||||
|                       thread->current_priority, thread->status); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Thread::SetWaitSynchronizationResult(ResultCode result) { | ||||
|     context.cpu_registers[0] = result.raw; | ||||
| } | ||||
| 
 | ||||
| void Thread::SetWaitSynchronizationOutput(s32 output) { | ||||
|     context.cpu_registers[1] = output; | ||||
| } | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| void ThreadingInit() { | ||||
|  |  | |||
|  | @ -38,21 +38,9 @@ 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 Kernel::Object { | ||||
| class Thread : public WaitObject { | ||||
| public: | ||||
|     static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, | ||||
|         u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); | ||||
|  | @ -70,7 +58,8 @@ public: | |||
|     inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | ||||
|     inline bool IsIdle() const { return idle; } | ||||
| 
 | ||||
|     ResultVal<bool> WaitSynchronization() override; | ||||
|     bool ShouldWait() override; | ||||
|     void Acquire() override; | ||||
| 
 | ||||
|     s32 GetPriority() const { return current_priority; } | ||||
|     void SetPriority(s32 priority); | ||||
|  | @ -78,9 +67,28 @@ public: | |||
|     u32 GetThreadId() const { return thread_id; } | ||||
| 
 | ||||
|     void Stop(const char* reason); | ||||
|     /// Resumes a thread from waiting by marking it as "ready".
 | ||||
|      | ||||
|     /**
 | ||||
|      * Release an acquired wait object | ||||
|      * @param wait_object WaitObject to release | ||||
|      */ | ||||
|     void ReleaseWaitObject(WaitObject* wait_object); | ||||
| 
 | ||||
|     /// Resumes a thread from waiting by marking it as "ready"
 | ||||
|     void ResumeFromWait(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Sets the result after the thread awakens (from either WaitSynchronization SVC) | ||||
|      * @param result Value to set to the returned result | ||||
|      */ | ||||
|     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; | ||||
| 
 | ||||
|     u32 thread_id; | ||||
|  | @ -95,11 +103,11 @@ public: | |||
| 
 | ||||
|     s32 processor_id; | ||||
| 
 | ||||
|     WaitType wait_type; | ||||
|     Object* wait_object; | ||||
|     VAddr wait_address; | ||||
|     std::vector<SharedPtr<WaitObject>> wait_objects; ///< Objects that the thread is waiting on
 | ||||
| 
 | ||||
|     std::vector<SharedPtr<Thread>> waiting_threads; | ||||
|     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; | ||||
| 
 | ||||
|  | @ -107,6 +115,7 @@ public: | |||
|     bool idle = false; | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     Thread() = default; | ||||
| }; | ||||
| 
 | ||||
|  | @ -117,38 +126,38 @@ SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); | |||
| void Reschedule(); | ||||
| 
 | ||||
| /// Arbitrate the highest priority thread that is waiting
 | ||||
| Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address); | ||||
| Thread* ArbitrateHighestPriorityThread(u32 address); | ||||
| 
 | ||||
| /// Arbitrate all threads currently waiting...
 | ||||
| void ArbitrateAllThreads(Object* arbiter, u32 address); | ||||
| void ArbitrateAllThreads(u32 address); | ||||
| 
 | ||||
| /// Gets the current thread
 | ||||
| Thread* GetCurrentThread(); | ||||
| 
 | ||||
| /// Waits the current thread on a sleep
 | ||||
| void WaitCurrentThread_Sleep(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Puts the current thread in the wait state for the given type | ||||
|  * @param wait_type Type of wait | ||||
|  * @param wait_object Kernel object that we are waiting on, defaults to current thread | ||||
|  * 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(WaitType wait_type, Object* wait_object = GetCurrentThread()); | ||||
| void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all); | ||||
| 
 | ||||
| /**
 | ||||
|  * Waits the current thread from an ArbitrateAddress call | ||||
|  * @param wait_address Arbitration address used to resume from wait | ||||
|  */ | ||||
| void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); | ||||
| 
 | ||||
| /**
 | ||||
|  * Schedules an event to wake up the specified thread after the specified delay. | ||||
|  * @param thread The thread to wake after the delay. | ||||
|  * @param handle The thread handle. | ||||
|  * @param nanoseconds The time this thread will be allowed to sleep for. | ||||
|  */ | ||||
| void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); | ||||
| 
 | ||||
| /**
 | ||||
|  * Puts the current thread in the wait state for the given type | ||||
|  * @param wait_type Type of wait | ||||
|  * @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); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets up the idle thread, this is a thread that is intended to never execute instructions, | ||||
|  * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue | ||||
|  | @ -156,6 +165,7 @@ void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_addre | |||
|  * @returns The handle of the idle thread | ||||
|  */ | ||||
| Handle SetupIdleThread(); | ||||
| 
 | ||||
| /// Initialize threading
 | ||||
| void ThreadingInit(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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,19 +24,17 @@ public: | |||
|     ResetType reset_type;                   ///< The ResetType of this timer
 | ||||
| 
 | ||||
|     bool signaled;                          ///< Whether the timer has been signaled or not
 | ||||
|     std::set<Handle> 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
 | ||||
|     u64 interval_delay;                     ///< The delay until the timer fires after the first time
 | ||||
| 
 | ||||
|     ResultVal<bool> WaitSynchronization() override { | ||||
|         bool wait = !signaled; | ||||
|         if (wait) { | ||||
|             waiting_threads.insert(GetCurrentThread()->GetHandle()); | ||||
|             Kernel::WaitCurrentThread(WAITTYPE_TIMER, this); | ||||
|         } | ||||
|         return MakeResult<bool>(wait); | ||||
|     bool ShouldWait() override { | ||||
|         return !signaled; | ||||
|     } | ||||
| 
 | ||||
|     void Acquire() override { | ||||
|         _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  | @ -92,12 +90,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> thread = Kernel::g_handle_table.Get<Thread>(thread_handle)) | ||||
|             thread->ResumeFromWait(); | ||||
|     } | ||||
| 
 | ||||
|     timer->waiting_threads.clear(); | ||||
|     timer->WakeupAllWaitingThreads(); | ||||
| 
 | ||||
|     if (timer->reset_type == RESETTYPE_ONESHOT) | ||||
|         timer->signaled = false; | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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, | ||||
|  | @ -103,12 +106,7 @@ static Result SendSyncRequest(Handle handle) { | |||
| 
 | ||||
|     LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); | ||||
| 
 | ||||
|     ResultVal<bool> wait = session->SyncRequest(); | ||||
|     if (wait.Succeeded() && *wait) { | ||||
|         Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
 | ||||
|     } | ||||
| 
 | ||||
|     return wait.Code().raw; | ||||
|     return session->SyncRequest().Code().raw; | ||||
| } | ||||
| 
 | ||||
| /// Close a handle
 | ||||
|  | @ -120,64 +118,122 @@ 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<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handle); | ||||
|     auto object = Kernel::g_handle_table.GetWaitObject(handle); | ||||
|     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<bool> wait = object->WaitSynchronization(); | ||||
| 
 | ||||
|     // Check for next thread to schedule
 | ||||
|     if (wait.Succeeded() && *wait) { | ||||
|     if (object->ShouldWait()) { | ||||
| 
 | ||||
|         object->AddWaitingThread(Kernel::GetCurrentThread()); | ||||
|         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); | ||||
| 
 | ||||
|         HLE::Reschedule(__func__); | ||||
| 
 | ||||
|         // NOTE: output of this SVC will be set later depending on how the thread resumes
 | ||||
|         return RESULT_INVALID.raw; | ||||
|     } | ||||
| 
 | ||||
|     return wait.Code().raw; | ||||
|     object->Acquire(); | ||||
| 
 | ||||
|     return RESULT_SUCCESS.raw; | ||||
| } | ||||
| 
 | ||||
| /// 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 = !wait_all; | ||||
|     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
 | ||||
|     // Check if 'handles' is invalid
 | ||||
|     if (handles == nullptr) | ||||
|         return ResultCode(ErrorDescription::InvalidPointer, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent).raw; | ||||
| 
 | ||||
|     LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", | ||||
|         handle_count, (wait_all ? "true" : "false"), nano_seconds); | ||||
|     // 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!"); | ||||
| 
 | ||||
|     // Iterate through each handle, synchronize kernel object
 | ||||
|     for (s32 i = 0; i < handle_count; i++) { | ||||
|         SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]); | ||||
|         if (object == nullptr) | ||||
|             return InvalidHandle(ErrorModule::Kernel).raw; | ||||
|     // Check if 'handle_count' is invalid
 | ||||
|     if (handle_count < 0) | ||||
|         return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw; | ||||
| 
 | ||||
|         LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], | ||||
|                 object->GetTypeName().c_str(), object->GetName().c_str()); | ||||
|     // 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) { | ||||
|             auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); | ||||
|             if (object == nullptr) | ||||
|                 return InvalidHandle(ErrorModule::Kernel).raw; | ||||
| 
 | ||||
|         // TODO(yuriks): Verify how the real function behaves when an error happens here
 | ||||
|         ResultVal<bool> wait_result = object->WaitSynchronization(); | ||||
|         bool wait = wait_result.Succeeded() && *wait_result; | ||||
|             // Check if the current thread should wait on this object...
 | ||||
|             if (object->ShouldWait()) { | ||||
| 
 | ||||
|         if (!wait && !wait_all) { | ||||
|             *out = i; | ||||
|             return RESULT_SUCCESS.raw; | ||||
|         } else { | ||||
|             unlock_all = false; | ||||
|                 // 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' is false
 | ||||
|         // NOTE: This should deadlock the current thread if no timeout was specified
 | ||||
|         if (!wait_all) { | ||||
|             wait_thread = true; | ||||
|             Kernel::WaitCurrentThread_WaitSynchronization(nullptr, true, wait_all); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (wait_all && unlock_all) { | ||||
|         *out = handle_count; | ||||
|         return RESULT_SUCCESS.raw; | ||||
|     // 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, true, wait_all); | ||||
|         } | ||||
| 
 | ||||
|         // Create an event to wake the thread up after the specified nanosecond delay has passed
 | ||||
|         Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); | ||||
| 
 | ||||
|         HLE::Reschedule(__func__); | ||||
| 
 | ||||
|         // NOTE: output of this SVC will be set later depending on how the thread resumes
 | ||||
|         return RESULT_INVALID.raw; | ||||
|     } | ||||
| 
 | ||||
|     // Check for next thread to schedule
 | ||||
|     HLE::Reschedule(__func__); | ||||
|     // 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; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 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; | ||||
| } | ||||
|  | @ -351,6 +407,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; | ||||
| } | ||||
| 
 | ||||
|  | @ -391,7 +448,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_Sleep(); | ||||
| 
 | ||||
|     // Create an event to wake the thread up after the specified nanosecond delay has passed
 | ||||
|     Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue