Merge pull request #2416 from lioncash/wait
kernel/svc: Clean up wait synchronization related functionality
This commit is contained in:
		@@ -46,8 +46,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
 | 
			
		||||
 | 
			
		||||
    bool resume = true;
 | 
			
		||||
 | 
			
		||||
    if (thread->GetStatus() == ThreadStatus::WaitSynchAny ||
 | 
			
		||||
        thread->GetStatus() == ThreadStatus::WaitSynchAll ||
 | 
			
		||||
    if (thread->GetStatus() == ThreadStatus::WaitSynch ||
 | 
			
		||||
        thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
 | 
			
		||||
        // Remove the thread from each of its waiting objects' waitlists
 | 
			
		||||
        for (const auto& object : thread->GetWaitObjects()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -147,8 +147,7 @@ void Process::PrepareForTermination() {
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            // TODO(Subv): When are the other running/ready threads terminated?
 | 
			
		||||
            ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny ||
 | 
			
		||||
                           thread->GetStatus() == ThreadStatus::WaitSynchAll,
 | 
			
		||||
            ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch,
 | 
			
		||||
                       "Exiting processes with non-waiting threads is currently unimplemented");
 | 
			
		||||
 | 
			
		||||
            thread->Stop();
 | 
			
		||||
 
 | 
			
		||||
@@ -424,7 +424,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
 | 
			
		||||
/// Default thread wakeup callback for WaitSynchronization
 | 
			
		||||
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
 | 
			
		||||
                                        SharedPtr<WaitObject> object, std::size_t index) {
 | 
			
		||||
    ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
 | 
			
		||||
    ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
 | 
			
		||||
 | 
			
		||||
    if (reason == ThreadWakeupReason::Timeout) {
 | 
			
		||||
        thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
 | 
			
		||||
@@ -502,7 +502,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    thread->SetWaitObjects(std::move(objects));
 | 
			
		||||
    thread->SetStatus(ThreadStatus::WaitSynchAny);
 | 
			
		||||
    thread->SetStatus(ThreadStatus::WaitSynch);
 | 
			
		||||
 | 
			
		||||
    // Create an event to wake the thread up after the specified nanosecond delay has passed
 | 
			
		||||
    thread->WakeAfterDelay(nano_seconds);
 | 
			
		||||
@@ -518,16 +518,14 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand
 | 
			
		||||
    LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
 | 
			
		||||
 | 
			
		||||
    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
 | 
			
		||||
    const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
 | 
			
		||||
    SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
 | 
			
		||||
    if (!thread) {
 | 
			
		||||
        LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
 | 
			
		||||
                  thread_handle);
 | 
			
		||||
        return ERR_INVALID_HANDLE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
 | 
			
		||||
    thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
 | 
			
		||||
    thread->ResumeFromWait();
 | 
			
		||||
    thread->CancelWait();
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -101,8 +101,7 @@ void Thread::ResumeFromWait() {
 | 
			
		||||
    ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
 | 
			
		||||
 | 
			
		||||
    switch (status) {
 | 
			
		||||
    case ThreadStatus::WaitSynchAll:
 | 
			
		||||
    case ThreadStatus::WaitSynchAny:
 | 
			
		||||
    case ThreadStatus::WaitSynch:
 | 
			
		||||
    case ThreadStatus::WaitHLEEvent:
 | 
			
		||||
    case ThreadStatus::WaitSleep:
 | 
			
		||||
    case ThreadStatus::WaitIPC:
 | 
			
		||||
@@ -142,6 +141,12 @@ void Thread::ResumeFromWait() {
 | 
			
		||||
    ChangeScheduler();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Thread::CancelWait() {
 | 
			
		||||
    ASSERT(GetStatus() == ThreadStatus::WaitSynch);
 | 
			
		||||
    SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
 | 
			
		||||
    ResumeFromWait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Resets a thread context, making it ready to be scheduled and run by the CPU
 | 
			
		||||
 * @param context Thread context to reset
 | 
			
		||||
 
 | 
			
		||||
@@ -49,8 +49,7 @@ enum class ThreadStatus {
 | 
			
		||||
    WaitHLEEvent, ///< Waiting for hle event to finish
 | 
			
		||||
    WaitSleep,    ///< Waiting due to a SleepThread SVC
 | 
			
		||||
    WaitIPC,      ///< Waiting for the reply from an IPC request
 | 
			
		||||
    WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
 | 
			
		||||
    WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
 | 
			
		||||
    WaitSynch,    ///< Waiting due to WaitSynchronization
 | 
			
		||||
    WaitMutex,    ///< Waiting due to an ArbitrateLock svc
 | 
			
		||||
    WaitCondVar,  ///< Waiting due to an WaitProcessWideKey svc
 | 
			
		||||
    WaitArb,      ///< Waiting due to a SignalToAddress/WaitForAddress svc
 | 
			
		||||
@@ -169,11 +168,17 @@ public:
 | 
			
		||||
        return tls_memory;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resumes a thread from waiting
 | 
			
		||||
     */
 | 
			
		||||
    /// Resumes a thread from waiting
 | 
			
		||||
    void ResumeFromWait();
 | 
			
		||||
 | 
			
		||||
    /// Cancels a waiting operation that this thread may or may not be within.
 | 
			
		||||
    ///
 | 
			
		||||
    /// When the thread is within a waiting state, this will set the thread's
 | 
			
		||||
    /// waiting result to signal a canceled wait. The function will then resume
 | 
			
		||||
    /// this thread.
 | 
			
		||||
    ///
 | 
			
		||||
    void CancelWait();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Schedules an event to wake up the specified thread after the specified delay
 | 
			
		||||
     * @param nanoseconds The time this thread will be allowed to sleep for
 | 
			
		||||
@@ -184,24 +189,27 @@ public:
 | 
			
		||||
    void CancelWakeupTimer();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the result after the thread awakens (from either WaitSynchronization SVC)
 | 
			
		||||
     * Sets the result after the thread awakens (from svcWaitSynchronization)
 | 
			
		||||
     * @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)
 | 
			
		||||
     * Sets the output parameter value after the thread awakens (from svcWaitSynchronization)
 | 
			
		||||
     * @param output Value to set to the output parameter
 | 
			
		||||
     */
 | 
			
		||||
    void SetWaitSynchronizationOutput(s32 output);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the index that this particular object occupies in the list of objects
 | 
			
		||||
     * that the thread passed to WaitSynchronizationN, starting the search from the last element.
 | 
			
		||||
     * It is used to set the output value of WaitSynchronizationN when the thread is awakened.
 | 
			
		||||
     * that the thread passed to WaitSynchronization, starting the search from the last element.
 | 
			
		||||
     *
 | 
			
		||||
     * It is used to set the output index of WaitSynchronization when the thread is awakened.
 | 
			
		||||
     *
 | 
			
		||||
     * When a thread wakes up due to an object signal, the kernel will use the index of the last
 | 
			
		||||
     * matching object in the wait objects list in case of having multiple instances of the same
 | 
			
		||||
     * object in the list.
 | 
			
		||||
     *
 | 
			
		||||
     * @param object Object to query the index of.
 | 
			
		||||
     */
 | 
			
		||||
    s32 GetWaitObjectIndex(const WaitObject* object) const;
 | 
			
		||||
@@ -238,13 +246,9 @@ public:
 | 
			
		||||
     */
 | 
			
		||||
    VAddr GetCommandBufferAddress() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns whether this thread is waiting for all the objects in
 | 
			
		||||
     * its wait list to become ready, as a result of a WaitSynchronizationN call
 | 
			
		||||
     * with wait_all = true.
 | 
			
		||||
     */
 | 
			
		||||
    bool IsSleepingOnWaitAll() const {
 | 
			
		||||
        return status == ThreadStatus::WaitSynchAll;
 | 
			
		||||
    /// Returns whether this thread is waiting on objects from a WaitSynchronization call.
 | 
			
		||||
    bool IsSleepingOnWait() const {
 | 
			
		||||
        return status == ThreadStatus::WaitSynch;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ThreadContext& GetContext() {
 | 
			
		||||
@@ -418,7 +422,7 @@ private:
 | 
			
		||||
    Process* owner_process;
 | 
			
		||||
 | 
			
		||||
    /// Objects that the thread is waiting on, in the same order as they were
 | 
			
		||||
    /// passed to WaitSynchronization1/N.
 | 
			
		||||
    /// passed to WaitSynchronization.
 | 
			
		||||
    ThreadWaitObjects wait_objects;
 | 
			
		||||
 | 
			
		||||
    /// List of threads that are waiting for a mutex that is held by this thread.
 | 
			
		||||
@@ -441,7 +445,7 @@ private:
 | 
			
		||||
    Handle callback_handle = 0;
 | 
			
		||||
 | 
			
		||||
    /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
 | 
			
		||||
    /// was waiting via WaitSynchronizationN then the object will be the last object that became
 | 
			
		||||
    /// was waiting via WaitSynchronization then the object will be the last object that became
 | 
			
		||||
    /// available. In case of a timeout, the object will be nullptr.
 | 
			
		||||
    WakeupCallback wakeup_callback;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -38,8 +38,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
 | 
			
		||||
        const ThreadStatus thread_status = thread->GetStatus();
 | 
			
		||||
 | 
			
		||||
        // The list of waiting threads must not contain threads that are not waiting to be awakened.
 | 
			
		||||
        ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny ||
 | 
			
		||||
                       thread_status == ThreadStatus::WaitSynchAll ||
 | 
			
		||||
        ASSERT_MSG(thread_status == ThreadStatus::WaitSynch ||
 | 
			
		||||
                       thread_status == ThreadStatus::WaitHLEEvent,
 | 
			
		||||
                   "Inconsistent thread statuses in waiting_threads");
 | 
			
		||||
 | 
			
		||||
@@ -49,10 +48,10 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
 | 
			
		||||
        if (ShouldWait(thread.get()))
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        // A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or
 | 
			
		||||
        // in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready.
 | 
			
		||||
        // A thread is ready to run if it's either in ThreadStatus::WaitSynch
 | 
			
		||||
        // and the rest of the objects it is waiting on are ready.
 | 
			
		||||
        bool ready_to_run = true;
 | 
			
		||||
        if (thread_status == ThreadStatus::WaitSynchAll) {
 | 
			
		||||
        if (thread_status == ThreadStatus::WaitSynch) {
 | 
			
		||||
            ready_to_run = thread->AllWaitObjectsReady();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -68,33 +67,35 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
 | 
			
		||||
void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
 | 
			
		||||
    ASSERT(!ShouldWait(thread.get()));
 | 
			
		||||
 | 
			
		||||
    if (!thread)
 | 
			
		||||
    if (!thread) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!thread->IsSleepingOnWaitAll()) {
 | 
			
		||||
        Acquire(thread.get());
 | 
			
		||||
    } else {
 | 
			
		||||
    if (thread->IsSleepingOnWait()) {
 | 
			
		||||
        for (const auto& object : thread->GetWaitObjects()) {
 | 
			
		||||
            ASSERT(!object->ShouldWait(thread.get()));
 | 
			
		||||
            object->Acquire(thread.get());
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        Acquire(thread.get());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const std::size_t index = thread->GetWaitObjectIndex(this);
 | 
			
		||||
 | 
			
		||||
    for (const auto& object : thread->GetWaitObjects())
 | 
			
		||||
    for (const auto& object : thread->GetWaitObjects()) {
 | 
			
		||||
        object->RemoveWaitingThread(thread.get());
 | 
			
		||||
    }
 | 
			
		||||
    thread->ClearWaitObjects();
 | 
			
		||||
 | 
			
		||||
    thread->CancelWakeupTimer();
 | 
			
		||||
 | 
			
		||||
    bool resume = true;
 | 
			
		||||
 | 
			
		||||
    if (thread->HasWakeupCallback())
 | 
			
		||||
    if (thread->HasWakeupCallback()) {
 | 
			
		||||
        resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index);
 | 
			
		||||
 | 
			
		||||
    if (resume)
 | 
			
		||||
    }
 | 
			
		||||
    if (resume) {
 | 
			
		||||
        thread->ResumeFromWait();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WaitObject::WakeupAllWaitingThreads() {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user