Nucleus
Barry Fixed scheduler double-schedule bug 6063f66 (3 years, 2 months ago)
/*
* This file contains the scheduler. It implements a basic task switching
* routine, as well as the schedule() function. The scheduler can be called
* from anywhere, and will switch to the next task decided by the scheduler
* rules. If it cannot find a task to schedule, it just idles until one becomes
* available. This avoids the need for an idle task.
*/
#include <nucleus/kernel.h>
#include <nucleus/task.h>
#define PRIORITY_COUNT 6
ObjectList *readyQueue[PRIORITY_COUNT];
/* Switch to a task */
static void
switch_to_task(Task *task)
{
/* Save current task state */
if (__builtin_expect(!!current, 1)) {
lock(current);
asm volatile("mov %%esp, %0" : "=r" (current->esp));
asm volatile("mov %%ebp, %0" : "=r" (current->ebp));
current->eip = (uintptr_t) &&end;
unlock(current);
put(current);
}
/* Switch to new context */
uintptr_t eip, esp, ebp;
current = task; /* Given reference, so no get() */
eip = current->eip;
esp = current->esp;
ebp = current->ebp;
asm volatile (
"cli;"
"movl %0, %%ecx;"
"movl %1, %%esp;"
"movl %2, %%ebp;"
"movl %3, %%cr3;"
"sti;"
"jmp *%%ecx"
:: "g" (eip), "g" (esp),
"g" (ebp), "g" (current->pageDir)
);
end:
/* This prevents GCC from optimising the jump to be after the return */
asm volatile("":::"memory");
}
/* Find the next schedulable ready queue */
static ObjectList *
highest_priority_queue(void)
{
enum Priority p;
for (p = PRIORITY_COUNT - 1; p > 0; p--) {
if (count(readyQueue[p]))
return readyQueue[p];
}
return NULL;
}
/* Schedule the next task */
void
schedule(void)
{
if (current && current->inCriticalSection)
return;
Task *task = current;
ObjectList *queue = highest_priority_queue();
/* Idle if necessary */
if (!queue) {
if (current && current->state == RUNNING)
return;
current = NULL;
asm volatile("sti");
while (!(queue = highest_priority_queue()))
asm volatile("hlt");
asm volatile("cli");
current = task;
}
/* Schedule next task */
task = pop_from_start(queue);
task->state = RUNNING;
if (task == current)
return;
if (current && current->state == RUNNING) {
current->state = READY;
add(readyQueue[current->priority], current);
}
switch_to_task(task);
}
/* Initialise the scheduler */
void
init_scheduler(void)
{
enum Priority p;
for (p = 0; p < PRIORITY_COUNT; p++)
readyQueue[p] = create_list(&taskType, LIST_NORMAL);
}