[feat] 100Hz task switch
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
#include <scheduler.h>
|
||||
#include <idt.h>
|
||||
#include <pic.h>
|
||||
#include <memory/heap.h>
|
||||
#include <memory/pmm.h>
|
||||
#include <common.h>
|
||||
@@ -13,16 +15,14 @@ static task_t* g_current = NULL;
|
||||
static task_t* g_task_list = NULL; // circular linked list head
|
||||
|
||||
// Trampoline: first thing a new task runs after context_switch.
|
||||
// The entry function pointer is stored in the task's name field
|
||||
// (we repurpose a slot — actually we store it in a simple global array).
|
||||
static void (*g_task_entries[TASK_MAX])(void);
|
||||
|
||||
extern "C" void task_entry_trampoline() {
|
||||
task_t* cur = scheduler_current();
|
||||
if (cur && g_task_entries[cur->id]) {
|
||||
g_task_entries[cur->id](); // call the user function
|
||||
g_task_entries[cur->id]();
|
||||
}
|
||||
task_exit(); // clean up when done
|
||||
task_exit();
|
||||
}
|
||||
|
||||
task_t* task_create(const char* name, void (*entry)(void)) {
|
||||
@@ -34,7 +34,6 @@ task_t* task_create(const char* name, void (*entry)(void)) {
|
||||
UINT32 id = g_task_count++;
|
||||
task_t* task = &g_tasks[id];
|
||||
|
||||
// Store entry function for the trampoline
|
||||
g_task_entries[id] = entry;
|
||||
|
||||
// Allocate kernel stack
|
||||
@@ -47,10 +46,10 @@ task_t* task_create(const char* name, void (*entry)(void)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Fill task struct
|
||||
task->id = id;
|
||||
task->state = TASK_STATE_READY;
|
||||
task->stack_base = stack;
|
||||
task->time_slice = TIME_SLICE_DEFAULT;
|
||||
|
||||
// Copy name
|
||||
const char* s = name;
|
||||
@@ -73,12 +72,13 @@ task_t* task_create(const char* name, void (*entry)(void)) {
|
||||
// r14 = 0
|
||||
// r15 = 0 <- RSP points here initially
|
||||
//
|
||||
// When preempted by timer IRQ, the ISR stub saves a full trap_frame
|
||||
// on the task's stack — that layout is only created by hardware+ISR.
|
||||
//
|
||||
UINT64* sp = (UINT64*)((UINT8*)stack + TASK_STACK_SIZE);
|
||||
|
||||
// Push return address (task_entry_trampoline)
|
||||
*--sp = (UINT64)task_entry_trampoline;
|
||||
|
||||
// Push callee-saved registers (all zero)
|
||||
*--sp = 0; // rbx
|
||||
*--sp = 0; // rbp
|
||||
*--sp = 0; // r12
|
||||
@@ -90,13 +90,12 @@ task_t* task_create(const char* name, void (*entry)(void)) {
|
||||
|
||||
// Insert into circular linked list
|
||||
if (g_task_list == NULL) {
|
||||
task->next = task; // points to itself (single element circle)
|
||||
task->next = task;
|
||||
g_task_list = task;
|
||||
} else {
|
||||
// Insert after current tail (g_task_list is the "last" in circle)
|
||||
task->next = g_task_list->next;
|
||||
g_task_list->next = task;
|
||||
g_task_list = task; // new tail
|
||||
g_task_list = task;
|
||||
}
|
||||
|
||||
serial_write("SCHED: created task '");
|
||||
@@ -108,28 +107,71 @@ task_t* task_create(const char* name, void (*entry)(void)) {
|
||||
return task;
|
||||
}
|
||||
|
||||
// Find next READY task in the circular list, starting from g_current->next
|
||||
static task_t* find_next_ready(void) {
|
||||
if (g_current == NULL || g_task_list == NULL) return NULL;
|
||||
|
||||
task_t* next = g_current->next;
|
||||
task_t* start = next;
|
||||
|
||||
do {
|
||||
if (next->state == TASK_STATE_READY) {
|
||||
return next;
|
||||
}
|
||||
next = next->next;
|
||||
} while (next != start);
|
||||
|
||||
return NULL; // no READY tasks
|
||||
}
|
||||
|
||||
void yield(void) {
|
||||
if (g_current == NULL || g_task_list == NULL) return;
|
||||
|
||||
task_t* cur = g_current;
|
||||
task_t* next = cur->next;
|
||||
task_t* next = find_next_ready();
|
||||
|
||||
// Skip terminated tasks
|
||||
while (next->state == TASK_STATE_TERMINATED && next != cur) {
|
||||
next = next->next;
|
||||
}
|
||||
if (next->state == TASK_STATE_TERMINATED) return; // all terminated
|
||||
|
||||
if (next == cur) return; // only one task, nothing to do
|
||||
if (next == NULL || next == cur) return;
|
||||
|
||||
if (cur->state != TASK_STATE_TERMINATED)
|
||||
cur->state = TASK_STATE_READY;
|
||||
next->state = TASK_STATE_RUNNING;
|
||||
next->time_slice = TIME_SLICE_DEFAULT;
|
||||
g_current = next;
|
||||
|
||||
context_switch(&cur->rsp, next->rsp);
|
||||
}
|
||||
|
||||
// Timer tick handler — called from PIT IRQ 0
|
||||
void scheduler_tick(void) {
|
||||
if (g_current == NULL) return;
|
||||
|
||||
// Decrement time slice
|
||||
if (g_current->time_slice > 0) {
|
||||
g_current->time_slice--;
|
||||
}
|
||||
|
||||
// If time slice expired, preempt
|
||||
if (g_current->time_slice == 0) {
|
||||
task_t* cur = g_current;
|
||||
task_t* next = find_next_ready();
|
||||
|
||||
if (next == NULL || next == cur) {
|
||||
// No other task ready, or only this task — reload time slice
|
||||
cur->time_slice = TIME_SLICE_DEFAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
// Preempt
|
||||
cur->state = TASK_STATE_READY;
|
||||
cur->time_slice = TIME_SLICE_DEFAULT;
|
||||
next->state = TASK_STATE_RUNNING;
|
||||
next->time_slice = TIME_SLICE_DEFAULT;
|
||||
g_current = next;
|
||||
|
||||
context_switch(&cur->rsp, next->rsp);
|
||||
}
|
||||
}
|
||||
|
||||
void scheduler_run(void) {
|
||||
if (g_task_list == NULL) {
|
||||
serial_write("SCHED: no tasks to run\n");
|
||||
@@ -137,7 +179,7 @@ void scheduler_run(void) {
|
||||
}
|
||||
|
||||
// Find first READY task
|
||||
task_t* start = g_task_list->next; // head of circle
|
||||
task_t* start = g_task_list->next;
|
||||
task_t* t = start;
|
||||
do {
|
||||
if (t->state == TASK_STATE_READY) {
|
||||
@@ -153,14 +195,14 @@ void scheduler_run(void) {
|
||||
|
||||
g_current = t;
|
||||
t->state = TASK_STATE_RUNNING;
|
||||
t->time_slice = TIME_SLICE_DEFAULT;
|
||||
|
||||
serial_write("SCHED: starting task '");
|
||||
serial_write(t->name);
|
||||
serial_write("'\n");
|
||||
|
||||
// First context switch — no old RSP to save (we're still in scheduler_run)
|
||||
// Just switch to the task's stack directly.
|
||||
// We need a dummy old_rsp to satisfy the API, but we never return here.
|
||||
// First context switch — switch to the task's stack
|
||||
// This will never return (until all tasks terminate)
|
||||
UINT64 dummy_rsp;
|
||||
context_switch(&dummy_rsp, t->rsp);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user