BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / blob / d46e09a34ca8cd546f3a4312976cf3b6a5a55ccf / task / scheduler.c

// Related

Nucleus

Barry Improved context switching and interrupt handling d46e09a (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

void context_switch(uintptr_t eip, page_dir_t pagedir,
                    uintptr_t esi, uintptr_t edi, uintptr_t ebx,
                    uintptr_t ebp, uintptr_t esp);

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 %%esi, %0" : "=r" (current->esi));
		asm volatile("mov %%edi, %0" : "=r" (current->edi));
		asm volatile("mov %%ebx, %0" : "=r" (current->ebx));
		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 */
	current = task; /* Given reference, so no get() */
	context_switch(current->eip, current->pageDir, current->esi,
	               current->edi, current->ebx, current->ebp, current->esp);
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);
}