mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
232 lines
7.2 KiB
Zig
232 lines
7.2 KiB
Zig
const std = @import("std");
|
|
const r = @import("root");
|
|
const rtl = @import("rtl");
|
|
|
|
const ke = r.ke;
|
|
const ki = ke.private;
|
|
|
|
/// Structure representing a kernel thread.
|
|
pub const Thread = struct {
|
|
/// Thread priority classes.
|
|
/// Realtime: Highest priority, aggressively scheduled.
|
|
/// Batch: Normal priority.
|
|
/// Idle: For idle and background threads.
|
|
pub const Priority = struct {
|
|
pub const Class = enum(u8) {
|
|
Realtime,
|
|
Batch,
|
|
Idle,
|
|
};
|
|
|
|
/// Idle thread has the lowest priority.
|
|
pub const idle_low = 0;
|
|
pub const idle = 1;
|
|
|
|
/// Batch threads have priorities 2-23
|
|
pub const low_batch = 2;
|
|
pub const high_batch = 23;
|
|
pub const batch_range = high_batch - low_batch + 1;
|
|
|
|
/// Realtime threads have priorities 40-63
|
|
pub const low_realtime = 40;
|
|
pub const max = 63;
|
|
|
|
/// In the mid-range of batch threads
|
|
pub const default = 10;
|
|
|
|
/// Interactive threads have priorities 24-39
|
|
pub const low_interactive = 24;
|
|
pub const high_interactive = 39;
|
|
|
|
pub fn class_from_prio(prio: u8) Class {
|
|
if (prio >= low_realtime and prio <= max) return .Realtime;
|
|
if (prio >= low_batch and prio < low_realtime) return .Batch;
|
|
if (prio <= idle) return .Idle;
|
|
|
|
unreachable;
|
|
}
|
|
};
|
|
|
|
/// Thread state.
|
|
pub const State = enum(u8) {
|
|
/// The thread is ready to run.
|
|
Ready,
|
|
/// The thread is currently running.
|
|
Running,
|
|
/// The thread was selected to run.
|
|
Selected,
|
|
/// The thread is currently sleeping.
|
|
Blocked,
|
|
/// The thread has exited and is waiting to be reaped.
|
|
Zombie,
|
|
/// The thread has exited.
|
|
Terminated,
|
|
};
|
|
/// Implementation-dependent context.
|
|
context: ki.impl.ThreadContext,
|
|
/// Thread lock.
|
|
lock: ke.SpinLock,
|
|
/// Niceness value.
|
|
nice: i8,
|
|
/// Effective priority value of the thread: `max(base_priority, inherited_prio)`.
|
|
priority: u8,
|
|
/// Base priority value of the thread.
|
|
base_priority: u8,
|
|
/// Priority inherited via priority donation.
|
|
inherited_prio: u8,
|
|
/// When the thread started sleeping.
|
|
sleep_start: u64,
|
|
/// Ticks spent voluntarily sleeping recently.
|
|
sleep_time: u64,
|
|
/// Ticks spent running recently.
|
|
run_time: u64,
|
|
/// Whether the thread is pinned to this CPU.
|
|
/// If it is pinned, then it can't be moved across another CPU.
|
|
pinned: bool,
|
|
/// Current state of the thread.
|
|
state: std.atomic.Value(State),
|
|
/// Linkage into a scheduler run queue.
|
|
runq_link: rtl.List.Entry,
|
|
/// Last CPU this thread ran on.
|
|
last_cpu: ?u32,
|
|
/// CPU this thread is enqueued on.
|
|
cpu: ?u32,
|
|
/// Run queue this thread is in
|
|
runq: ?*ki.sched.RunQueue,
|
|
/// Index into the run queue this thread is currently in.
|
|
/// Only valid when `runq` is non-null.
|
|
runq_idx: u8,
|
|
/// Current wait status.
|
|
wait_status: std.atomic.Value(ki.wait.Status),
|
|
waitblocks: [4]ki.wait.WaitBlock,
|
|
/// Reason for the wait, if any.
|
|
wait_reason: ?[]const u8,
|
|
/// Timer used for timeouts.
|
|
timer: ke.Timer,
|
|
/// Turnstile.
|
|
turnstile: *ki.turnstile.Turnstile,
|
|
turnstile_waiter: ?*ki.turnstile.Waiter,
|
|
turnstiles_owned: rtl.List,
|
|
/// Object this thread is currently blocked on, or null.
|
|
waiting_on: ?*anyopaque,
|
|
/// Queue this thread is associated with.
|
|
queue: ?*ke.Queue,
|
|
queue_item: ?*rtl.List.Entry,
|
|
/// Base of the stack.
|
|
stack: r.VAddr,
|
|
/// PELT load average,
|
|
avg: ki.sched.Average,
|
|
/// Set whenever the thread is switching off its stack.
|
|
/// This is used to avoid taking thread next lock to wait for switch off
|
|
/// to complete.
|
|
switching: std.atomic.Value(bool),
|
|
smr_sections: rtl.List,
|
|
|
|
/// Initialize a thread.
|
|
/// - `stack`: Address of the base of the stack used by the thread.
|
|
/// - `stack_size`: Size of the stack
|
|
/// - `prio`: Base priority of the thread
|
|
/// - `entry`: Entry point of the thread
|
|
/// - `arg`: Extraneous argument to be passed to `entry`
|
|
pub fn init(
|
|
thread: *Thread,
|
|
stack: r.VAddr,
|
|
stack_size: usize,
|
|
prio: u8,
|
|
entry: *const fn (?*anyopaque) void,
|
|
arg: ?*anyopaque,
|
|
) void {
|
|
thread.* = .{
|
|
.context = .init(
|
|
stack,
|
|
stack_size,
|
|
entry,
|
|
arg,
|
|
),
|
|
.lock = .init(),
|
|
.nice = 0,
|
|
.priority = prio,
|
|
.base_priority = prio,
|
|
.inherited_prio = 0,
|
|
.sleep_start = 0,
|
|
.sleep_time = 0,
|
|
.run_time = 0,
|
|
.pinned = false,
|
|
.state = .init(.Ready),
|
|
.runq_link = .{},
|
|
.last_cpu = null,
|
|
.cpu = null,
|
|
.runq = null,
|
|
.runq_idx = 0,
|
|
.wait_status = .init(.Satisfied),
|
|
.waitblocks = undefined,
|
|
.wait_reason = null,
|
|
.timer = undefined,
|
|
.turnstile = undefined,
|
|
.turnstile_waiter = null,
|
|
.turnstiles_owned = undefined,
|
|
.waiting_on = null,
|
|
.queue = null,
|
|
.queue_item = null,
|
|
.stack = stack,
|
|
.switching = .init(false),
|
|
.avg = .{
|
|
.last_update = .init(0),
|
|
// New threads are considered heavy until they prove themselves,
|
|
// this might allow for better balancing during bursts.
|
|
.load = ki.sched.pelt_load_avg_max,
|
|
.est = ki.sched.pelt_load_avg_max,
|
|
.period_contrib = 0,
|
|
},
|
|
.smr_sections = undefined,
|
|
};
|
|
|
|
thread.turnstiles_owned.init();
|
|
thread.timer.init();
|
|
thread.smr_sections.init();
|
|
}
|
|
|
|
pub fn priority_class(self: *Thread) Priority.Class {
|
|
return Priority.class_from_prio(self.priority);
|
|
}
|
|
|
|
/// Class of the thread's *base* priority, ignoring any inherited boost.
|
|
pub fn base_priority_class(self: *Thread) Priority.Class {
|
|
return Priority.class_from_prio(self.base_priority);
|
|
}
|
|
|
|
/// The priority the thread should run at.
|
|
pub fn effective_priority(self: *Thread) u8 {
|
|
return @max(self.base_priority, self.inherited_prio);
|
|
}
|
|
|
|
pub fn is_interactive(self: *Thread) bool {
|
|
return self.priority >= Priority.low_interactive and self.priority <= Priority.high_interactive;
|
|
}
|
|
};
|
|
|
|
/// HandoffList of threads waiting to be reaped.
|
|
/// This is managed by the process subsystem.
|
|
pub var reaper_list: rtl.HandoffList = undefined;
|
|
|
|
/// Terminate the currently running thread.
|
|
/// This does not return.
|
|
pub fn exit() void {
|
|
_ = ke.ipl.raise(.Dispatch);
|
|
const curtd = current();
|
|
|
|
curtd.lock.acquire_no_ipl();
|
|
curtd.state.store(.Zombie, .monotonic);
|
|
|
|
// Reuse the runq linkage to put on reaper list.
|
|
reaper_list.insert(@ptrCast(&curtd.runq_link.next));
|
|
|
|
ki.sched.detach_load_avg(ki.sched.percpu.local(), curtd);
|
|
ki.sched.yield_locked();
|
|
}
|
|
|
|
/// Return the currently running thread.
|
|
pub fn current() *ke.Thread {
|
|
return ki.sched.percpu.local().current_thread.?;
|
|
}
|