Implement a workaround for a concurency issue on SMP systems:
A thread that just missed a semaphore and put itself into the sems notify queue could be enqueued into the run queue by release_sem_etc() of another CPU before the CPU running the thread had a chance to reschedule it. Therefore there is a timeframe where a thread can be running on one CPU and already be in the run queue again. In this case no other CPU may schedule this thread because then it would overwrite the threads' CPU pointer which kills the rest of the scheduler logic, smp_get_current_cpu() and everything that depends on that (like the kernel debugger). The more CPUs you have the easier this could happen, up to the point where it was always triggered during boot on my quad core system. The system would freeze and you could not enter the kernel debugger, because two CPUs thought they were the same and disabled each other through SMP communication. This makes booting my system stable and might fix the occasional hang on boot for other SMP systems with only 2 CPUs/cores. I've put a ToDo comment that details this above the workaround. Maybe we should fix this in another way. Reviews, comments and suggestions welcome ;-) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23721 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -183,37 +183,59 @@ scheduler_reschedule(void)
|
||||
nextThread = nextThread->queue_next;
|
||||
}
|
||||
} else {
|
||||
// select next thread from the run queue
|
||||
while (nextThread && nextThread->priority > B_IDLE_PRIORITY) {
|
||||
while (nextThread) {
|
||||
// select next thread from the run queue
|
||||
while (nextThread && nextThread->priority > B_IDLE_PRIORITY) {
|
||||
#if 0
|
||||
if (oldThread == nextThread && nextThread->was_yielded) {
|
||||
// ignore threads that called thread_yield() once
|
||||
nextThread->was_yielded = false;
|
||||
prevThread = nextThread;
|
||||
nextThread = nextThread->queue_next;
|
||||
}
|
||||
if (oldThread == nextThread && nextThread->was_yielded) {
|
||||
// ignore threads that called thread_yield() once
|
||||
nextThread->was_yielded = false;
|
||||
prevThread = nextThread;
|
||||
nextThread = nextThread->queue_next;
|
||||
}
|
||||
#endif
|
||||
|
||||
// always extract real time threads
|
||||
if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY)
|
||||
break;
|
||||
// always extract real time threads
|
||||
if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY)
|
||||
break;
|
||||
|
||||
// never skip last non-idle normal thread
|
||||
if (nextThread->queue_next && nextThread->queue_next->priority == B_IDLE_PRIORITY)
|
||||
break;
|
||||
// never skip last non-idle normal thread
|
||||
if (nextThread->queue_next && nextThread->queue_next->priority == B_IDLE_PRIORITY)
|
||||
break;
|
||||
|
||||
// skip normal threads sometimes (roughly 20%)
|
||||
if (_rand() > 0x1a00)
|
||||
break;
|
||||
// skip normal threads sometimes (roughly 20%)
|
||||
if (_rand() > 0x1a00)
|
||||
break;
|
||||
|
||||
// skip until next lower priority
|
||||
int32 priority = nextThread->priority;
|
||||
do {
|
||||
// skip until next lower priority
|
||||
int32 priority = nextThread->priority;
|
||||
do {
|
||||
prevThread = nextThread;
|
||||
nextThread = nextThread->queue_next;
|
||||
} while (nextThread->queue_next != NULL
|
||||
&& priority == nextThread->queue_next->priority
|
||||
&& nextThread->queue_next->priority > B_IDLE_PRIORITY);
|
||||
}
|
||||
|
||||
if (nextThread->cpu
|
||||
&& nextThread->cpu->cpu_num != oldThread->cpu->cpu_num) {
|
||||
// ToDo: This thread is still running on another CPU. The
|
||||
// thread just missed a semaphore, put itself into the notify
|
||||
// queue but was not yet rescheduled. During this time frame
|
||||
// release_sem_etc() was called for said semaphore and put the
|
||||
// thread into the run queue to notify it.
|
||||
// Therefore it is now _still_ running on one CPU and _already_
|
||||
// part of the run queue again. We have to skip this thread
|
||||
// here because otherwise we would overwrite the thread->cpu
|
||||
// pointer with the current CPU which would make both CPUs
|
||||
// "think" they are the same one and kill off the scheduler
|
||||
// logic in here as well as all calls to smp_get_current_cpu().
|
||||
prevThread = nextThread;
|
||||
nextThread = nextThread->queue_next;
|
||||
} while (nextThread->queue_next != NULL
|
||||
&& priority == nextThread->queue_next->priority
|
||||
&& nextThread->queue_next->priority > B_IDLE_PRIORITY);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user