mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	WaitSynchronizationN: Implement return values
This commit is contained in:
		
							parent
							
								
									e4a5d8ad4f
								
							
						
					
					
						commit
						7faf2d8e06
					
				
					 10 changed files with 193 additions and 87 deletions
				
			
		|  | @ -52,13 +52,13 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | ||||||
|     // Wait current thread (acquire the arbiter)...
 |     // Wait current thread (acquire the arbiter)...
 | ||||||
|     case ArbitrationType::WaitIfLessThan: |     case ArbitrationType::WaitIfLessThan: | ||||||
|         if ((s32)Memory::Read32(address) <= value) { |         if ((s32)Memory::Read32(address) <= value) { | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); |             Kernel::WaitCurrentThread_ArbitrateAddress(object, address); | ||||||
|             HLE::Reschedule(__func__); |             HLE::Reschedule(__func__); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     case ArbitrationType::WaitIfLessThanWithTimeout: |     case ArbitrationType::WaitIfLessThanWithTimeout: | ||||||
|         if ((s32)Memory::Read32(address) <= value) { |         if ((s32)Memory::Read32(address) <= value) { | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); |             Kernel::WaitCurrentThread_ArbitrateAddress(object, address); | ||||||
|             Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); |             Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); | ||||||
|             HLE::Reschedule(__func__); |             HLE::Reschedule(__func__); | ||||||
|         } |         } | ||||||
|  | @ -68,7 +68,7 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | ||||||
|         s32 memory_value = Memory::Read32(address) - 1; |         s32 memory_value = Memory::Read32(address) - 1; | ||||||
|         Memory::Write32(address, memory_value); |         Memory::Write32(address, memory_value); | ||||||
|         if (memory_value <= value) { |         if (memory_value <= value) { | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); |             Kernel::WaitCurrentThread_ArbitrateAddress(object, address); | ||||||
|             HLE::Reschedule(__func__); |             HLE::Reschedule(__func__); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|  | @ -78,7 +78,7 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | ||||||
|         s32 memory_value = Memory::Read32(address) - 1; |         s32 memory_value = Memory::Read32(address) - 1; | ||||||
|         Memory::Write32(address, memory_value); |         Memory::Write32(address, memory_value); | ||||||
|         if (memory_value <= value) { |         if (memory_value <= value) { | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); |             Kernel::WaitCurrentThread_ArbitrateAddress(object, address); | ||||||
|             Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); |             Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); | ||||||
|             HLE::Reschedule(__func__); |             HLE::Reschedule(__func__); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -28,11 +28,11 @@ public: | ||||||
|     bool signaled;                          ///< Whether the event has already been signaled
 |     bool signaled;                          ///< Whether the event has already been signaled
 | ||||||
