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