Step 11: Preemptive scheduler
This commit is contained in:
parent
2a37ac201e
commit
ad329b5f81
11 changed files with 213 additions and 137 deletions
|
|
@ -57,9 +57,31 @@ pub fn initialize() void {
|
||||||
isr.install_exceptions();
|
isr.install_exceptions();
|
||||||
isr.install_irqs();
|
isr.install_irqs();
|
||||||
isr.install_syscalls();
|
isr.install_syscalls();
|
||||||
interrupt.registerIRQ(0, interrupt.pit_handler);
|
interrupt.registerIRQ(0, kernel.time.increment);
|
||||||
interrupt.registerIRQ(1, kernel.ps2.keyboard_handler);
|
interrupt.registerIRQ(1, kernel.ps2.keyboard_handler);
|
||||||
|
interrupt.register(1, debug_trap);
|
||||||
|
interrupt.register(13, general_protection_fault);
|
||||||
|
interrupt.register(14, page_fault);
|
||||||
|
|
||||||
// load IDT
|
// load IDT
|
||||||
lidt(@ptrToInt(&idtr));
|
lidt(@ptrToInt(&idtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn general_protection_fault() void {
|
||||||
|
kernel.println("general protection fault");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug_trap() void {
|
||||||
|
kernel.println("debug fault/trap");
|
||||||
|
kernel.println("dr7: 0b{b}", dr7());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_fault() void {
|
||||||
|
const vaddr = cr2();
|
||||||
|
kernel.println("cr2: 0x{x}", vaddr);
|
||||||
|
kernel.println("phy: 0x{x}", paging.translate(vaddr));
|
||||||
|
kernel.println("pde: 0x{x} ({})", paging.pde(vaddr), vaddr >> 22);
|
||||||
|
kernel.println("pte: 0x{x} ({})", paging.pte(vaddr), vaddr >> 12);
|
||||||
|
paging.format();
|
||||||
|
while (true) asm volatile ("hlt");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,11 @@ export fn interruptDispatch() void {
|
||||||
switch (n) {
|
switch (n) {
|
||||||
// Exceptions.
|
// Exceptions.
|
||||||
EXCEPTION_0...EXCEPTION_31 => {
|
EXCEPTION_0...EXCEPTION_31 => {
|
||||||
handlers[n]();
|
kernel.println("");
|
||||||
|
kernel.println("num: {}", isr.context.interrupt_n);
|
||||||
|
kernel.println("err: {}", isr.context.error_code);
|
||||||
|
kernel.println("ip: 0x{x}", isr.context.eip);
|
||||||
|
return handlers[n]();
|
||||||
},
|
},
|
||||||
|
|
||||||
// IRQs.
|
// IRQs.
|
||||||
|
|
@ -181,27 +185,14 @@ pub fn maskIRQ(irq: u8, mask: bool) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// configures the chan0 with a rate generator, which will trigger irq0
|
// configures the chan0 with a rate generator, which will trigger irq0
|
||||||
|
pub const divisor = 2685;
|
||||||
|
pub const tick = 2251; // f = 1.193182 MHz, TODO: turn into a function
|
||||||
pub fn configPIT() void {
|
pub fn configPIT() void {
|
||||||
const chanNum = 0;
|
const chanNum = 0;
|
||||||
const chan = PIT_CHAN0;
|
const chan = PIT_CHAN0;
|
||||||
const divisor = 2685;
|
|
||||||
const LOHI = 0b11; // bit4 | bit5
|
const LOHI = 0b11; // bit4 | bit5
|
||||||
const PITMODE_RATE_GEN = 0x2;
|
const PITMODE_RATE_GEN = 0x2;
|
||||||
outb(PIT_CMD, chanNum << 6 | LOHI << 4 | PITMODE_RATE_GEN << 1);
|
outb(PIT_CMD, chanNum << 6 | LOHI << 4 | PITMODE_RATE_GEN << 1);
|
||||||
outb(PIT_CHAN0, divisor & 0xff);
|
outb(PIT_CHAN0, divisor & 0xff);
|
||||||
outb(PIT_CHAN0, divisor >> 8);
|
outb(PIT_CHAN0, divisor >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pit_handler() void {
|
|
||||||
// pit freq = 1.193182 MHz
|
|
||||||
// chan0 divisor = 2685
|
|
||||||
// PIT_RATE in us
|
|
||||||
kernel.time.increment(2251);
|
|
||||||
kernel.task.sleeping_tasks.decrement(2251);
|
|
||||||
while (kernel.task.sleeping_tasks.popZero()) |sleepnode| {
|
|
||||||
const tasknode = sleepnode.data;
|
|
||||||
tasknode.data.state = .ReadyToRun;
|
|
||||||
kernel.vmem.free(@ptrToInt(sleepnode));
|
|
||||||
kernel.task.ready_tasks.prepend(tasknode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Kernel stack for interrupt handling.
|
// Kernel stack for interrupt handling.
|
||||||
KERNEL_STACK = 0x10000
|
// KERNEL_STACK = 0x10000
|
||||||
// GDT selectors.
|
// GDT selectors.
|
||||||
KERNEL_DS = 0x10
|
KERNEL_DS = 0x10
|
||||||
|
|
||||||
|
|
@ -27,24 +27,23 @@ isrCommon:
|
||||||
pusha // Save the registers state.
|
pusha // Save the registers state.
|
||||||
|
|
||||||
// Setup kernel data segment.
|
// Setup kernel data segment.
|
||||||
mov $KERNEL_DS, %ax
|
// mov $KERNEL_DS, %ax
|
||||||
mov %ax, %ds
|
// mov %ax, %ds
|
||||||
mov %ax, %es
|
// mov %ax, %es
|
||||||
|
|
||||||
// Save the pointer to the current context and switch to the kernel stack.
|
// Save the pointer to the current context and switch to the kernel stack.
|
||||||
mov %esp, context
|
mov %esp, context
|
||||||
mov $KERNEL_STACK, %esp
|
// mov $KERNEL_STACK, %esp
|
||||||
|
|
||||||
call interruptDispatch // Handle the interrupt event.
|
call interruptDispatch // Handle the interrupt event.
|
||||||
|
|
||||||
// Restore the pointer to the context (of a different thread, potentially).
|
// Restore the pointer to the context (of a different thread, potentially).
|
||||||
mov context, %esp
|
// mov context, %esp
|
||||||
|
|
||||||
// Setup user data segment.
|
// Setup user data segment.
|
||||||
mov $USER_DS, %ax
|
// mov $USER_DS, %ax
|
||||||
mov %ax, %ds
|
// mov %ax, %ds
|
||||||
mov %ax, %es
|
// mov %ax, %es
|
||||||
|
|
||||||
|
|
||||||
popa // Restore the registers state.
|
popa // Restore the registers state.
|
||||||
add $8, %esp // Remove interrupt number and error code from stack.
|
add $8, %esp // Remove interrupt number and error code from stack.
|
||||||
|
|
|
||||||
|
|
@ -33,3 +33,15 @@ pub inline fn lidt(idtr: usize) void {
|
||||||
: [idtr] "r" (idtr)
|
: [idtr] "r" (idtr)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cr2() usize {
|
||||||
|
return asm volatile ("movl %%cr2, %[result]"
|
||||||
|
: [result] "=r" (-> usize)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dr7() usize {
|
||||||
|
return asm volatile ("movl %%dr7, %[result]"
|
||||||
|
: [result] "=r" (-> usize)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,19 +15,14 @@ const HUGE = 0x80;
|
||||||
|
|
||||||
pub var pageDirectory: [1024]PageEntry align(4096) linksection(".bss") = [_]PageEntry{0} ** 1024;
|
pub var pageDirectory: [1024]PageEntry align(4096) linksection(".bss") = [_]PageEntry{0} ** 1024;
|
||||||
|
|
||||||
fn pageFault() void {
|
|
||||||
kernel.println("pagefault");
|
|
||||||
while (true) asm volatile ("hlt");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: inline these
|
// TODO: inline these
|
||||||
fn pageBase(virt: usize) usize {
|
fn pageBase(virt: usize) usize {
|
||||||
return virt & (~PAGE_SIZE +% 1);
|
return virt & (~PAGE_SIZE +% 1);
|
||||||
}
|
}
|
||||||
fn pde(virt: usize) *PageEntry {
|
pub fn pde(virt: usize) *PageEntry {
|
||||||
return &PD[virt >> 22]; //relies on recursive mapping
|
return &PD[virt >> 22]; //relies on recursive mapping
|
||||||
}
|
}
|
||||||
fn pte(virt: usize) *PageEntry {
|
pub fn pte(virt: usize) *PageEntry {
|
||||||
return &PT[virt >> 12]; //relies on recursive mapping
|
return &PT[virt >> 12]; //relies on recursive mapping
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,7 +59,6 @@ pub fn initialize() void {
|
||||||
// TODO: verify is this a hack?
|
// TODO: verify is this a hack?
|
||||||
assert(pmem.stack_end < kernel.layout.IDENTITY);
|
assert(pmem.stack_end < kernel.layout.IDENTITY);
|
||||||
|
|
||||||
interrupt.register(14, pageFault);
|
|
||||||
setupPaging(@ptrToInt(&pageDirectory[0])); //asm routine
|
setupPaging(@ptrToInt(&pageDirectory[0])); //asm routine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,6 @@ pub fn loop() void {
|
||||||
keypress(input_ring_buffer[input_read_index]);
|
keypress(input_ring_buffer[input_read_index]);
|
||||||
input_read_index +%= 1;
|
input_read_index +%= 1;
|
||||||
}
|
}
|
||||||
|
// task.usleep(10 * 1000) catch unreachable;
|
||||||
task.lock_scheduler();
|
|
||||||
task.schedule();
|
|
||||||
task.unlock_scheduler();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ export fn kmain(magic: u32, info: *const multiboot.MultibootInfo) noreturn {
|
||||||
pci.scan();
|
pci.scan();
|
||||||
|
|
||||||
task.new(@ptrToInt(topbar)) catch unreachable;
|
task.new(@ptrToInt(topbar)) catch unreachable;
|
||||||
|
task.preempt();
|
||||||
|
|
||||||
console.loop();
|
console.loop();
|
||||||
unreachable;
|
unreachable;
|
||||||
|
|
|
||||||
199
src/task.zig
199
src/task.zig
|
|
@ -17,6 +17,7 @@ var timer_last_count: u64 = 0;
|
||||||
pub fn update_time_used() void {
|
pub fn update_time_used() void {
|
||||||
const current_count = time.offset_us;
|
const current_count = time.offset_us;
|
||||||
const elapsed = current_count - timer_last_count;
|
const elapsed = current_count - timer_last_count;
|
||||||
|
// if (current_task.data.tid == 1) println("{} adding {} time", current_task.data.tid, elapsed);
|
||||||
timer_last_count = current_count;
|
timer_last_count = current_count;
|
||||||
current_task.data.time_used += elapsed;
|
current_task.data.time_used += elapsed;
|
||||||
}
|
}
|
||||||
|
|
@ -24,8 +25,9 @@ pub fn update_time_used() void {
|
||||||
pub const TaskState = enum {
|
pub const TaskState = enum {
|
||||||
Running,
|
Running,
|
||||||
ReadyToRun,
|
ReadyToRun,
|
||||||
Blocked,
|
Paused,
|
||||||
Sleeping,
|
Sleeping,
|
||||||
|
Terminated,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Task = struct {
|
pub const Task = struct {
|
||||||
|
|
@ -59,76 +61,63 @@ pub const Task = struct {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// responsible for calling the task entrypoint
|
|
||||||
pub fn destroy(self: *Task) void {
|
pub fn destroy(self: *Task) void {
|
||||||
vmem.free(self.esp);
|
vmem.free(self.esp);
|
||||||
vmem.free(@ptrToInt(self));
|
vmem.free(@ptrToInt(self));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
///ASM
|
|
||||||
// extern fn jmp_to_entrypoint(entrypoint: usize) void;
|
|
||||||
// // this is only run once on the first execution of a task
|
|
||||||
// pub fn birth() void {
|
|
||||||
// println("birth!");
|
|
||||||
// unlock_scheduler();
|
|
||||||
// const entrypoint = current_task.data.entrypoint;
|
|
||||||
// jmp_to_entrypoint(entrypoint);
|
|
||||||
// // comptime asm ("jmp %[entrypoint]"
|
|
||||||
// // :
|
|
||||||
// // : [entrypoint] "{ecx}" (entrypoint)
|
|
||||||
// // );
|
|
||||||
// }
|
|
||||||
///ASM
|
///ASM
|
||||||
extern fn switch_tasks(new_esp: usize, old_esp_addr: usize) void;
|
extern fn switch_tasks(new_esp: usize, old_esp_addr: usize) void;
|
||||||
|
|
||||||
pub fn new(entrypoint: usize) !void {
|
pub fn new(entrypoint: usize) !void {
|
||||||
|
task.lock_scheduler();
|
||||||
|
defer task.unlock_scheduler();
|
||||||
|
|
||||||
const node = try vmem.create(TaskNode);
|
const node = try vmem.create(TaskNode);
|
||||||
node.data = try Task.create(entrypoint);
|
node.data = try Task.create(entrypoint);
|
||||||
ready_tasks.prepend(node);
|
ready_tasks.prepend(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sleep_tick(tick: usize) void {
|
||||||
|
task.lock_scheduler();
|
||||||
|
defer task.unlock_scheduler();
|
||||||
|
|
||||||
|
task.sleeping_tasks.decrement(tick);
|
||||||
|
var popped = false;
|
||||||
|
while (task.sleeping_tasks.popZero()) |sleepnode| {
|
||||||
|
// println("finished sleeping");
|
||||||
|
// task.format();
|
||||||
|
const tasknode = sleepnode.data;
|
||||||
|
tasknode.data.state = .ReadyToRun;
|
||||||
|
vmem.free(@ptrToInt(sleepnode));
|
||||||
|
task.ready_tasks.prepend(tasknode);
|
||||||
|
popped = true;
|
||||||
|
}
|
||||||
|
if (popped) preempt();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: make a sleep without malloc
|
// TODO: make a sleep without malloc
|
||||||
pub fn usleep(usec: u64) !void {
|
pub fn usleep(usec: u64) !void {
|
||||||
|
assert(current_task.data.state == .Running);
|
||||||
|
|
||||||
const node = try vmem.create(SleepNode);
|
const node = try vmem.create(SleepNode);
|
||||||
|
|
||||||
|
update_time_used();
|
||||||
|
|
||||||
lock_scheduler();
|
lock_scheduler();
|
||||||
|
defer unlock_scheduler();
|
||||||
|
|
||||||
current_task.data.state = .Sleeping;
|
current_task.data.state = .Sleeping;
|
||||||
node.data = current_task;
|
node.data = current_task;
|
||||||
node.counter = usec;
|
node.counter = usec;
|
||||||
sleeping_tasks.insert(node);
|
sleeping_tasks.insert(node);
|
||||||
schedule();
|
schedule();
|
||||||
unlock_scheduler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn block(state: TaskState) void {
|
pub var IRQ_disable_counter: usize = 0;
|
||||||
assert(state != .Running);
|
pub var postpone_task_switches_counter: isize = 0; // this counter can go negative when we are scheduling after a postpone
|
||||||
assert(state != .ReadyToRun);
|
pub var postpone_task_switches_flag: bool = false;
|
||||||
|
|
||||||
lock_scheduler();
|
|
||||||
current_task.data.state = state;
|
|
||||||
blocked_tasks.append(current_task);
|
|
||||||
schedule();
|
|
||||||
unlock_scheduler();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unblock(node: *TaskNode) void {
|
|
||||||
lock_scheduler();
|
|
||||||
node.data.state = .ReadyToRun;
|
|
||||||
blocked_tasks.remove(node);
|
|
||||||
if (ready_tasks.first == null) {
|
|
||||||
// Only one task was running before, so pre-empt
|
|
||||||
switch_to(node);
|
|
||||||
} else {
|
|
||||||
// There's at least one task on the "ready to run" queue already, so don't pre-empt
|
|
||||||
ready_tasks.append(node);
|
|
||||||
unlock_scheduler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var IRQ_disable_counter: usize = 0;
|
|
||||||
var postpone_task_switches_counter: isize = 0; // this counter can go negative when we are scheduling after a postpone
|
|
||||||
var postpone_task_switches_flag: bool = false;
|
|
||||||
pub fn lock_scheduler() void {
|
pub fn lock_scheduler() void {
|
||||||
if (constants.SMP == false) {
|
if (constants.SMP == false) {
|
||||||
x86.cli();
|
x86.cli();
|
||||||
|
|
@ -138,38 +127,44 @@ pub fn lock_scheduler() void {
|
||||||
}
|
}
|
||||||
pub fn unlock_scheduler() void {
|
pub fn unlock_scheduler() void {
|
||||||
if (constants.SMP == false) {
|
if (constants.SMP == false) {
|
||||||
|
assert(IRQ_disable_counter > 0);
|
||||||
|
assert(postpone_task_switches_counter > 0);
|
||||||
postpone_task_switches_counter -= 1;
|
postpone_task_switches_counter -= 1;
|
||||||
if (postpone_task_switches_flag == true and postpone_task_switches_counter == 0) {
|
if (postpone_task_switches_flag == true and postpone_task_switches_counter == 1) {
|
||||||
// in this section, postpone counter will go to -1 during the task
|
|
||||||
postpone_task_switches_flag = false;
|
postpone_task_switches_flag = false;
|
||||||
|
notify("AFTER POSTPONE");
|
||||||
schedule();
|
schedule();
|
||||||
}
|
}
|
||||||
IRQ_disable_counter -= 1;
|
IRQ_disable_counter -= 1;
|
||||||
if (IRQ_disable_counter == 0) {
|
// must be the last instruction because we do interrupts inside interrupts
|
||||||
x86.sti();
|
if (IRQ_disable_counter == 0) x86.sti();
|
||||||
x86.hlt();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn preempt() void {
|
||||||
|
if (current_task.data.state != .Running) return;
|
||||||
|
|
||||||
|
update_time_used();
|
||||||
|
if (ready_tasks.first == null) {
|
||||||
|
notify("NO PREEMPT SINGLE TASK");
|
||||||
|
time.task_slice_remaining = 0;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock_scheduler();
|
||||||
|
schedule();
|
||||||
|
unlock_scheduler();
|
||||||
}
|
}
|
||||||
|
|
||||||
// expects:
|
// expects:
|
||||||
// - chosen is .ReadyToRun
|
// - chosen is .ReadyToRun
|
||||||
// - chosen is not in any scheduler lists
|
// - chosen is not in any scheduler lists
|
||||||
|
// - current_task has been moved to a queue
|
||||||
// - scheduler is locked
|
// - scheduler is locked
|
||||||
// - the tasks being switched to will unlock_scheduler()
|
// - the tasks being switched to will unlock_scheduler()
|
||||||
pub fn switch_to(chosen: *TaskNode) void {
|
pub fn switch_to(chosen: *TaskNode) void {
|
||||||
assert(chosen.data.state == .ReadyToRun);
|
assert(chosen.data.state == .ReadyToRun);
|
||||||
|
assert(current_task.data.state != .Running);
|
||||||
if (postpone_task_switches_counter != 0) {
|
|
||||||
postpone_task_switches_flag = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case of self preemption, shouldn't happen really
|
|
||||||
if (current_task.data.state == .Running) {
|
|
||||||
current_task.data.state = .ReadyToRun;
|
|
||||||
ready_tasks.append(current_task);
|
|
||||||
}
|
|
||||||
|
|
||||||
// save old stack
|
// save old stack
|
||||||
const old_task_esp_addr = ¤t_task.data.esp;
|
const old_task_esp_addr = ¤t_task.data.esp;
|
||||||
|
|
@ -177,7 +172,10 @@ pub fn switch_to(chosen: *TaskNode) void {
|
||||||
// switch states
|
// switch states
|
||||||
chosen.data.state = .Running;
|
chosen.data.state = .Running;
|
||||||
current_task = chosen;
|
current_task = chosen;
|
||||||
|
if (ready_tasks.first == null) time.task_slice_remaining = 0;
|
||||||
|
if (ready_tasks.first != null) time.task_slice_remaining = time.TASK_SLICE;
|
||||||
|
|
||||||
|
// we don't have any startup code for tasks, so i do it here
|
||||||
if (current_task.data.born == false) {
|
if (current_task.data.born == false) {
|
||||||
current_task.data.born = true;
|
current_task.data.born = true;
|
||||||
unlock_scheduler();
|
unlock_scheduler();
|
||||||
|
|
@ -189,29 +187,48 @@ pub fn switch_to(chosen: *TaskNode) void {
|
||||||
|
|
||||||
pub var CPU_idle_time: u64 = 0;
|
pub var CPU_idle_time: u64 = 0;
|
||||||
pub var CPU_idle_start_time: u64 = 0;
|
pub var CPU_idle_start_time: u64 = 0;
|
||||||
|
// expects:
|
||||||
|
// lock_scheduler should be called before
|
||||||
|
// unlock_scheduler should be called after
|
||||||
|
// current_task is blocked or running (preemption)
|
||||||
pub fn schedule() void {
|
pub fn schedule() void {
|
||||||
assert(IRQ_disable_counter > 0);
|
assert(IRQ_disable_counter > 0);
|
||||||
|
assert(current_task.data.state != .ReadyToRun);
|
||||||
|
|
||||||
if (postpone_task_switches_counter != 0) {
|
// postponed
|
||||||
|
if (postpone_task_switches_counter != 1 and current_task.data.state == .Running) {
|
||||||
postpone_task_switches_flag = true;
|
postpone_task_switches_flag = true;
|
||||||
|
notify("POSTPONING SCHEDULE");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// next task
|
||||||
update_time_used();
|
|
||||||
|
|
||||||
// format();
|
|
||||||
if (ready_tasks.popFirst()) |t| {
|
if (ready_tasks.popFirst()) |t| {
|
||||||
// somebody is ready to run
|
|
||||||
// std doesn't do this, for developer flexibility maybe?
|
|
||||||
t.prev = null;
|
t.prev = null;
|
||||||
t.next = null;
|
t.next = null;
|
||||||
switch_to(t);
|
|
||||||
} else if (current_task.data.state == .Running) {
|
// notify("SWITCHING TO 0x{x}", t.data.esp);
|
||||||
// single task mode, carry on
|
if (current_task.data.state == .Running) {
|
||||||
|
current_task.data.state = .ReadyToRun;
|
||||||
|
ready_tasks.append(current_task);
|
||||||
|
}
|
||||||
|
return switch_to(t);
|
||||||
|
}
|
||||||
|
// single task
|
||||||
|
if (current_task.data.state == .Running) {
|
||||||
|
notify("SINGLE TASK");
|
||||||
|
time.task_slice_remaining = 0;
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
// idle mode
|
// no tasks
|
||||||
notify_idle();
|
idle_mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idle_mode() void {
|
||||||
|
assert(ready_tasks.first == null);
|
||||||
|
assert(current_task.data.state != .Running);
|
||||||
|
assert(current_task.data.state != .ReadyToRun);
|
||||||
|
|
||||||
|
notify("IDLE");
|
||||||
|
|
||||||
// borrow the current task
|
// borrow the current task
|
||||||
const borrow = current_task;
|
const borrow = current_task;
|
||||||
|
|
@ -235,19 +252,18 @@ pub fn schedule() void {
|
||||||
x86.cli(); // disable interrupts again to see if there is something to do
|
x86.cli(); // disable interrupts again to see if there is something to do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notify_idle() void {
|
pub fn notify(comptime message: []const u8, args: ...) void {
|
||||||
const bg = vga.background;
|
const bg = vga.background;
|
||||||
const fg = vga.foreground;
|
const fg = vga.foreground;
|
||||||
const cursor = vga.cursor;
|
const cursor = vga.cursor;
|
||||||
vga.background = fg;
|
vga.background = fg;
|
||||||
vga.foreground = bg;
|
vga.foreground = bg;
|
||||||
vga.cursor = 80 - 4;
|
vga.cursor = 80 - message.len - 10;
|
||||||
vga.cursor_enabled = false;
|
vga.cursor_enabled = false;
|
||||||
|
|
||||||
print("IDLE");
|
print(message, args);
|
||||||
|
|
||||||
vga.cursor_enabled = true;
|
vga.cursor_enabled = true;
|
||||||
vga.cursor = cursor;
|
vga.cursor = cursor;
|
||||||
|
|
@ -271,3 +287,32 @@ pub fn format() void {
|
||||||
var sit = sleeping_tasks.first;
|
var sit = sleeping_tasks.first;
|
||||||
while (sit) |node| : (sit = node.next) println("{} {}", node.data.data, node.counter);
|
while (sit) |node| : (sit = node.next) println("{} {}", node.data.data, node.counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pub fn block(state: TaskState) void {
|
||||||
|
// assert(current_task.data.state == .Running);
|
||||||
|
|
||||||
|
// assert(state != .Running);
|
||||||
|
// assert(state != .ReadyToRun);
|
||||||
|
|
||||||
|
// lock_scheduler();
|
||||||
|
// defer unlock_scheduler();
|
||||||
|
|
||||||
|
// update_time_used();
|
||||||
|
// current_task.data.state = state;
|
||||||
|
// blocked_tasks.append(current_task);
|
||||||
|
// schedule();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn unblock(node: *TaskNode) void {
|
||||||
|
// lock_scheduler();
|
||||||
|
// node.data.state = .ReadyToRun;
|
||||||
|
// blocked_tasks.remove(node);
|
||||||
|
// if (ready_tasks.first == null) {
|
||||||
|
// // Only one task was running before, so pre-empt
|
||||||
|
// switch_to(node);
|
||||||
|
// } else {
|
||||||
|
// // There's at least one task on the "ready to run" queue already, so don't pre-empt
|
||||||
|
// ready_tasks.append(node);
|
||||||
|
// unlock_scheduler();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
|
||||||
15
src/time.zig
15
src/time.zig
|
|
@ -1,8 +1,19 @@
|
||||||
usingnamespace @import("index.zig");
|
usingnamespace @import("index.zig");
|
||||||
|
|
||||||
pub var offset_us: u64 = 0;
|
pub var offset_us: u64 = 0;
|
||||||
pub fn increment(value: u32) void {
|
pub var task_slice_remaining: u64 = 0;
|
||||||
offset_us += value;
|
pub var TASK_SLICE: u64 = 50 * 1000;
|
||||||
|
pub fn increment() void {
|
||||||
|
const tick = x86.interrupt.tick; //us
|
||||||
|
|
||||||
|
offset_us += tick;
|
||||||
|
task.sleep_tick(tick);
|
||||||
|
|
||||||
|
if (task_slice_remaining != 0) {
|
||||||
|
// There is a time slice length
|
||||||
|
if (task_slice_remaining <= tick) return task.preempt();
|
||||||
|
if (task_slice_remaining > tick) task_slice_remaining -= tick;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uptime() void {
|
pub fn uptime() void {
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ pub fn clear() void {
|
||||||
}
|
}
|
||||||
pub fn topbar() void {
|
pub fn topbar() void {
|
||||||
const bg = vga.background;
|
const bg = vga.background;
|
||||||
|
// println("topbar1");
|
||||||
while (true) {
|
while (true) {
|
||||||
const cursor = vga.cursor;
|
const cursor = vga.cursor;
|
||||||
vga.background = Color.Red;
|
vga.background = Color.Red;
|
||||||
|
|
@ -73,13 +74,14 @@ pub fn topbar() void {
|
||||||
time.uptime();
|
time.uptime();
|
||||||
print(" | ");
|
print(" | ");
|
||||||
task.format_short();
|
task.format_short();
|
||||||
|
// print(" ({})", task.IRQ_disable_counter);
|
||||||
println("");
|
println("");
|
||||||
|
|
||||||
vga.cursor_enabled = true;
|
vga.cursor_enabled = true;
|
||||||
vga.cursor = cursor;
|
vga.cursor = cursor;
|
||||||
vga.background = bg;
|
vga.background = bg;
|
||||||
|
|
||||||
task.usleep(500 * 1000) catch unreachable; // 60ms
|
task.usleep(50 * 1000) catch unreachable; // 60ms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ pub fn available() usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn malloc(size: usize) !usize {
|
pub fn malloc(size: usize) !usize {
|
||||||
if (available() == 0) return error.OutOfMemory;
|
if (available() == 0) {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
stack_index -= 1;
|
stack_index -= 1;
|
||||||
var vaddr: usize = stack[stack_index];
|
var vaddr: usize = stack[stack_index];
|
||||||
try x86.paging.mmap(vaddr, null);
|
try x86.paging.mmap(vaddr, null);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue