BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / blob / master / task / schedule.c

// Related

Orion

Barry Moving signal handlers into separate namespace 7ae31b0 (2 years, 4 months ago)
/*
 * This file controls the Kernel's scheduler.  It decides when a new task must
 * be scheduled, and which tasks can actually be scheduled.
 */

#include <stdint.h>
#include "../proc/proc.h"
#include "../mem/paging.h"
#include "task.h"

uintptr_t read_eip(void);
void context_switch(uintptr_t eip, page_dir_t phys,
                    uintptr_t ebp, uintptr_t esp);
void handle_signals(void);

TaskQueue readyQueue, tasks;
Task *currentTask[MAX_CPUS];

/* Switch to a task */
static void
switch_to_task(Task *task)
{
	uintptr_t esp, ebp, eip;
	asm volatile("mov %%esp, %0" : "=r" (esp));
	asm volatile("mov %%ebp, %0" : "=r" (ebp));
	eip = read_eip();
	if (eip == 0x10032004) /* Magic number */
		return;

	acquire(&current->lock);
	current->eip = eip;
	current->esp = esp;
	current->ebp = ebp;
	release(&current->lock);
	current = task;
	eip = current->eip;
	esp = current->esp;
	ebp = current->ebp;

	context_switch(eip, current->pageDir, ebp, esp);
	/* UNREACHED */

	/*
	 * This code actually returns to the read_eip() call above.  This is due
	 * to how the call works.  The context switch jumps to the address
	 * stored in the eip variable.  This address is the return address of
	 * the read_eip() call.  The context_switch() function sets the return
	 * value before jumping, so it appears as though read_eip() returned
	 * that value.  The code uses the magic number 0x10032004 to tell when
	 * this is occurring and just returns early.
	 */
}

/* Find task by ID */
Task *
find_task(pid_t tid)
{
	Task *task;
	for (task = tasks.start; task && task->tid != tid; task = task->tnext);
	return task;
}

/* Add a task to a task queue */
void
add_to_queue(TaskQueue *queue, Task *task)
{
	if (!queue->start) {
		queue->start = task;
		queue->end = task;
	} else {
		queue->end->next = task;
		queue->end = task;
	}
	task->next = NULL;
}

/* Remove a task from a task queue */
void
remove_from_queue(TaskQueue *queue, Task *task)
{
	/* Start of list */
	if (queue->start == task) {
		queue->start = task->next;
		if (!queue->start)
			queue->end = NULL;
		return;
	}

	/* Search */
	Task *prev;
	for (prev = queue->start; prev->next; prev = prev->next)
		if (prev->next == task)
			break;
	if (prev->next) {
		prev->next = task->next;
		if (queue->end == task)
			queue->end = prev;
	}
}

/* Remove the first task from a task queue */
Task *
pop_from_queue(TaskQueue *queue)
{
	Task *head = queue->start;
	if (!head)
		return NULL;
	queue->start = head->next;
	if (!queue->start)
		queue->end = NULL;
	return head;
}

/* Block a task */
void
block_task(int reason)
{
	acquire(&current->lock);
	current->state = reason;
	release(&current->lock);
	schedule();
}

/* Unblock a task */
void
unblock_task(Task *task)
{
	if (task->state == READY || task->state == RUNNING)
		return;
	task->state = READY;
	if (!readyQueue.start || task->priority > current->priority) {
		acquire(&readyQueue.lock);
		task->next = readyQueue.start;
		readyQueue.start = task;
		if (!readyQueue.end)
			readyQueue.end = task;
		release(&readyQueue.lock);
	} else {
		add_to_queue(&readyQueue, task);
	}
}

/* Schedule next task */
void
schedule(void)
{
	Task *task = current;

	/* Next schedulable task */
	if (readyQueue.start) {
		acquire(&readyQueue.lock);
		task = readyQueue.start;
		readyQueue.start = task->next;
		if (!readyQueue.start)
			readyQueue.end = NULL;
		task->state = RUNNING;
		task->next = NULL;
		if (current->state == RUNNING) {
			current->state = READY;
			add_to_queue(&readyQueue, current);
		}
		release(&readyQueue.lock);
		switch_to_task(task);
	/* Idle task */
	} else if (current->state != RUNNING) {
		current = NULL;
		asm volatile("sti");
		while (!readyQueue.start)
			asm volatile("hlt");
		asm volatile("cli");
		current = task;
		acquire(&readyQueue.lock);
		task = readyQueue.start;
		readyQueue.start = task->next;
		if (!readyQueue.start)
			readyQueue.end = NULL;
		task->state = RUNNING;
		task->next = NULL;
		release(&readyQueue.lock);
		switch_to_task(task);
	}

	handle_signals();
}