Split 335 2/7 Fix/kernel scheduling races (#329)
This commit is contained in:
parent
b9c233adef
commit
4e3a9cbbdb
|
|
@ -209,7 +209,7 @@ void KScheduler::OnThreadStart()
|
|||
|
||||
u64 KScheduler::UpdateHighestPriorityThread(KThread * highest_thread)
|
||||
{
|
||||
if (KThread * prev_highest_thread = m_state.highest_priority_thread;
|
||||
if (KThread * prev_highest_thread = m_state.highest_priority_thread.load(std::memory_order_acquire);
|
||||
prev_highest_thread != highest_thread) [[likely]]
|
||||
{
|
||||
if (prev_highest_thread != nullptr) [[likely]]
|
||||
|
|
@ -233,7 +233,7 @@ u64 KScheduler::UpdateHighestPriorityThread(KThread * highest_thread)
|
|||
}
|
||||
}
|
||||
|
||||
m_state.highest_priority_thread = highest_thread;
|
||||
m_state.highest_priority_thread.store(highest_thread, std::memory_order_release);
|
||||
m_state.needs_scheduling = true;
|
||||
return (1ULL << m_core_id);
|
||||
}
|
||||
|
|
@ -458,7 +458,7 @@ void KScheduler::ScheduleImpl()
|
|||
|
||||
// Load the appropriate thread pointers for scheduling.
|
||||
KThread * const cur_thread{GetCurrentThreadPointer(m_kernel)};
|
||||
KThread * highest_priority_thread{m_state.highest_priority_thread};
|
||||
KThread * highest_priority_thread{m_state.highest_priority_thread.load(std::memory_order_acquire)};
|
||||
|
||||
// Check whether there are runnable interrupt tasks.
|
||||
if (m_state.interrupt_task_runnable)
|
||||
|
|
@ -562,7 +562,7 @@ void KScheduler::ScheduleImplFiber()
|
|||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
// Refresh the highest priority thread.
|
||||
highest_priority_thread = m_state.highest_priority_thread;
|
||||
highest_priority_thread = m_state.highest_priority_thread.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
// Reload the guest thread context.
|
||||
|
|
@ -794,12 +794,6 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore & kernel)
|
|||
KThread & cur_thread = GetCurrentThread(kernel);
|
||||
KProcess & cur_process = GetCurrentProcess(kernel);
|
||||
|
||||
// If the thread's yield count matches, there's nothing for us to do.
|
||||
if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a reference to the priority queue.
|
||||
auto & priority_queue = GetPriorityQueue(kernel);
|
||||
|
||||
|
|
@ -807,6 +801,12 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore & kernel)
|
|||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// The process count is shared by all cores and changes under this lock.
|
||||
if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto cur_state = cur_thread.GetRawState();
|
||||
if (cur_state == ThreadState::Runnable)
|
||||
{
|
||||
|
|
@ -839,12 +839,6 @@ void KScheduler::YieldWithCoreMigration(KernelCore & kernel)
|
|||
KThread & cur_thread = GetCurrentThread(kernel);
|
||||
KProcess & cur_process = GetCurrentProcess(kernel);
|
||||
|
||||
// If the thread's yield count matches, there's nothing for us to do.
|
||||
if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a reference to the priority queue.
|
||||
auto & priority_queue = GetPriorityQueue(kernel);
|
||||
|
||||
|
|
@ -852,6 +846,12 @@ void KScheduler::YieldWithCoreMigration(KernelCore & kernel)
|
|||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// The process count is shared by all cores and changes under this lock.
|
||||
if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto cur_state = cur_thread.GetRawState();
|
||||
if (cur_state == ThreadState::Runnable)
|
||||
{
|
||||
|
|
@ -872,7 +872,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore & kernel)
|
|||
|
||||
if (KThread * running_on_suggested_core =
|
||||
(suggested_core >= 0)
|
||||
? kernel.Scheduler(suggested_core).m_state.highest_priority_thread
|
||||
? kernel.Scheduler(suggested_core).m_state.highest_priority_thread.load(std::memory_order_acquire)
|
||||
: nullptr;
|
||||
running_on_suggested_core != suggested)
|
||||
{
|
||||
|
|
@ -939,12 +939,6 @@ void KScheduler::YieldToAnyThread(KernelCore & kernel)
|
|||
KThread & cur_thread = GetCurrentThread(kernel);
|
||||
KProcess & cur_process = GetCurrentProcess(kernel);
|
||||
|
||||
// If the thread's yield count matches, there's nothing for us to do.
|
||||
if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a reference to the priority queue.
|
||||
auto & priority_queue = GetPriorityQueue(kernel);
|
||||
|
||||
|
|
@ -952,6 +946,12 @@ void KScheduler::YieldToAnyThread(KernelCore & kernel)
|
|||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// The process count is shared by all cores and changes under this lock.
|
||||
if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto cur_state = cur_thread.GetRawState();
|
||||
if (cur_state == ThreadState::Runnable)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ private:
|
|||
bool interrupt_task_runnable{false};
|
||||
bool should_count_idle{false};
|
||||
u64 idle_count{0};
|
||||
KThread* highest_priority_thread{nullptr};
|
||||
std::atomic<KThread*> highest_priority_thread{nullptr};
|
||||
void* idle_thread_stack{nullptr};
|
||||
std::atomic<KThread*> prev_thread{nullptr};
|
||||
KInterruptTaskManager* interrupt_task_manager{nullptr};
|
||||
|
|
|
|||
|
|
@ -690,11 +690,19 @@ Result KThread::SetCoreMask(s32 core_id, u64 v_affinity_mask) {
|
|||
// Check if the thread is currently running.
|
||||
bool thread_is_current{};
|
||||
s32 thread_core;
|
||||
for (thread_core = 0; thread_core < static_cast<s32>(Hardware::NUM_CPU_CORES);
|
||||
++thread_core) {
|
||||
if (m_kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) {
|
||||
thread_is_current = true;
|
||||
break;
|
||||
if (!m_kernel.IsMulticore() && m_kernel.CurrentScheduler() != nullptr &&
|
||||
this == GetCurrentThreadPointer(m_kernel)) {
|
||||
// In SC, inactive schedulers may still reference the caller after preemption.
|
||||
// Only its executing core matters; it already owns its context lock there.
|
||||
thread_core = static_cast<s32>(m_kernel.CurrentPhysicalCoreIndex());
|
||||
thread_is_current = true;
|
||||
} else {
|
||||
for (thread_core = 0; thread_core < static_cast<s32>(Hardware::NUM_CPU_CORES);
|
||||
++thread_core) {
|
||||
if (m_kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) {
|
||||
thread_is_current = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -710,6 +718,13 @@ Result KThread::SetCoreMask(s32 core_id, u64 v_affinity_mask) {
|
|||
// Wait until the thread isn't pinned any more.
|
||||
m_pinned_waiter_list.push_back(GetCurrentThread(m_kernel));
|
||||
GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
|
||||
} else if (!m_kernel.IsMulticore() && m_kernel.CurrentScheduler() != nullptr &&
|
||||
thread_core != static_cast<s32>(m_kernel.CurrentPhysicalCoreIndex())) {
|
||||
// SC preemption saves/unlocks a thread's context but leaves the virtual
|
||||
// scheduler's current pointer in place. The inactive virtual core is already
|
||||
// quiescent; waiting for its scheduler to run would block the sole CPU thread.
|
||||
std::unique_lock context_lock{m_context_guard, std::try_to_lock};
|
||||
retry_update = !context_lock.owns_lock();
|
||||
} else {
|
||||
// If the thread isn't pinned, release the scheduler lock and retry until it's
|
||||
// not current.
|
||||
|
|
|
|||
|
|
@ -238,12 +238,14 @@ void PhysicalCore::LogBacktrace()
|
|||
void PhysicalCore::Idle()
|
||||
{
|
||||
std::unique_lock lk{m_guard};
|
||||
m_on_interrupt.wait(lk, [this] { return m_is_interrupted; });
|
||||
m_on_interrupt.wait(lk, [this] {
|
||||
return m_is_interrupted.load(std::memory_order_acquire);
|
||||
});
|
||||
}
|
||||
|
||||
bool PhysicalCore::IsInterrupted() const
|
||||
{
|
||||
return m_is_interrupted;
|
||||
return m_is_interrupted.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void PhysicalCore::Interrupt()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
|
@ -74,7 +75,7 @@ private:
|
|||
std::condition_variable m_on_interrupt;
|
||||
ICpuCore * m_cpucore{};
|
||||
KThread * m_current_thread{};
|
||||
bool m_is_interrupted{};
|
||||
std::atomic_bool m_is_interrupted{};
|
||||
bool m_is_single_core{};
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue