WIP: Implement thread exit

This commit is contained in:
2025-03-26 15:33:41 +02:00
parent e1bd496b8f
commit b1a59dd42b
3 changed files with 95 additions and 5 deletions
+13
View File
@@ -20,6 +20,19 @@ pub const impl = switch (cpu) {
pub const vmm = impl.vmm;
pub const IrqGuard = struct {
state: bool,
pub fn acquire() @This() {
const state = set_interrupt_mask(true);
return .{ .state = state };
}
pub fn release(self: @This()) void {
set_interrupt_mask(self.state);
}
};
/// Halts the CPU execution indefinitely, without ever returning.
pub inline fn halt() noreturn {
impl.halt();
+2 -4
View File
@@ -18,7 +18,6 @@ const std = @import("std");
fn f0(arg: usize) callconv(.C) void {
log.info("Argument is {}", .{arg});
thread.yield();
}
noinline fn f1(arg: usize, c: usize) void {
@@ -37,8 +36,6 @@ pub export fn kernel_main() callconv(.C) noreturn {
var a = arena.Arena.init(256 * 0x1000) orelse @panic("Could not setup kernel arena");
thread.Queue.init_this_cpu(&a);
const t0 = thread.Thread.create_kernel(&a, &f0, 1234);
const code = switch (comptime arch.cpu) {
.riscv64 => &[_]u8{
0x93, 0x02, 0xB0, 0x07, // li t0, 123
@@ -55,7 +52,8 @@ pub export fn kernel_main() callconv(.C) noreturn {
};
const t1 = thread.test_create_user_from_code(&a, code) catch @panic("Could not create test thread");
thread.enqueue(t0);
const t = thread.Thread.create_kernel(&a, &f0, 1234);
thread.enqueue(t);
thread.enqueue(t1);
log.info("Test", .{});
+80 -1
View File
@@ -6,6 +6,7 @@ const arena = @import("arena.zig");
const arch = @import("kernel.zig").arch;
const log = @import("debug.zig").log;
const mem = @import("mem.zig");
const sync = @import("sync.zig");
const ProcessAddressSpace = mem.vmm.ProcessAddressSpace;
@@ -15,7 +16,7 @@ const ProcessAddressSpace = mem.vmm.ProcessAddressSpace;
pub const KernelThreadFn = fn (usize) callconv(.C) void;
pub fn kernel_return() callconv(.C) noreturn {
@panic("TODO: kernel thread exit");
Thread.exit_current();
}
/// Task to run when there are no real threads in the queue
@@ -34,6 +35,8 @@ pub const Queue = struct {
current: ?*Thread = null,
/// Thread queue head pointer.
head: ?*Thread = null,
/// Queue's lock
lock: sync.Spinlock = .{},
/// Pointer to this CPU's thread queue.
pub threadlocal var t_this_cpu: ?*Queue = null;
@@ -59,6 +62,7 @@ pub const Queue = struct {
/// Yields CPU to the next available task.
pub fn yield(self: *@This()) void {
// TODO locking here
if (self.current) |curr| {
// Switching from thread
if (curr.next) |next| {
@@ -67,6 +71,12 @@ pub const Queue = struct {
self.current = next;
next.switch_from(curr);
}
} else if (self.head) |h| {
// ... to thread (head)
if (h != curr) {
self.current = h;
h.switch_from(curr);
}
} else {
// ... to idle
self.current = null;
@@ -86,6 +96,11 @@ pub const Queue = struct {
/// Adds an available task to this queue.
pub fn enqueue(self: *@This(), t: *Thread) void {
var guard = self.lock.lock_irqsave();
defer guard.release();
t.queue = self;
if (self.head) |gt| {
t.next = gt;
t.prev = gt.prev;
@@ -97,6 +112,44 @@ pub const Queue = struct {
t.prev = t;
}
}
/// # Invariants
///
/// `t` must be a thread within the `self` queue.
fn dequeue(self: *@This(), t: *Thread) void {
var guard = self.lock.lock_irqsave();
defer guard.release();
t.queue = null;
if (t == self.head) {
if (t.next == t) {
self.head = null;
t.next = null;
t.prev = null;
return;
}
if (t.next) |tn| {
tn.prev = t.prev;
}
if (t.prev) |tp| {
tp.next = t.next;
}
self.head = t.next;
} else {
if (t.next) |tn| {
tn.prev = t.prev;
}
if (t.prev) |tp| {
tp.next = t.next;
}
}
t.next = null;
t.prev = null;
}
};
/// Represents a single execution thread.
@@ -106,6 +159,8 @@ pub const Thread = struct {
/// Architecture-specific task context.
arch_context: arch.Context,
/// Queue to which this thread belongs
queue: ?*Queue = null,
/// Next thread in the queue.
next: ?*Thread = null,
/// Previous thread in the queue.
@@ -151,6 +206,30 @@ pub const Thread = struct {
pub fn switch_from(self: *@This(), from: *@This()) void {
self.arch_context.switch_from(&from.arch_context);
}
pub fn dequeue(self: *@This()) void {
// TODO queueing information should be put under a lock for SMP to work properly
if (self.queue) |q| {
q.dequeue(self);
}
}
pub fn current() *@This() {
return Queue.t_this_cpu.?.current.?;
}
pub fn exit_current() noreturn {
// Mask IRQs so they don't break current thread's state
const mask = arch.IrqGuard.acquire();
defer mask.release();
const curr = Thread.current();
curr.dequeue();
yield();
@panic("This code should not be reachable");
}
};
/// Helper data structure to represent kernel stacks in task contexts.