kernel: fix incorrect locking order in suspension
This commit is contained in:
		@@ -763,19 +763,6 @@ void KThread::Continue() {
 | 
			
		||||
    KScheduler::OnThreadStateChanged(kernel, this, old_state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KThread::WaitUntilSuspended() {
 | 
			
		||||
    // Make sure we have a suspend requested.
 | 
			
		||||
    ASSERT(IsSuspendRequested());
 | 
			
		||||
 | 
			
		||||
    // Loop until the thread is not executing on any core.
 | 
			
		||||
    for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
 | 
			
		||||
        KThread* core_thread{};
 | 
			
		||||
        do {
 | 
			
		||||
            core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
 | 
			
		||||
        } while (core_thread == this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result KThread::SetActivity(Svc::ThreadActivity activity) {
 | 
			
		||||
    // Lock ourselves.
 | 
			
		||||
    KScopedLightLock lk(activity_pause_lock);
 | 
			
		||||
 
 | 
			
		||||
@@ -214,8 +214,6 @@ public:
 | 
			
		||||
 | 
			
		||||
    void Continue();
 | 
			
		||||
 | 
			
		||||
    void WaitUntilSuspended();
 | 
			
		||||
 | 
			
		||||
    constexpr void SetSyncedIndex(s32 index) {
 | 
			
		||||
        synced_index = index;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1198,27 +1198,34 @@ void KernelCore::Suspend(bool suspended) {
 | 
			
		||||
    const bool should_suspend{exception_exited || suspended};
 | 
			
		||||
    const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
 | 
			
		||||
 | 
			
		||||
    std::vector<KScopedAutoObject<KThread>> process_threads;
 | 
			
		||||
    {
 | 
			
		||||
        KScopedSchedulerLock sl{*this};
 | 
			
		||||
 | 
			
		||||
        if (auto* process = CurrentProcess(); process != nullptr) {
 | 
			
		||||
            process->SetActivity(activity);
 | 
			
		||||
 | 
			
		||||
            if (!should_suspend) {
 | 
			
		||||
                // Runnable now; no need to wait.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (auto* thread : process->GetThreadList()) {
 | 
			
		||||
                process_threads.emplace_back(thread);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    //! This refers to the application process, not the current process.
 | 
			
		||||
    KScopedAutoObject<KProcess> process = CurrentProcess();
 | 
			
		||||
    if (process.IsNull()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Wait for execution to stop.
 | 
			
		||||
    for (auto& thread : process_threads) {
 | 
			
		||||
        thread->WaitUntilSuspended();
 | 
			
		||||
    // Set the new activity.
 | 
			
		||||
    process->SetActivity(activity);
 | 
			
		||||
 | 
			
		||||
    // Wait for process execution to stop.
 | 
			
		||||
    bool must_wait{should_suspend};
 | 
			
		||||
 | 
			
		||||
    // KernelCore::Suspend must be called from locked context, or we
 | 
			
		||||
    // could race another call to SetActivity, interfering with waiting.
 | 
			
		||||
    while (must_wait) {
 | 
			
		||||
        KScopedSchedulerLock sl{*this};
 | 
			
		||||
 | 
			
		||||
        // Assume that all threads have finished running.
 | 
			
		||||
        must_wait = false;
 | 
			
		||||
 | 
			
		||||
        for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
 | 
			
		||||
            if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() ==
 | 
			
		||||
                process.GetPointerUnsafe()) {
 | 
			
		||||
                // A thread has not finished running yet.
 | 
			
		||||
                // Continue waiting.
 | 
			
		||||
                must_wait = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user