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