forked from suyu/suyu
kernel: fix incorrect locking order in suspension
This commit is contained in:
parent
78df1ddce8
commit
5086380a63
3 changed files with 26 additions and 34 deletions
|
@ -763,19 +763,6 @@ void KThread::Continue() {
|
||||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
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) {
|
Result KThread::SetActivity(Svc::ThreadActivity activity) {
|
||||||
// Lock ourselves.
|
// Lock ourselves.
|
||||||
KScopedLightLock lk(activity_pause_lock);
|
KScopedLightLock lk(activity_pause_lock);
|
||||||
|
|
|
@ -214,8 +214,6 @@ public:
|
||||||
|
|
||||||
void Continue();
|
void Continue();
|
||||||
|
|
||||||
void WaitUntilSuspended();
|
|
||||||
|
|
||||||
constexpr void SetSyncedIndex(s32 index) {
|
constexpr void SetSyncedIndex(s32 index) {
|
||||||
synced_index = index;
|
synced_index = index;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1198,27 +1198,34 @@ void KernelCore::Suspend(bool suspended) {
|
||||||
const bool should_suspend{exception_exited || suspended};
|
const bool should_suspend{exception_exited || suspended};
|
||||||
const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
|
const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
|
||||||
|
|
||||||
std::vector<KScopedAutoObject<KThread>> process_threads;
|
//! This refers to the application process, not the current process.
|
||||||
{
|
KScopedAutoObject<KProcess> process = CurrentProcess();
|
||||||
KScopedSchedulerLock sl{*this};
|
if (process.IsNull()) {
|
||||||
|
|
||||||
if (auto* process = CurrentProcess(); process != nullptr) {
|
|
||||||
process->SetActivity(activity);
|
|
||||||
|
|
||||||
if (!should_suspend) {
|
|
||||||
// Runnable now; no need to wait.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto* thread : process->GetThreadList()) {
|
// Set the new activity.
|
||||||
process_threads.emplace_back(thread);
|
process->SetActivity(activity);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for execution to stop.
|
// Wait for process execution to stop.
|
||||||
for (auto& thread : process_threads) {
|
bool must_wait{should_suspend};
|
||||||
thread->WaitUntilSuspended();
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue