kernel/scheduler: Always reschedule after enqueueing if the runqueue was empty.
Normally the heap's priority will suffice to check if we need to reschedule. However, in cases where the CPU in question is currently in the middle of rescheduling already, and it is about to change its priority to "idle", we can race with it and not notice that we need to send it an ICI. Previously this meant we would just lose some performance, but after recent fixes to not reschedule only as necessary, this race led to hangs. Now we report whether the runqueue we added the thread to was empty in ThreadData::Enqueue(), and if it was, we then always trigger a scheduler invocation on the target CPU. In my testing, this fixes #17847; and at least in my unscientific benchmarks, improves compile performance by as much as 10% (I saw ~55s -> ~50s in some tests.)
This commit is contained in:
@@ -108,19 +108,20 @@ enqueue(Thread* thread, bool newOne)
|
||||
ASSERT(thread->previous_cpu != NULL);
|
||||
ASSERT(threadData->Core() != NULL);
|
||||
targetCPU = &gCPUEntries[thread->previous_cpu->cpu_num];
|
||||
} else if (gSingleCore)
|
||||
} else if (gSingleCore) {
|
||||
targetCore = &gCoreEntries[0];
|
||||
else if (threadData->Core() != NULL
|
||||
} else if (threadData->Core() != NULL
|
||||
&& (!newOne || !threadData->HasCacheExpired())) {
|
||||
targetCore = threadData->Rebalance();
|
||||
}
|
||||
|
||||
bool rescheduleNeeded = threadData->ChooseCoreAndCPU(targetCore, targetCPU);
|
||||
const bool rescheduleNeeded = threadData->ChooseCoreAndCPU(targetCore, targetCPU);
|
||||
|
||||
TRACE("enqueueing thread %ld with priority %ld on CPU %ld (core %ld)\n",
|
||||
thread->id, threadPriority, targetCPU->ID(), targetCore->ID());
|
||||
|
||||
threadData->Enqueue();
|
||||
bool wasRunQueueEmpty = false;
|
||||
threadData->Enqueue(wasRunQueueEmpty);
|
||||
|
||||
// notify listeners
|
||||
NotifySchedulerListeners(&SchedulerListener::ThreadEnqueuedInRunQueue,
|
||||
@@ -128,11 +129,12 @@ enqueue(Thread* thread, bool newOne)
|
||||
|
||||
int32 heapPriority = CPUPriorityHeap::GetKey(targetCPU);
|
||||
if (threadPriority > heapPriority
|
||||
|| (threadPriority == heapPriority && rescheduleNeeded)) {
|
||||
|| (threadPriority == heapPriority && rescheduleNeeded)
|
||||
|| wasRunQueueEmpty) {
|
||||
|
||||
if (targetCPU->ID() == smp_get_current_cpu())
|
||||
if (targetCPU->ID() == smp_get_current_cpu()) {
|
||||
gCPU[targetCPU->ID()].invoke_scheduler = true;
|
||||
else {
|
||||
} else {
|
||||
smp_send_ici(targetCPU->ID(), SMP_MSG_RESCHEDULE, 0, 0, 0,
|
||||
NULL, SMP_MSG_FLAG_ASYNC);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
inline bigtime_t WentSleepActive() const { return fWentSleepActive; }
|
||||
|
||||
inline void PutBack();
|
||||
inline void Enqueue();
|
||||
inline void Enqueue(bool& wasRunQueueEmpty);
|
||||
inline bool Dequeue();
|
||||
|
||||
inline void UpdateActivity(bigtime_t active);
|
||||
@@ -406,7 +406,7 @@ ThreadData::PutBack()
|
||||
|
||||
|
||||
inline void
|
||||
ThreadData::Enqueue()
|
||||
ThreadData::Enqueue(bool& wasRunQueueEmpty)
|
||||
{
|
||||
SCHEDULER_ENTER_FUNCTION();
|
||||
|
||||
@@ -427,8 +427,7 @@ ThreadData::Enqueue()
|
||||
|
||||
fThread->state = B_THREAD_READY;
|
||||
|
||||
int32 priority = GetEffectivePriority();
|
||||
|
||||
const int32 priority = GetEffectivePriority();
|
||||
if (fThread->pinned_to_cpu > 0) {
|
||||
ASSERT(fThread->previous_cpu != NULL);
|
||||
CPUEntry* cpu = CPUEntry::GetCPU(fThread->previous_cpu->cpu_num);
|
||||
@@ -437,12 +436,18 @@ ThreadData::Enqueue()
|
||||
ASSERT(!fEnqueued);
|
||||
fEnqueued = true;
|
||||
|
||||
ThreadData* top = cpu->PeekThread();
|
||||
wasRunQueueEmpty = (top == NULL || top->IsIdle());
|
||||
|
||||
cpu->PushBack(this, priority);
|
||||
} else {
|
||||
CoreRunQueueLocker _(fCore);
|
||||
ASSERT(!fEnqueued);
|
||||
fEnqueued = true;
|
||||
|
||||
ThreadData* top = fCore->PeekThread();
|
||||
wasRunQueueEmpty = (top == NULL || top->IsIdle());
|
||||
|
||||
fCore->PushBack(this, priority);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user