|     std::string name;                       ///< Name of event (optional)
 |     std::string name;                       ///< Name of event (optional)
 | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> WaitSynchronization() override { |     ResultVal<bool> WaitSynchronization(unsigned index) override { | ||||||
|         bool wait = !signaled; |         bool wait = !signaled; | ||||||
|         if (wait) { |         if (wait) { | ||||||
|             AddWaitingThread(GetCurrentThread()); |             AddWaitingThread(GetCurrentThread()); | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); |             Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_EVENT, this, index); | ||||||
|         } |         } | ||||||
|         return MakeResult<bool>(wait); |         return MakeResult<bool>(wait); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -30,13 +30,13 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { | ||||||
|         waiting_threads.erase(itr); |         waiting_threads.erase(itr); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Thread* WaitObject::ResumeNextThread() { | Thread* WaitObject::ReleaseNextThread() { | ||||||
|     if (waiting_threads.empty()) |     if (waiting_threads.empty()) | ||||||
|         return nullptr; |         return nullptr; | ||||||
| 
 | 
 | ||||||
|     auto next_thread = waiting_threads.front(); |     auto next_thread = waiting_threads.front(); | ||||||
| 
 | 
 | ||||||
|     next_thread->ResumeFromWait(); |     next_thread->ReleaseFromWait(this); | ||||||
|     waiting_threads.erase(waiting_threads.begin()); |     waiting_threads.erase(waiting_threads.begin()); | ||||||
| 
 | 
 | ||||||
|     return next_thread.get(); |     return next_thread.get(); | ||||||
|  |  | ||||||
|  | @ -66,9 +66,10 @@ public: | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Wait for kernel object to synchronize. |      * 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 |      * @return True if the current thread should wait as a result of the wait | ||||||
|      */ |      */ | ||||||
|     virtual ResultVal<bool> WaitSynchronization() { |     virtual ResultVal<bool> WaitSynchronization(unsigned index=0) { | ||||||
|         LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); |         LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); | ||||||
|         return UnimplementedFunction(ErrorModule::Kernel); |         return UnimplementedFunction(ErrorModule::Kernel); | ||||||
|     } |     } | ||||||
|  | @ -111,10 +112,10 @@ public: | ||||||
|     void RemoveWaitingThread(Thread* thead); |     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 |      * @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
 |     /// Releases all threads waiting on this object
 | ||||||
|     void ReleaseAllWaitingThreads(); |     void ReleaseAllWaitingThreads(); | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ public: | ||||||
|     Handle lock_thread;                         ///< Handle to thread that currently has mutex
 |     Handle lock_thread;                         ///< Handle to thread that currently has mutex
 | ||||||
|     std::string name;                           ///< Name of mutex (optional)
 |     std::string name;                           ///< Name of mutex (optional)
 | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> WaitSynchronization() override; |     ResultVal<bool> WaitSynchronization(unsigned index) override; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | @ -50,7 +50,7 @@ void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandl | ||||||
|  */ |  */ | ||||||
| void ResumeWaitingThread(Mutex* mutex) { | void ResumeWaitingThread(Mutex* mutex) { | ||||||
|     // Find the next waiting thread for the mutex...
 |     // Find the next waiting thread for the mutex...
 | ||||||
|     auto next_thread = mutex->ResumeNextThread(); |     auto next_thread = mutex->ReleaseNextThread(); | ||||||
|     if (next_thread != nullptr) { |     if (next_thread != nullptr) { | ||||||
|         MutexAcquireLock(mutex, next_thread->GetHandle()); |         MutexAcquireLock(mutex, next_thread->GetHandle()); | ||||||
|     } else { |     } else { | ||||||
|  | @ -155,11 +155,11 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { | ||||||
|     return handle; |     return handle; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultVal<bool> Mutex::WaitSynchronization() { | ResultVal<bool> Mutex::WaitSynchronization(unsigned index) { | ||||||
|     bool wait = locked; |     bool wait = locked; | ||||||
|     if (locked) { |     if (locked) { | ||||||
|         AddWaitingThread(GetCurrentThread()); |         AddWaitingThread(GetCurrentThread()); | ||||||
|         Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this); |         Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_MUTEX, this, index); | ||||||
|     } else { |     } else { | ||||||
|         // Lock the mutex when the first thread accesses it
 |         // Lock the mutex when the first thread accesses it
 | ||||||
|         locked = true; |         locked = true; | ||||||
|  |  | ||||||
|  | @ -32,11 +32,11 @@ public: | ||||||
|         return available_count > 0; |         return available_count > 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> WaitSynchronization() override { |     ResultVal<bool> WaitSynchronization(unsigned index) override { | ||||||
|         bool wait = !IsAvailable(); |         bool wait = !IsAvailable(); | ||||||
| 
 | 
 | ||||||
|         if (wait) { |         if (wait) { | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_SEMA, this); |             Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_SEMA, this, index); | ||||||
|             AddWaitingThread(GetCurrentThread()); |             AddWaitingThread(GetCurrentThread()); | ||||||
|         } else { |         } else { | ||||||
|             --available_count; |             --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
 |     // 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
 |     // 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; |         --semaphore->available_count; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,11 +22,11 @@ | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| ResultVal<bool> Thread::WaitSynchronization() { | ResultVal<bool> Thread::WaitSynchronization(unsigned index) { | ||||||
|     const bool wait = status != THREADSTATUS_DORMANT; |     const bool wait = status != THREADSTATUS_DORMANT; | ||||||
|     if (wait) { |     if (wait) { | ||||||
|         AddWaitingThread(GetCurrentThread()); |         AddWaitingThread(GetCurrentThread()); | ||||||
|         WaitCurrentThread(WAITTYPE_THREADEND, this); |         WaitCurrentThread_WaitSynchronization(WAITTYPE_THREADEND, this, index); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return MakeResult<bool>(wait); |     return MakeResult<bool>(wait); | ||||||
|  | @ -92,11 +92,11 @@ static bool CheckWaitType(const Thread* thread, WaitType type) { | ||||||
| 
 | 
 | ||||||
| /// Check if a thread is blocking on a specified wait type with a specified handle
 | /// 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) { | 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); |     for (auto itr = thread->wait_objects.begin(); itr != thread->wait_objects.end(); ++itr) { | ||||||
|     if (itr == thread->wait_objects.end()) { |         if (itr->first == wait_object) | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|             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
 | /// 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); |     ChangeReadyState(this, false); | ||||||
|     status = THREADSTATUS_DORMANT; |     status = THREADSTATUS_DORMANT; | ||||||
|     ResumeAllWaitingThreads(); |     ReleaseAllWaitingThreads(); | ||||||
| 
 | 
 | ||||||
|     // Stopped threads are never waiting.
 |     // Stopped threads are never waiting.
 | ||||||
|     wait_type = WAITTYPE_NONE; |     wait_type = WAITTYPE_NONE; | ||||||
|  | @ -135,7 +135,7 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Arbitrate the highest priority thread that is waiting
 | /// 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; |     Thread* highest_priority_thread = nullptr; | ||||||
|     s32 priority = THREADPRIO_LOWEST; |     s32 priority = THREADPRIO_LOWEST; | ||||||
| 
 | 
 | ||||||
|  | @ -155,19 +155,19 @@ Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { | ||||||
| 
 | 
 | ||||||
|     // If a thread was arbitrated, resume it
 |     // If a thread was arbitrated, resume it
 | ||||||
|     if (nullptr != highest_priority_thread) { |     if (nullptr != highest_priority_thread) { | ||||||
|         highest_priority_thread->ResumeFromWait(); |         highest_priority_thread->ReleaseFromWait(arbiter); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return highest_priority_thread; |     return highest_priority_thread; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Arbitrate all threads currently waiting
 | /// 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...
 |     // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
 | ||||||
|     for (auto& thread : thread_list) { |     for (auto& thread : thread_list) { | ||||||
|         if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) |         if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) | ||||||
|             thread->ResumeFromWait(); |             thread->ReleaseFromWait(arbiter); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -220,19 +220,32 @@ static Thread* NextThread() { | ||||||
|     return next; |     return next; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void WaitCurrentThread(WaitType wait_type, WaitObject* wait_object) { | void WaitCurrentThread(WaitType wait_type) { | ||||||
|     Thread* thread = GetCurrentThread(); |     Thread* thread = GetCurrentThread(); | ||||||
|     thread->wait_type = wait_type; |     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))); |     ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void WaitCurrentThread(WaitType wait_type, WaitObject* wait_object, VAddr wait_address) { | void WaitCurrentThread_WaitSynchronization(WaitType wait_type, WaitObject* wait_object, unsigned index) { | ||||||
|     WaitCurrentThread(wait_type, wait_object); |     Thread* thread = GetCurrentThread(); | ||||||
|  |     thread->wait_type = wait_type; | ||||||
|  | 
 | ||||||
|  |     bool insert_wait_object = true; | ||||||
|  |     for (auto itr = thread->wait_objects.begin(); itr < thread->wait_objects.end(); ++itr) { | ||||||
|  |         if (itr->first == wait_object) { | ||||||
|  |             insert_wait_object = false; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (insert_wait_object) | ||||||
|  |         thread->wait_objects.push_back(std::pair<SharedPtr<WaitObject>, unsigned>(wait_object, index)); | ||||||
|  | 
 | ||||||
|  |     ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WaitCurrentThread_ArbitrateAddress(WaitObject* wait_object, VAddr wait_address) { | ||||||
|  |     WaitCurrentThread_WaitSynchronization(WaitType::WAITTYPE_ARB, wait_object, 0); | ||||||
|     GetCurrentThread()->wait_address = wait_address; |     GetCurrentThread()->wait_address = wait_address; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -248,6 +261,9 @@ static void ThreadWakeupCallback(u64 parameter, int cycles_late) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     thread->SetReturnValue(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,  | ||||||
|  |         ErrorSummary::StatusChanged, ErrorLevel::Info), -1); | ||||||
|  | 
 | ||||||
|     thread->ResumeFromWait(); |     thread->ResumeFromWait(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -262,7 +278,40 @@ void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { | ||||||
|     CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); |     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() { | void Thread::ResumeFromWait() { | ||||||
|     // Cancel any outstanding wakeup events
 |     // Cancel any outstanding wakeup events
 | ||||||
|     CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); |     CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); | ||||||
|  | @ -271,11 +320,12 @@ void Thread::ResumeFromWait() { | ||||||
| 
 | 
 | ||||||
|     // Remove this thread from all other WaitObjects
 |     // Remove this thread from all other WaitObjects
 | ||||||
|     for (auto wait_object : wait_objects) |     for (auto wait_object : wait_objects) | ||||||
|         wait_object->RemoveWaitingThread(this); |         wait_object.first->RemoveWaitingThread(this); | ||||||
| 
 | 
 | ||||||
|     wait_objects.clear(); |     wait_objects.clear(); | ||||||
| 
 | 
 | ||||||
|     wait_type = WAITTYPE_NONE; |     wait_type = WAITTYPE_NONE; | ||||||
|  |     wait_all = false; | ||||||
|     if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { |     if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | ||||||
|         ChangeReadyState(this, true); |         ChangeReadyState(this, true); | ||||||
|     } |     } | ||||||
|  | @ -342,6 +392,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | ||||||
|     thread->initial_priority = thread->current_priority = priority; |     thread->initial_priority = thread->current_priority = priority; | ||||||
|     thread->processor_id = processor_id; |     thread->processor_id = processor_id; | ||||||
|     thread->wait_type = WAITTYPE_NONE; |     thread->wait_type = WAITTYPE_NONE; | ||||||
|  |     thread->wait_all = false; | ||||||
|     thread->wait_objects.clear(); |     thread->wait_objects.clear(); | ||||||
|     thread->wait_address = 0; |     thread->wait_address = 0; | ||||||
|     thread->name = std::move(name); |     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() { | void ThreadingInit() { | ||||||
|  |  | ||||||
|  | @ -70,7 +70,7 @@ public: | ||||||
|     inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } |     inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | ||||||
|     inline bool IsIdle() const { return idle; } |     inline bool IsIdle() const { return idle; } | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> WaitSynchronization() override; |     ResultVal<bool> WaitSynchronization(unsigned index) override; | ||||||
| 
 | 
 | ||||||
|     s32 GetPriority() const { return current_priority; } |     s32 GetPriority() const { return current_priority; } | ||||||
|     void SetPriority(s32 priority); |     void SetPriority(s32 priority); | ||||||
|  | @ -78,9 +78,29 @@ public: | ||||||
|     u32 GetThreadId() const { return thread_id; } |     u32 GetThreadId() const { return thread_id; } | ||||||
| 
 | 
 | ||||||
|     void Stop(const char* reason); |     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(); |     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; |     Core::ThreadContext context; | ||||||
| 
 | 
 | ||||||
|     u32 thread_id; |     u32 thread_id; | ||||||
|  | @ -96,7 +116,7 @@ public: | ||||||
|     s32 processor_id; |     s32 processor_id; | ||||||
| 
 | 
 | ||||||
|     WaitType wait_type; |     WaitType wait_type; | ||||||
|     std::vector<SharedPtr<WaitObject>> wait_objects; |     std::vector<std::pair<SharedPtr<WaitObject>, unsigned>> wait_objects; | ||||||
|     VAddr wait_address; |     VAddr wait_address; | ||||||
| 
 | 
 | ||||||
|     std::string name; |     std::string name; | ||||||
|  | @ -105,6 +125,8 @@ public: | ||||||
|     bool idle = false; |     bool idle = false; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |     bool wait_all = false; | ||||||
|  | 
 | ||||||
|     Thread() = default; |     Thread() = default; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -115,37 +137,41 @@ SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); | ||||||
| void Reschedule(); | void Reschedule(); | ||||||
| 
 | 
 | ||||||
| /// Arbitrate the highest priority thread that is waiting
 | /// 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...
 | /// Arbitrate all threads currently waiting...
 | ||||||
| void ArbitrateAllThreads(Object* arbiter, u32 address); | void ArbitrateAllThreads(WaitObject* arbiter, u32 address); | ||||||
| 
 | 
 | ||||||
| /// Gets the current thread
 | /// Gets the current thread
 | ||||||
| Thread* GetCurrentThread(); | 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_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. |  * Waits the current thread from a WaitSynchronization call | ||||||
|  * @param thread The thread to wake after the delay. |  * @param wait_type Type of wait | ||||||
|  * @param nanoseconds The time this thread will be allowed to sleep for. |  * @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 |  * Waits the current thread from an ArbitrateAddress call | ||||||
|  * @param wait_type Type of wait |  | ||||||
|  * @param wait_object Kernel object that we are waiting on |  * @param wait_object Kernel object that we are waiting on | ||||||
|  * @param wait_address Arbitration address used to resume from wait |  * @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, |  * Sets up the idle thread, this is a thread that is intended to never execute instructions, | ||||||
|  |  | ||||||
|  | @ -29,11 +29,11 @@ public: | ||||||
|     u64 initial_delay;                      ///< The delay until the timer fires for the first time
 |     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
 |     u64 interval_delay;                     ///< The delay until the timer fires after the first time
 | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> WaitSynchronization() override { |     ResultVal<bool> WaitSynchronization(unsigned index) override { | ||||||
|         bool wait = !signaled; |         bool wait = !signaled; | ||||||
|         if (wait) { |         if (wait) { | ||||||
|             AddWaitingThread(GetCurrentThread()); |             AddWaitingThread(GetCurrentThread()); | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_TIMER, this); |             Kernel::WaitCurrentThread_WaitSynchronization(WAITTYPE_TIMER, this, index); | ||||||
|         } |         } | ||||||
|         return MakeResult<bool>(wait); |         return MakeResult<bool>(wait); | ||||||
|     } |     } | ||||||
|  | @ -91,7 +91,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { | ||||||
|     timer->signaled = true; |     timer->signaled = true; | ||||||
| 
 | 
 | ||||||
|     // Resume all waiting threads
 |     // Resume all waiting threads
 | ||||||
|     timer->ResumeAllWaitingThreads(); |     timer->ReleaseAllWaitingThreads(); | ||||||
| 
 | 
 | ||||||
|     if (timer->reset_type == RESETTYPE_ONESHOT) |     if (timer->reset_type == RESETTYPE_ONESHOT) | ||||||
|         timer->signaled = false; |         timer->signaled = false; | ||||||
|  |  | ||||||
|  | @ -133,6 +133,9 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | ||||||
|     if (wait.Succeeded() && *wait) { |     if (wait.Succeeded() && *wait) { | ||||||
|         // Create an event to wake the thread up after the specified nanosecond delay has passed
 |         // Create an event to wake the thread up after the specified nanosecond delay has passed
 | ||||||
|         Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); |         Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); | ||||||
|  | 
 | ||||||
|  |         Kernel::GetCurrentThread()->SetWaitAll(false); | ||||||
|  | 
 | ||||||
|         HLE::Reschedule(__func__); |         HLE::Reschedule(__func__); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -140,45 +143,65 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Wait for the given handles to synchronize, timeout after the specified nanoseconds
 | /// 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, | static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { | ||||||
|     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
 |     while (handle_index < handle_count) { | ||||||
|     bool unlock_all = true; |         SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[handle_index]); | ||||||
|     bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
 |  | ||||||
| 
 |  | ||||||
|     LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", |  | ||||||
|         handle_count, (wait_all ? "true" : "false"), nano_seconds); |  | ||||||
| 
 |  | ||||||
|     // Iterate through each handle, synchronize kernel object
 |  | ||||||
|     for (s32 i = 0; i < handle_count; i++) { |  | ||||||
|         SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]); |  | ||||||
|         if (object == nullptr) |         if (object == nullptr) | ||||||
|             return InvalidHandle(ErrorModule::Kernel).raw; |             return InvalidHandle(ErrorModule::Kernel).raw; | ||||||
| 
 | 
 | ||||||
|         LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], |         ResultVal<bool> wait = object->WaitSynchronization(handle_index); | ||||||
|                 object->GetTypeName().c_str(), object->GetName().c_str()); |  | ||||||
| 
 | 
 | ||||||
|         // TODO(yuriks): Verify how the real function behaves when an error happens here
 |         wait_thread = (wait.Succeeded() && *wait); | ||||||
|         ResultVal<bool> wait_result = object->WaitSynchronization(); |  | ||||||
|         bool wait = wait_result.Succeeded() && *wait_result; |  | ||||||
| 
 | 
 | ||||||
|         if (!wait && !wait_all) { |         // If this object waited and we are waiting on all objects to synchronize
 | ||||||
|             *out = i; |         if (wait_thread && wait_all) { | ||||||
|             return RESULT_SUCCESS.raw; |             // Enforce later on that this thread does not continue
 | ||||||
|         } else { |             wait_all_succeeded = true; | ||||||
|             unlock_all = false; |  | ||||||
|         } |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     if (wait_all && unlock_all) { |         // If this object synchronized and we are not waiting on all objects to synchronize
 | ||||||
|         *out = handle_count; |         if (!wait_thread && !wait_all) | ||||||
|         return RESULT_SUCCESS.raw; |             // We're done, the thread will continue
 | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         handle_index++; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Check for next thread to schedule
 |     // 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__); |         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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 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; |     return RESULT_SUCCESS.raw; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue