all: more tidying

This commit is contained in:
Loïc Grégoire
2026-10-08 18:01:42 -04:00
parent dc39456d6e
commit cefc69e1b5
51 changed files with 352 additions and 339 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ const Msr = enum(u32) {
};
fn write_msr(msr: Msr, value: u64) void {
amd64.wrmsr(@intFromEnum(msr), value);
amd64.wrmsr(@backingInt(msr), value);
}
const PVClock = extern struct {
+5 -5
View File
@@ -138,11 +138,11 @@ pub inline fn wrmsr(msr: u32, value: u64) void {
}
pub inline fn read_msr(comptime msr: Msr) u64 {
return rdmsr(@intFromEnum(msr));
return rdmsr(@backingInt(msr));
}
pub inline fn write_msr(comptime msr: Msr, value: u64) void {
wrmsr(@intFromEnum(msr), value);
wrmsr(@backingInt(msr), value);
}
pub inline fn rdgsbase() usize {
@@ -348,8 +348,8 @@ pub const CpuidRequest = union(enum) {
.HighestExtendedFunction => .{ 0x80000000, 0 },
.ExtendedInfo => .{ 0x80000001, 0 },
.PowerManagementInfo => .{ 0x80000007, 0 },
.ExtendedFeatures => |s| .{ 0x7, @intFromEnum(s) },
.BrandString => |p| .{ @intFromEnum(p), 0 },
.ExtendedFeatures => |s| .{ 0x7, @backingInt(s) },
.BrandString => |p| .{ @backingInt(p), 0 },
.HypervisorId => .{ 0x40000000, 0 },
};
return cpuid(leaf, subleaf);
@@ -709,7 +709,7 @@ pub const IdtEntry = extern struct {
.offset_low = @truncate(offset),
.selector = cs,
.ist = ist,
.type_attr = @intFromEnum(attr),
.type_attr = @backingInt(attr),
.offset_mid = @truncate(offset >> 16),
.offset_high = @truncate(offset >> 32),
.reserved = 0,
+2 -2
View File
@@ -81,8 +81,8 @@ pub const InternedString = packed struct(usize) {
}
};
const Table = rcu.HashTable(Entry, "link", Context);
const EntryZone = mm.zone.TypedZone(Entry);
const Table = rcu.HashTableType(Entry, "link", Context);
const EntryZone = mm.zone.ZoneType(Entry);
var domain: *ke.smr.Domain = undefined;
var table: Table = undefined;
+2 -2
View File
@@ -82,9 +82,9 @@ const Segment = struct {
const freelist_count = @bitSizeOf(usize);
const max_qcaches = 16;
var seg_zone: mmp.zone.TypedZone(Segment) = undefined;
var seg_zone: mmp.zone.ZoneType(Segment) = undefined;
const SegmentTree = rtl.RBTree(Segment.cmp);
const SegmentTree = rtl.RbTreeType(Segment.cmp);
pub const Arena = struct {
/// Name of the arena, for debugging purposes.
+8 -8
View File
@@ -22,7 +22,7 @@ const ps = r.ps;
const mm = r.mm;
/// Maximum number of additional threads created per pool.
const max_dynamic_threads = ke.Tunable(u8, 4, "ex.work.max_dynamic");
const max_dynamic_threads = ke.TunableType(u8, 4, "ex.work.max_dynamic");
var pool_manager_event: ke.Event = undefined;
@@ -98,7 +98,7 @@ pub const DelayedWorkItem = struct {
}
};
const Context = rtl.TaggedPtr(Pool);
const Context = rtl.TaggedPtrType(Pool);
/// Represents a thread worker pool and its queue.
const Pool = struct {
@@ -204,7 +204,7 @@ const PerCpu = struct {
normal: Pool,
};
const percpu = ke.CpuLocal(PerCpu, undefined);
const percpu = ke.CpuLocalType(PerCpu, undefined);
/// Low-priority work queue.
var low: Pool = undefined;
@@ -214,8 +214,8 @@ fn work_loop(p: ?*anyopaque) void {
const dynamic = ctx.tag() == 1;
while (true) {
const timeout: ?r.Nanoseconds = if (dynamic)
.from(r.Seconds.init(5))
const timeout: ?rtl.Duration = if (dynamic)
rtl.Duration.seconds(5)
else
null;
const item = ctx.ptr().queue.remove(timeout) catch {
@@ -239,9 +239,9 @@ fn work_dpc(dpc: *ke.Dpc, _: ?*anyopaque) void {
enqueue(&item.item);
}
/// Enqueue a work item in `time`.
pub fn enqueue_in(item: *DelayedWorkItem, time: r.Nanoseconds) void {
ke.timer.set(&item.timer, time, .{ .dpc = &item.dpc });
/// Enqueue a work item after `duration`.
pub fn enqueue_in(item: *DelayedWorkItem, duration: rtl.Duration) void {
ke.timer.set(&item.timer, duration, .{ .dpc = &item.dpc });
}
/// Enqueue a work item to be executed eventually.
+1 -1
View File
@@ -124,7 +124,7 @@ export fn isr_handler_main(frame: *const amd64.IrqFrame) callconv(.c) void {
_ = ke.ipl.set_hardware(ipl);
if (@intFromEnum(ipl) < @intFromEnum(ke.Ipl.get_max_software()) and
if (@backingInt(ipl) < @backingInt(ke.Ipl.get_max_software()) and
kep.ipl.is_softint_pending(.Dispatch))
{
kep.dpc.dispatch();
+5 -5
View File
@@ -7,7 +7,7 @@ const r = @import("root");
const ke = r.ke;
const kep = r.ke.private;
const id = CpuLocal(u32, 0);
const id = CpuLocalType(u32, 0);
/// Initialize a CPU. Must be called on all CPUs.
pub fn init_cpu(cpu_id: u32) void {
@@ -25,7 +25,7 @@ pub fn current() u32 {
}
/// Wraps around CPU-local data.
pub fn CpuLocal(comptime T: type, comptime init: T) type {
pub fn CpuLocalType(comptime T: type, comptime init: T) type {
return struct {
var storage: T linksection(".data.percpu") = init;
@@ -42,7 +42,7 @@ pub fn CpuLocal(comptime T: type, comptime init: T) type {
}
/// Wraps around CPU-local data with a designated symbol name.
pub fn ExportedCpuLocal(
pub fn ExportedCpuLocalTypeType(
comptime T: type,
comptime init: T,
comptime name: []const u8,
@@ -62,5 +62,5 @@ pub fn ExportedCpuLocal(
}
/// Bitmask of CPUs.
pub const CpuMask = rtl.BitMap(config.ncpus);
pub const AtomicCpuMask = rtl.AtomicBitMap(config.ncpus);
pub const CpuMask = rtl.BitmapType(config.ncpus);
pub const AtomicCpuMask = rtl.AtomicBitmapType(config.ncpus);
+3 -3
View File
@@ -39,7 +39,7 @@ const PerCpu = struct {
lock: ke.SpinLock,
};
const pcpu = ke.CpuLocal(PerCpu, .{
const pcpu = ke.CpuLocalType(PerCpu, .{
.lock = undefined,
.queue = undefined,
});
@@ -120,7 +120,7 @@ fn dispatch_queue(cpu: u32) void {
if (sched_cpu.start_timer) {
ke.timer.set(
&sched_cpu.resched_timer,
.from(r.Milliseconds.init(config.sched_timeslice)),
rtl.Duration.ms(config.sched_timeslice),
.{ .dpc = &sched_cpu.resched_dpc },
);
}
@@ -130,7 +130,7 @@ fn dispatch_queue(cpu: u32) void {
ke.timer.cancel(&sched_cpu.resched_timer);
ke.timer.set(
&sched_cpu.resched_timer,
.from(r.Milliseconds.init(config.sched_timeslice)),
rtl.Duration.ms(config.sched_timeslice),
.{ .dpc = &sched_cpu.resched_dpc },
);
}
+1 -1
View File
@@ -29,7 +29,7 @@ const PerCpu = struct {
state: std.atomic.Value(State),
};
const percpu = ke.CpuLocal(PerCpu, undefined);
const percpu = ke.CpuLocalType(PerCpu, undefined);
var frozen_cpus = std.atomic.Value(u32).init(0);
+2 -2
View File
@@ -33,13 +33,13 @@ const PerCpu = struct {
};
/// Current IPL on this CPU.
pub const cpu_ipl = ke.ExportedCpuLocal(
pub const cpu_ipl = ke.ExportedCpuLocalTypeType(
Ipl,
.Passive,
"cpu_ipl",
);
pub const percpu = ke.CpuLocal(PerCpu, .{
pub const percpu = ke.CpuLocalType(PerCpu, .{
.pending_softints = std.atomic.Value(u8).init(0),
});
+2 -2
View File
@@ -45,7 +45,7 @@ const DebugWriter = struct {
// Messages are on average 2^5 = 32 bytes.
const avg_msg_size_bits = 5;
pub var ringbuffer = kep.log_ring.RingBuffer(
pub var ringbuffer = kep.log_ring.RingBufferType(
config.log_buffer_shift,
avg_msg_size_bits,
).init();
@@ -87,7 +87,7 @@ pub fn log(
ringbuffer.publish(res);
// Signal whomever is waiting on logs to get published.
if (@intFromEnum(ke.ipl.current()) <= @intFromEnum(ke.Ipl.Dispatch)) {
if (@backingInt(ke.ipl.current()) <= @backingInt(ke.Ipl.Dispatch)) {
event.signal();
}
+5 -4
View File
@@ -3,7 +3,7 @@
//!
//! # Overview
//!
//! `RingBuffer` is a concurrent ring buffer that allows multiple writers
//! `RingBufferType` is a concurrent ring buffer that allows multiple writers
//! and readers to operate concurrently without locks, and also supports re-
//! entrancy, making it safe to use from interrupt context.
//! It is modelled after Linux's `printk_ringbuffer`, but is simplified,
@@ -68,8 +68,9 @@
//! during eviction.
const std = @import("std");
const r = @import("root");
const rtl = @import("rtl");
const r = @import("root");
const ke = r.ke;
const kep = ke.private;
@@ -101,7 +102,7 @@ const Desc = struct {
pub const Info = struct {
sequence: u64,
timestamp: r.Nanoseconds,
timestamp: rtl.Timestamp,
length: u16,
};
@@ -131,7 +132,7 @@ pub const ReadError = error{NotYetAvailable};
// block position.
const lpos_no_data = 1;
pub fn RingBuffer(data_size_bits: usize, avg_msg_bits: usize) type {
pub fn RingBufferType(data_size_bits: usize, avg_msg_bits: usize) type {
return struct {
const data_size = 1 << data_size_bits;
const desc_bits = data_size_bits - avg_msg_bits;
+1 -1
View File
@@ -56,7 +56,7 @@ pub const Queue = struct {
/// Remove the item at the head.
/// This blocks until an item is actually popped.
pub fn remove(self: *Queue, timeout: ?r.Nanoseconds) !*rtl.List.Entry {
pub fn remove(self: *Queue, timeout: ?rtl.Duration) !*rtl.List.Entry {
const ipl = self.hdr.lock.acquire();
const td = kep.sched.percpu.local().current_thread orelse unreachable;
+3 -3
View File
@@ -14,15 +14,15 @@ pub const Dpc = p.dpc.Dpc;
pub const TimeCounter = p.time.TimeCounter;
pub const Timer = p.timer.Timer;
pub const Console = p.log.Console;
pub const CpuLocal = p.cpu.CpuLocal;
pub const ExportedCpuLocal = p.cpu.ExportedCpuLocal;
pub const CpuLocalType = p.cpu.CpuLocalType;
pub const ExportedCpuLocalTypeType = p.cpu.ExportedCpuLocalTypeType;
pub const CpuMask = p.cpu.CpuMask;
pub const AtomicCpuMask = p.cpu.AtomicCpuMask;
pub const Event = p.event.Event;
pub const Queue = p.queue.Queue;
pub const Mutex = p.mutex.Mutex;
pub const ShootdownState = p.shootdown.ShootdownState;
pub const Tunable = p.tunable.Tunable;
pub const TunableType = p.tunable.TunableType;
pub const Priority = p.thread.Priority;
pub const Stack = p.thread.Stack;
pub const Continuation = p.thread.Continuation;
+12 -12
View File
@@ -109,7 +109,7 @@ const interactivity_threshold = 30;
const scaling_factor = 50;
const preempt_threshold = @backingInt(ke.Priority.LowRealtime);
const balance_interval = @as(u64, config.sched_balance_interval);
const sched_migration_cost = ke.Tunable(
const sched_migration_cost = ke.TunableType(
u64,
500,
"sched_migration_cost_us",
@@ -226,8 +226,8 @@ pub const Average = struct {
est: usize = pelt_load_avg_max,
// Amount of the period that has been accounted for
period_contrib: usize = 0,
/// Timestamp (us) up to which history has been accounted.
last_update: r.Microseconds = r.Microseconds.init(0),
/// Timestamp up to which history has been accounted.
last_update: rtl.Timestamp = .{ .value = 0 },
};
pub const Accounting = struct {
@@ -241,7 +241,7 @@ pub const Accounting = struct {
pct_window_start: u64 = 0,
};
pub const percpu = ke.CpuLocal(PerCpu, undefined);
pub const percpu = ke.CpuLocalType(PerCpu, undefined);
var balance_timer: ke.Timer = undefined;
var balance_dpc = ke.Dpc.init(balance);
@@ -303,7 +303,7 @@ pub fn late_init() linksection(r.init) void {
balance_timer.init();
ke.timer.set(
&balance_timer,
.from(r.Milliseconds.init(balance_interval)),
rtl.Duration.ms(balance_interval),
.{ .dpc = &balance_dpc },
);
}
@@ -331,8 +331,8 @@ fn pelt_do_decay(val: u64, n: u64) u64 {
}
fn pelt_update(avg: *Average, runnable: bool) void {
const now = ke.time.read_time().to(r.Microseconds);
const new_delta = now.value - avg.last_update.value;
const now = ke.time.read_time();
const new_delta = now.to_us() - avg.last_update.to_us();
if (new_delta == 0) return;
@@ -411,7 +411,7 @@ fn attach_load_avg(cpu: *PerCpu, td: *ke.Thread) void {
if (!tracks_load_avg(td)) return;
if (td.avg.last_update.value == 0) {
td.avg.last_update = ke.time.read_time().to(r.Microseconds);
td.avg.last_update = ke.time.read_time();
} else {
pelt_update_td(td, null, false);
}
@@ -1093,7 +1093,7 @@ fn do_switch(cpu: *PerCpu, cur: *ke.Thread, next: *ke.Thread) void {
next.last_cpu = ke.cpu.current();
const time = ke.time.read_time();
const now_us = time.to(r.Microseconds).value;
const now_us = time.to_us();
if (tracks_load_avg(cur)) {
account(cur, now_us, .Running);
@@ -1233,7 +1233,7 @@ pub fn clock(_: *ke.Dpc, _: ?*anyopaque) void {
const curtd = cpu.current_thread orelse return;
const now = ke.time.read_time();
const now_us = now.to(r.Microseconds).value;
const now_us = now.to_us();
curtd.lock.acquire_no_ipl();
defer curtd.lock.release_no_ipl();
@@ -1448,7 +1448,7 @@ pub fn unblock_locked(td: *ke.Thread) void {
const now = ke.time.read_time();
const now_us = now.to(r.Microseconds).value;
const now_us = now.to_us();
const delta = (now_us - td.acct.stamp);
if (tracks_load_avg(td)) {
@@ -1765,7 +1765,7 @@ fn balance(_: *ke.Dpc, _: ?*anyopaque) void {
ke.timer.set(
&balance_timer,
.from(r.Milliseconds.init(ms)),
rtl.Duration.ms(ms),
.{
.dpc = &balance_dpc,
},
+1 -1
View File
@@ -47,7 +47,7 @@ const PerCpu = struct {
const slot_free: u16 = std.math.maxInt(u16);
const slot_reserved: u16 = std.math.maxInt(u16) - 1;
const percpu = ke.CpuLocal(PerCpu, undefined);
const percpu = ke.CpuLocalType(PerCpu, undefined);
pub var shootdowns: rtl.HandoffList = undefined;
+40 -27
View File
@@ -132,7 +132,46 @@ fn scan(
}
if (dom.preempt) {
read_seq = scan_preempt(dom, clk_read, goal, should_wait, read_seq);
}
// The orderings here serve two purposes:
// 1. All subsequent operations are ordered after the sequence has advanced.
// 2. Fast-path pollers must see all operations before the updated read_seq.
// (1) is done by the acquire fence below, and (2) is done by the release
// success ordering in the CAS. This pairs with the load acquire in poll().
// It would also be valid to relax the CAS and have a full fence here.
rtl.barrier.fence(.acquire);
// Advance the global read sequence.
var dom_rd_seq = dom.clock.read_seq.load(.acquire);
while (read_seq > dom_rd_seq) {
if (dom.clock.read_seq.cmpxchgWeak(
dom_rd_seq,
read_seq,
.release,
.acquire,
)) |seq| {
dom_rd_seq = seq;
} else {
dom_rd_seq = read_seq;
break;
}
}
return dom_rd_seq;
}
fn scan_preempt(
dom: *Domain,
goal: Sequence,
clk_read: Sequence,
should_wait: bool,
last_read_seq: Sequence,
) Sequence {
rtl.barrier.fence(.seq_cst);
var read_seq = last_read_seq;
// Preemption is enabled, check stalled readers.
for (0..ke.ncpus) |i| {
@@ -185,34 +224,8 @@ fn scan(
read_seq = @min(read_seq, seq);
}
}
}
// The orderings here serve two purposes:
// 1. All subsequent operations are ordered after the sequence has advanced.
// 2. Fast-path pollers must see all operations before the updated read_seq.
// (1) is done by the acquire fence below, and (2) is done by the release
// success ordering in the CAS. This pairs with the load acquire in poll().
// It would also be valid to relax the CAS and have a full fence here.
rtl.barrier.fence(.acquire);
// Advance the global read sequence.
var dom_rd_seq = dom.clock.read_seq.load(.acquire);
while (read_seq > dom_rd_seq) {
if (dom.clock.read_seq.cmpxchgWeak(
dom_rd_seq,
read_seq,
.release,
.acquire,
)) |seq| {
dom_rd_seq = seq;
} else {
dom_rd_seq = read_seq;
break;
}
}
return dom_rd_seq;
return read_seq;
}
/// Poll to determine whether all CPUs have reached `goal`.
+4 -4
View File
@@ -8,7 +8,7 @@ const r = @import("root");
const ke = r.ke;
const kep = ke.private;
fn LockTemplate(comptime T: type) type {
fn LockType(comptime T: type) type {
comptime {
if (!@hasDecl(T, "acquire_no_ipl"))
@compileError("lock backend must implement acquire_no_ipl");
@@ -115,7 +115,7 @@ fn LockTemplate(comptime T: type) type {
}
/// Simple Spin lock implementation.
pub const SpinLock = LockTemplate(struct {
pub const SpinLock = LockType(struct {
locked: std.atomic.Value(u8),
const Lock = @This();
@@ -163,7 +163,7 @@ const PerCpu = struct {
curr_idx: u32,
};
const pcpu = ke.CpuLocal(PerCpu, undefined);
const pcpu = ke.CpuLocalType(PerCpu, undefined);
/// Initialize a CPU's MCS nodes.
fn init_cpu() linksection(r.init) void {
@@ -182,7 +182,7 @@ comptime {
/// Queued spin lock implementation.
/// See https://rdmsr.github.io/writing/qspinlocks
pub const QSpinLock = LockTemplate(struct {
pub const QSpinLock = LockType(struct {
const Tail = packed struct(u16) {
idx: u2,
cpu: u14,
+3 -3
View File
@@ -348,12 +348,12 @@ const Cpu = struct {
/// Note that this is in addition to the global minimum of `ncpus` stacks.
/// Keep this low for minimal memory overhead, but potentially
/// increased latency.
const excess_stacks = ke.Tunable(u32, 1, "ke.thread.excess_stacks");
const excess_stacks = ke.TunableType(u32, 1, "ke.thread.excess_stacks");
var global_depot_lock: ke.SpinLock = undefined;
var global_depot: Depot = .{};
const percpu = ke.CpuLocal(Cpu, undefined);
const percpu = ke.CpuLocalType(Cpu, undefined);
const percpu_count_max = 2;
const wma_unit = 256;
@@ -368,7 +368,7 @@ inline fn wma_mix(old: u32, new: u32) u32 {
fn init_cpu() linksection(r.init) void {
const cpu = percpu.local();
cpu.* = .{
.stack_alloc_dpc = .init(stack_alloc_handler),
.stack_alloc_dpc = ke.Dpc.init(stack_alloc_handler),
.depot = .{},
};
}
+6 -6
View File
@@ -37,7 +37,7 @@ const State = struct {
// SeqLock-protected time state.
// Avoids a race on `initial_count` and `offset` with `update_overflow`.
var state = rtl.SeqLock(State).init(.{
var state = rtl.SeqLockType(State).init(.{
.initial_count = 0,
.offset = 0,
});
@@ -84,14 +84,14 @@ pub fn register_source(tc: *TimeCounter) void {
}
}
/// Return the time elapsed since boot in nanoseconds.
pub fn read_time() r.Nanoseconds {
const tc = best_tc orelse return .init(0);
/// Return the time elapsed since boot.
pub fn read_time() rtl.Timestamp {
const tc = best_tc orelse return .{ .value = 0 };
const s = state.load();
const curr_count = tc.read_count() & tc.mask;
const elapsed = (curr_count - s.initial_count) & tc.mask;
return .init(s.offset + ticks_to_ns(tc, elapsed));
return .{ .value = (s.offset + ticks_to_ns(tc, elapsed)) };
}
/// Return the best TimeCounter.
@@ -100,7 +100,7 @@ pub fn best() ?*TimeCounter {
}
/// Do a busy sleep of `ns` nanoseconds using TimeCounter.
pub fn sleep(ns: r.Nanoseconds) void {
pub fn sleep(ns: rtl.Duration) void {
const start = read_time();
while (true) {
+9 -9
View File
@@ -10,7 +10,7 @@ const pl = r.pl;
const std = @import("std");
const assert = std.debug.assert;
const TimerHeap = rtl.PairingHeap(.Min, kep.timer.cmp_timer);
const TimerHeap = rtl.PairingHeapType(.Min, kep.timer.cmp_timer);
const PerCpu = struct {
/// Heap of pending timers on this CPU.
@@ -35,7 +35,7 @@ pub const Timer = struct {
/// Timer state.
state: std.atomic.Value(State),
/// When the timer is bound to expire.
deadline: r.Nanoseconds,
deadline: rtl.Timestamp,
/// Attached DPC.
dpc: ?*ke.Dpc,
/// Intrusive pairing heap node.
@@ -47,7 +47,7 @@ pub const Timer = struct {
self.* = .{
.hdr = undefined,
.state = std.atomic.Value(State).init(.Stopped),
.deadline = r.Nanoseconds.init(0),
.deadline = .{ .value = 0 },
.dpc = null,
.node = .{},
.cpu = null,
@@ -57,10 +57,10 @@ pub const Timer = struct {
}
};
const percpu = ke.CpuLocal(PerCpu, .{
const percpu = ke.CpuLocalType(PerCpu, .{
.timers = TimerHeap.init(),
.lock = undefined,
.dpc = .init(handle_expiry),
.dpc = ke.Dpc.init(handle_expiry),
});
fn pcpu_init() linksection(r.init) void {
@@ -77,7 +77,7 @@ const Options = struct {
/// Start a timer with an expiration time.
/// A DPC that will be enqueued upon expiration can be passed.
pub fn set(timer: *Timer, time: r.Nanoseconds, opts: Options) void {
pub fn set(timer: *Timer, duration: rtl.Duration, opts: Options) void {
const ipl = timer.hdr.lock.acquire();
defer timer.hdr.lock.release(ipl);
@@ -92,7 +92,7 @@ pub fn set(timer: *Timer, time: r.Nanoseconds, opts: Options) void {
defer cpu.lock.release_no_ipl();
// Initialize the timer.
timer.deadline = .init(ke.time.read_time().value + time.value);
timer.deadline = ke.time.read_time().add(duration);
timer.cpu = cpu;
timer.dpc = opts.dpc;
@@ -102,7 +102,7 @@ pub fn set(timer: *Timer, time: r.Nanoseconds, opts: Options) void {
if (cpu.timers.root == &timer.node) {
// This is the earliest timer to expire, arm the hardware timer.
pl.arm_timer(time);
pl.arm_timer(duration);
}
// Locks dropped
@@ -198,7 +198,7 @@ fn handle_expiry(_: *ke.Dpc, _: ?*anyopaque) void {
if (timer.deadline.value > now.value and
timer.deadline.value - now.value > std.time.ns_per_ms)
{
pl.arm_timer(.init(timer.deadline.value - now.value));
pl.arm_timer(rtl.Duration.ns(timer.deadline.value - now.value));
cpu.lock.release_no_ipl();
return;
}
+3 -3
View File
@@ -1,11 +1,11 @@
//! Tunable kernel parameters.
//! TunableType kernel parameters.
//! These are variables set in the cmdline at boot and never changed.
const std = @import("std");
const rtl = @import("rtl");
const r = @import("root");
const pl = r.pl;
const set = rtl.LinkerSet("tunables", *const Entry);
const set = rtl.LinkerSetType("tunables", *const Entry);
const Entry = struct {
name: []const u8,
@@ -16,7 +16,7 @@ const Entry = struct {
},
};
pub fn Tunable(
pub fn TunableType(
comptime T: type,
comptime default: T,
comptime name: []const u8,
+1 -1
View File
@@ -101,7 +101,7 @@ pub const Status = enum(u8) {
};
const Options = struct {
timeout: ?r.Nanoseconds = null,
timeout: ?rtl.Duration = null,
waitblocks: ?[]WaitBlock = null,
continuation: ?ke.Continuation = null,
};
+3 -3
View File
@@ -68,14 +68,14 @@ pub const HolderData = if (config.warden) HeldLocks else struct {};
const num_classes = 128;
const num_buckets = 128;
const cpu_held_spinlocks = ke.CpuLocal(HeldLocks, .{});
const cpu_held_spinlocks = ke.CpuLocalType(HeldLocks, .{});
/// Matrix of reachability between classes
var reachable: [num_classes]Relation = @splat(.{});
var graph_lock: ke.SpinLock = undefined;
var classes: [num_classes]Class = @splat(undefined);
var class_buckets: [num_classes]rtl.AVLTree(nodes_cmp) = @splat(.init());
var class_buckets: [num_classes]rtl.AvlTreeType(nodes_cmp) = @splat(.init());
var started = false;
var num_active_classes: u32 = 0;
@@ -89,7 +89,7 @@ fn nodes_cmp(a: *const rtl.bst.Node, b: *const rtl.bst.Node) std.math.Order {
/// Find a lock class by its name.
fn find_lock_class(name: []const u8) struct {
?*Class,
?*rtl.AVLTree(nodes_cmp),
?*rtl.AvlTreeType(nodes_cmp),
} {
if (name.len == 0) {
// This lock shouldn't be checked.
+5 -1
View File
@@ -125,7 +125,7 @@ extern var rodata_end_addr: u8;
extern var data_start_addr: u8;
extern var data_end_addr: u8;
fn map_self() void {
fn map_executable() void {
const layout = r.impl.get_image_layout();
const text_start = @intFromPtr(&text_start_addr);
@@ -180,6 +180,10 @@ fn map_self() void {
.global = true,
},
);
}
fn map_self() void {
map_executable();
const max_hhdm_address = 4 * 1024 * 1024 * 1024;
+3 -2
View File
@@ -5,6 +5,7 @@
//! 2. Zone trimming
//! 3. Working-set trimming (eventually...)
const rtl = @import("rtl");
const r = @import("root");
const std = @import("std");
const ps = r.ps;
@@ -12,7 +13,7 @@ const ke = r.ke;
const mm = r.mm;
const mmp = mm.private;
const balance_interval = ke.Tunable(u32, 1000, "mm.balance.interval_ms");
const balance_interval = ke.TunableType(u32, 1000, "mm.balance.interval_ms");
const stack_reap_interval = 5;
const zone_update_interval = mm.zone.update_interval_s;
@@ -60,7 +61,7 @@ fn balance_manager(_: ?*anyopaque) void {
if (which == timeout) {
ke.timer.set(
&timer,
.from(r.Milliseconds.init(balance_interval.load())),
rtl.Duration.ms(balance_interval.load()),
.{},
);
}
+13 -13
View File
@@ -139,34 +139,34 @@ const slab_align = 8;
const zones_num = 32;
/// Number of rounds in a magazine.
const magazine_size = ke.Tunable(u8, 8, "mm.zone.mag_size");
const magazine_size = ke.TunableType(u8, 8, "mm.zone.mag_size");
/// How much extra memory CPUs are allowed to keep around.
const max_local_memory = ke.Tunable(
const max_local_memory = ke.TunableType(
u32,
@intCast(r.kib(128)),
"mm.zone.max_local_mem",
);
/// Number of contentions allowed per second before the depot grows.
const depot_grow_level = ke.Tunable(
const depot_grow_level = ke.TunableType(
u32,
5 * wma_unit,
"mm.zone.depot_grow_level",
);
/// Number of contentions allowed per second before the depot shrinks.
const depot_shrink_level = ke.Tunable(
const depot_shrink_level = ke.TunableType(
u32,
wma_unit / 2,
"mm.zone.depot_shrink_level",
);
/// Number of excess magazines in a zone before they are trimmed.
const excess_magazines = ke.Tunable(u32, 8, "mm.zone.excess_mags");
const excess_magazines = ke.TunableType(u32, 8, "mm.zone.excess_mags");
/// Excess memory in a zone before magazines are trimmed.
const excess_memory = ke.Tunable(
const excess_memory = ke.TunableType(
u32,
@intCast(r.kib(16)),
"mm.zone.excess_mem",
@@ -200,7 +200,7 @@ var zone_list_lock: ke.Mutex = undefined;
var magazine_zone: Zone = undefined;
var cpu_zone: Zone = undefined;
var smr_zone: TypedZone(ke.smr.Domain) = undefined;
var smr_zone: ZoneType(ke.smr.Domain) = undefined;
var smr_cpu_zone: Zone = undefined;
pub var smr_sys_domain: *ke.smr.Domain = undefined;
@@ -491,10 +491,10 @@ inline fn wma_mix(old: usize, new: usize) usize {
return (3 * old + new * wma_unit) / 4;
}
fn cpus_alloc() []rtl.CachePadded(Cpu) {
fn cpus_alloc() []rtl.CachePaddedType(Cpu) {
const ptr = cpu_zone.alloc(.{}) catch
@panic("failed to allocate zone CPU state");
const raw: [*]rtl.CachePadded(Cpu) = @ptrCast(@alignCast(ptr));
const raw: [*]rtl.CachePaddedType(Cpu) = @ptrCast(@alignCast(ptr));
return raw[0..ke.ncpus];
}
@@ -533,7 +533,7 @@ pub const Zone = struct {
dont_touch: bool,
/// Per-CPU state.
cpus: []rtl.CachePadded(Cpu),
cpus: []rtl.CachePaddedType(Cpu),
ctor: ?*const fn (obj: *anyopaque) void,
dtor: ?*const fn (obj: *anyopaque) void,
@@ -1746,7 +1746,7 @@ pub fn update() void {
}
/// Parameterized version of Zone, useful for object caches.
pub fn TypedZone(comptime T: type) type {
pub fn ZoneType(comptime T: type) type {
return struct {
zone: Zone,
@@ -1807,7 +1807,7 @@ pub fn early_init() linksection(r.init) void {
/// Post-SMP initialization.
pub fn late_init() linksection(r.init) void {
cpu_zone.init("cpus", @sizeOf(rtl.CachePadded(Cpu)) * ke.ncpus, .{
cpu_zone.init("cpus", @sizeOf(rtl.CachePaddedType(Cpu)) * ke.ncpus, .{
.magazines = false,
.alignment = std.atomic.cache_line,
});
@@ -1822,7 +1822,7 @@ pub fn late_init() linksection(r.init) void {
z.cpus = cpus_alloc();
for (0..ke.ncpus) |i| {
z.cpus[i] = rtl.CachePadded(Cpu).init(.{
z.cpus[i] = rtl.CachePaddedType(Cpu).init(.{
.lock = ke.SpinLock.init("Magazine"),
.alloc = null,
.free = null,
+10 -9
View File
@@ -1,5 +1,6 @@
const std = @import("std");
const amd64 = @import("arch");
const rtl = @import("rtl");
const r = @import("root");
const tsc = @import("tsc.zig");
const acpi = r.pl.acpi;
@@ -74,31 +75,31 @@ pub const ApicRegisters = enum(u32) {
pub var apics: std.ArrayList(u32) = .empty;
var xapic_address: ?usize = null;
pub var xapic_base_physical: usize = 0;
const in_x2apic_mode = ke.CpuLocal(bool, false);
const timer_ticks_per_us = ke.CpuLocal(u64, 0);
const in_x2apic_mode = ke.CpuLocalType(bool, false);
const timer_ticks_per_us = ke.CpuLocalType(u64, 0);
const log = std.log.scoped(.apic);
fn xapic_write(register: ApicRegisters, value: u32) void {
const lapic_base = xapic_address orelse return;
r.mmio_write(u32, lapic_base + @intFromEnum(register), value);
r.mmio_write(u32, lapic_base + @backingInt(register), value);
}
fn xapic_read(register: ApicRegisters) u32 {
const lapic_base = xapic_address orelse return 0;
return r.mmio_read(u32, lapic_base + @intFromEnum(register));
return r.mmio_read(u32, lapic_base + @backingInt(register));
}
fn x2apic_write(register: ApicRegisters, value: u64) void {
amd64.wrmsr(
@intFromEnum(amd64.Msr.X2ApicBase) + (@intFromEnum(register) >> 4),
@backingInt(amd64.Msr.X2ApicBase) + (@backingInt(register) >> 4),
value,
);
}
fn x2apic_read(register: ApicRegisters) u64 {
return amd64.rdmsr(
@intFromEnum(amd64.Msr.X2ApicBase) + (@intFromEnum(register) >> 4),
@backingInt(amd64.Msr.X2ApicBase) + (@backingInt(register) >> 4),
);
}
@@ -170,7 +171,7 @@ fn lapic_calibrate(ms: u64) u64 {
write(.LvtTimer, (1 << 16));
write(.TimerInitialCount, std.math.maxInt(u32));
ke.time.sleep(.from(r.Milliseconds.init(ms)));
ke.time.sleep(rtl.Duration.ms(ms));
const ticks = std.math.maxInt(u32) - read(.TimerCurrentCount);
@@ -205,7 +206,7 @@ fn timer_init() linksection(r.init) void {
timer_ticks_per_us.local().* = (avg_ticks / calib_us);
}
pub fn arm_timer(ns: r.Nanoseconds) void {
pub fn arm_timer(ns: rtl.Duration) void {
if (amd64.cpu_features.tsc_deadline) {
const now = amd64.rdtsc();
@@ -222,7 +223,7 @@ pub fn arm_timer(ns: r.Nanoseconds) void {
write(.LvtTimer, 1 << 16);
write(.TimerInitialCount, 0);
const us = ns.to(r.Microseconds).value;
const us = ns.to_us();
const ticks: u32 = @truncate(@max(us * timer_ticks_per_us.local().*, 1));
// Setup IRQ.
+3 -2
View File
@@ -1,3 +1,4 @@
const rtl = @import("rtl");
const r = @import("root");
const pl = r.pl;
const amd64 = r.arch;
@@ -109,8 +110,8 @@ pub fn debug_read() u8 {
return amd64.inb(com1);
}
pub fn arm_timer(deadline: r.Nanoseconds) void {
apic.arm_timer(deadline);
pub fn arm_timer(duration: rtl.Duration) void {
apic.arm_timer(duration);
}
pub fn send_ipi(target_cpu: u32) void {
+33 -38
View File
@@ -19,9 +19,9 @@ extern var __percpu_end: u8;
pub export var cpu_id_to_apic_id: [config.ncpus]u32 = undefined;
const start_stack = ke.ExportedCpuLocal(usize, 0, "ap_start_stack");
const start_stack = ke.ExportedCpuLocalTypeType(usize, 0, "ap_start_stack");
pub const start_thread = ke.CpuLocal(*ke.Thread, undefined);
pub const start_thread = ke.CpuLocalType(*ke.Thread, undefined);
var aps_booted = std.atomic.Value(usize).init(0);
@@ -58,6 +58,23 @@ fn make_thread(
return td;
}
fn delay_for_cpu() struct { rtl.Duration, rtl.Duration } {
// From Linux, on modern CPUs we can skip the long delay after INIT.
const skip_delay = switch (amd64.cpu_features.vendor) {
.Intel => amd64.cpu_features.family >= 0x06,
.Amd => amd64.cpu_features.family >= 0x0f,
.Hygon => amd64.cpu_features.family >= 0x18,
.Unknown => false,
};
const init_delay = rtl.Duration.ms(if (skip_delay) 0 else 10);
const sipi_delay = rtl.Duration.us(if (skip_delay) 10 else 300);
return .{ init_delay, sipi_delay };
}
const ap_stack_size = r.kib(16);
pub fn init() linksection(r.init) void {
const trampoline_start = @intFromPtr(&AP_TRAMPOLINE_START);
const trampoline_size = @intFromPtr(&AP_TRAMPOLINE_END) - trampoline_start;
@@ -69,22 +86,10 @@ pub fn init() linksection(r.init) void {
.execute = true,
});
// From Linux, on modern CPUs we can skip the long delay after INIT.
const skip_delay = switch (amd64.cpu_features.vendor) {
.Intel => amd64.cpu_features.family >= 0x06,
.Amd => amd64.cpu_features.family >= 0x0f,
.Hygon => amd64.cpu_features.family >= 0x18,
.Unknown => false,
};
const init_delay = r.Nanoseconds.init(
if (skip_delay) 0 else std.time.ns_per_ms * 10,
);
const sipi_delay = r.Nanoseconds.init(
if (skip_delay) std.time.ns_per_us * 10 else std.time.ns_per_us * 300,
);
const init_delay, const sipi_delay = delay_for_cpu();
const page: [*]u8 = @ptrFromInt(mm.p2v(0x8000));
const pcpu_start = @intFromPtr(&__percpu_start);
@memcpy(
page[0..trampoline_size],
@as([*]u8, @ptrFromInt(trampoline_start)),
@@ -94,19 +99,16 @@ pub fn init() linksection(r.init) void {
const data_phys: *ApData = @ptrFromInt(mm.p2v(0x8000 + data_offset));
const idtr = amd64.sidtr();
const offsets = mm.zone.gpa.alloc(usize, apic.apics.items.len + 1) catch
@panic("Failed to allocate AP local data offsets");
// Allocate per-cpu offsets for CPU-local data.
kep.impl.cpu_offsets = @ptrCast(offsets);
const percpu_size = @intFromPtr(&__percpu_end) -
@intFromPtr(&__percpu_start);
const percpu_size = @intFromPtr(&__percpu_end) - pcpu_start;
log.info("per-CPU data size: {} bytes", .{percpu_size});
// Allocate per-cpu offsets for CPU-local data.
kep.impl.cpu_offsets = @ptrCast(offsets);
kep.impl.cpu_offsets[0] = 0;
cpu_id_to_apic_id[0] = apic.get_id();
// Set up the AP data block.
@@ -117,7 +119,7 @@ pub fn init() linksection(r.init) void {
data_phys.xapic_virt_base = mm.p2v(apic.xapic_base_physical);
for (0..apic.apics.items.len, apic.apics.items) |i, apic_id| {
const cpu_id = i + 1;
const cpu_id: u32 = @as(u32, @intCast(i)) + 1;
cpu_id_to_apic_id[cpu_id] = apic_id;
// Allocate per-cpu data.
@@ -126,39 +128,32 @@ pub fn init() linksection(r.init) void {
@memcpy(
cpu_data,
@as([*]u8, @ptrCast(&__percpu_start))[0..percpu_size],
@as([*]u8, @ptrFromInt(pcpu_start))[0..percpu_size],
);
const self_offset_offset = @intFromPtr(&kep.impl.cpu_self_offset) -
@intFromPtr(&__percpu_start);
pcpu_start;
kep.impl.cpu_offsets[cpu_id] = @intFromPtr(cpu_data.ptr) -%
@intFromPtr(&__percpu_start);
kep.impl.cpu_offsets[cpu_id] = @intFromPtr(cpu_data.ptr) -% pcpu_start;
const off: *usize = @ptrCast(@alignCast(&cpu_data[self_offset_offset]));
// copy the offset into the AP's self_offset variable
off.* = kep.impl.cpu_offsets[cpu_id];
const stack_size = r.kib(16);
const stack_top = @intFromPtr(mm.heap.alloc(
stack_size,
ap_stack_size,
.DontWaitForMemory,
) catch
@panic("Failed to allocate AP stack")) + stack_size;
@panic("Failed to allocate AP stack")) + ap_stack_size;
start_stack.remote(@intCast(cpu_id)).* = stack_top & ~@as(usize, 15);
start_thread.remote(@intCast(cpu_id)).* = make_thread(
stack_top,
@intCast(cpu_id),
);
start_stack.remote(cpu_id).* = stack_top & ~@as(usize, 15);
start_thread.remote(cpu_id).* = make_thread(stack_top, cpu_id);
rtl.barrier.fence(.release);
// Send the INIT-SIPI-SIPI sequence to start the AP.
apic.send_init(apic_id);
ke.time.sleep(init_delay);
apic.send_sipi(apic_id, 0x08);
ke.time.sleep(sipi_delay);
+6 -4
View File
@@ -1,6 +1,7 @@
// Not sure if this should be in platform/pc or in kern/
const std = @import("std");
const amd64 = @import("arch");
const rtl = @import("rtl");
const r = @import("root");
const ke = r.ke;
@@ -54,7 +55,7 @@ pub fn init() linksection(r.init) void {
// Calculate how many ticks it costs to call tc_sleep.
for (0..calib_cost_runs) |_| {
const start = amd64.rdtsc();
ke.time.sleep(.init(0));
ke.time.sleep(rtl.Duration.ns(0));
const end = amd64.rdtsc();
calib_cost += (end - start);
}
@@ -62,7 +63,7 @@ pub fn init() linksection(r.init) void {
calib_cost /= calib_cost_runs;
const runs = 5;
const calib_time = 10 * std.time.ns_per_ms;
const calib_time = rtl.Duration.ms(10);
// Sleep for 10ms `runs` times and measure the TSC frequency,
// then average the middle values to get a stable estimate.
@@ -70,9 +71,10 @@ pub fn init() linksection(r.init) void {
for (0..runs) |i| {
const start = amd64.rdtsc();
ke.time.sleep(.init(calib_time));
ke.time.sleep(calib_time);
const end = amd64.rdtsc();
freqs[i] = (end - start - calib_cost) * (std.time.ns_per_s / calib_time);
freqs[i] = (end - start - calib_cost) *
(std.time.ns_per_s / calib_time.value);
}
// Sort and average middle values, discarding min and max.
+6 -2
View File
@@ -1,3 +1,4 @@
const rtl = @import("rtl");
const r = @import("root");
const rv64 = r.arch;
const ke = r.ke;
@@ -63,7 +64,10 @@ pub fn debug_read() u8 {
}
pub fn send_ipi(_: u32) void {}
pub fn arm_timer(ns: r.Nanoseconds) void {
const ticks = @max(1, @as(u64, @intCast((@as(u128, ns.value) * timebase_hz) / std.time.ns_per_s)));
pub fn arm_timer(ns: rtl.Duration) void {
const ticks = @max(1, @as(
u64,
@intCast((@as(u128, ns.value) * timebase_hz) / std.time.ns_per_s),
));
set_timer(read_time() +% ticks);
}
+2 -3
View File
@@ -3,7 +3,6 @@
//! functions.
const config = @import("config");
const r = @import("root");
const rtl = @import("rtl");
pub const impl = switch (config.arch) {
@@ -27,7 +26,7 @@ const ImplSchema = struct {
pub fn send_ipi(cpu: u32) void {
_ = cpu;
}
pub fn arm_timer(ns: r.Nanoseconds) void {
pub fn arm_timer(ns: rtl.Duration) void {
_ = ns;
}
};
@@ -58,7 +57,7 @@ pub inline fn debug_read() u8 {
}
/// Arm a one-shot timer to fire in `ns` nanoseconds.
pub inline fn arm_timer(ns: r.Nanoseconds) void {
pub inline fn arm_timer(ns: rtl.Duration) void {
return impl.arm_timer(ns);
}
+1 -1
View File
@@ -5,7 +5,7 @@ const mm = r.mm;
pub const thread = @import("thread.zig");
pub var turnstile_zone: mm.zone.TypedZone(kep.turnstile.Turnstile) = undefined;
pub var turnstile_zone: mm.zone.ZoneType(kep.turnstile.Turnstile) = undefined;
fn turnstile_ctor(ts: *kep.turnstile.Turnstile) void {
ts.* = .{
+1 -1
View File
@@ -12,7 +12,7 @@ pub const kernel_thread_stack_size = r.kib(16);
var reaper_item: ex.WorkItem = undefined;
var stack_item: ex.WorkItem = undefined;
var thread_zone: mm.zone.TypedZone(Thread) = undefined;
var thread_zone: mm.zone.ZoneType(Thread) = undefined;
/// Higher-level wrapper over a thread.
pub const Thread = struct {
+1 -1
View File
@@ -443,7 +443,7 @@ const Core = struct {
};
/// Scalable, lock-free hashtable backed by SMR.
pub fn Table(
pub fn TableType(
comptime T: type,
comptime link_field: []const u8,
comptime Context: type,
+1 -1
View File
@@ -4,7 +4,7 @@
const std = @import("std");
pub const HashTable = @import("hashtable.zig").Table;
pub const HashTableType = @import("hashtable.zig").TableType;
/// Singly-linked list
pub const SList = struct {
+1 -6
View File
@@ -16,17 +16,12 @@ const config = @import("config");
pub const init = ".text.init";
pub const percpu_init_set = rtl.LinkerSet("percpu_init", *const fn () void);
pub const percpu_init_set = rtl.LinkerSetType("percpu_init", *const fn () void);
pub const percpu = ".data.percpu";
pub const VAddr = usize;
pub const PAddr = usize;
pub const Nanoseconds = rtl.units.Nanoseconds;
pub const Microseconds = rtl.units.Microseconds;
pub const Milliseconds = rtl.units.Milliseconds;
pub const Seconds = rtl.units.Seconds;
pub var kernel_heap_base: usize = 0;
pub var kernel_pfndb_base: usize = 0;
+3 -3
View File
@@ -19,17 +19,17 @@ fn set_bf(node: *bst.Node, bf: u2) void {
node.parent.set_tag(bf);
}
pub fn AVLTree(
pub fn AvlTreeType(
comptime cmp: fn (*const bst.Node, *const bst.Node) std.math.Order,
) type {
return struct {
tree: bst.BST(cmp),
tree: bst.BstType(cmp),
const Tree = @This();
pub fn init() Tree {
return Tree{
.tree = bst.BST(cmp).init(),
.tree = bst.BstType(cmp).init(),
};
}
+27 -24
View File
@@ -16,7 +16,7 @@ fn bit_mask(comptime Word: type, bit_index: usize) Word {
}
/// Static bitmap that uses the most efficient underlying word size for N < 64
pub fn BitMap(comptime N: usize) type {
pub fn BitmapType(comptime N: usize) type {
return struct {
pub const Word = if (N > 32) u64 else ClosestType(N);
const Bitmap = @This();
@@ -107,7 +107,8 @@ pub fn BitMap(comptime N: usize) type {
/// Check whether all bits in the bitmap are equal to a given value.
pub fn is_all(self: *const Bitmap, value: bool) bool {
const v: Bitmap.Word = if (value) std.math.maxInt(Bitmap.Word)
const v: Bitmap.Word = if (value)
std.math.maxInt(Bitmap.Word)
else
0;
for (self.storage) |word| {
@@ -118,9 +119,9 @@ pub fn BitMap(comptime N: usize) type {
};
}
/// Atomic static bitmap with the same API as `BitMap`, but each operation
/// Atomic static bitmap with the same API as `BitmapType`, but each operation
/// accepts a memory ordering.
pub fn AtomicBitMap(comptime N: usize) type {
pub fn AtomicBitmapType(comptime N: usize) type {
return struct {
pub const Word = if (N > 32) u64 else ClosestType(N);
const Bitmap = @This();
@@ -250,7 +251,9 @@ pub fn AtomicBitMap(comptime N: usize) type {
value: bool,
comptime order: AtomicOrder,
) bool {
const v: Bitmap.Word = if (value) std.math.maxInt(Bitmap.Word) else
const v: Bitmap.Word = if (value)
std.math.maxInt(Bitmap.Word)
else
0;
for (self.storage) |word| {
if (word.load(order) != v) return false;
@@ -260,19 +263,19 @@ pub fn AtomicBitMap(comptime N: usize) type {
};
}
test "BitMap has proper underlying type" {
assert(BitMap(8).Word == u8);
assert(BitMap(10).Word == u16);
assert(BitMap(16).Word == u16);
assert(BitMap(21).Word == u32);
assert(BitMap(32).Word == u32);
assert(BitMap(64).Word == u64);
assert(BitMap(128).Word == u64);
assert(BitMap(1024).Word == u64);
test "BitmapType has proper underlying type" {
assert(BitmapType(8).Word == u8);
assert(BitmapType(10).Word == u16);
assert(BitmapType(16).Word == u16);
assert(BitmapType(21).Word == u32);
assert(BitmapType(32).Word == u32);
assert(BitmapType(64).Word == u64);
assert(BitmapType(128).Word == u64);
assert(BitmapType(1024).Word == u64);
}
test BitMap {
var bitmap = BitMap(10).init(false);
test BitmapType {
var bitmap = BitmapType(10).init(false);
assert(@TypeOf(bitmap).Word == u16);
assert(bitmap.get(0) == false);
@@ -285,8 +288,8 @@ test BitMap {
assert(bitmap.get(0) == false);
}
test "BitMap count and iter" {
var bitmap = BitMap(70).init(false);
test "BitmapType count and iter" {
var bitmap = BitmapType(70).init(false);
assert(bitmap.count() == 0);
{
@@ -314,7 +317,7 @@ test "BitMap count and iter" {
assert(bitmap.count() == 4);
var all = BitMap(70).init(true);
var all = BitmapType(70).init(true);
assert(all.count() == 70);
var all_it = all.iter();
@@ -325,8 +328,8 @@ test "BitMap count and iter" {
assert(expected == 70);
}
test AtomicBitMap {
var bitmap = AtomicBitMap(10).init(false);
test AtomicBitmapType {
var bitmap = AtomicBitmapType(10).init(false);
assert(@TypeOf(bitmap).Word == u16);
assert(bitmap.get(0, .seq_cst) == false);
@@ -341,8 +344,8 @@ test AtomicBitMap {
assert(bitmap.is_all(false, .seq_cst) == true);
}
test "AtomicBitMap count and iter" {
var bitmap = AtomicBitMap(70).init(false);
test "AtomicBitmapType count and iter" {
var bitmap = AtomicBitmapType(70).init(false);
assert(bitmap.count(.seq_cst) == 0);
{
@@ -370,7 +373,7 @@ test "AtomicBitMap count and iter" {
assert(bitmap.count(.seq_cst) == 4);
var all = AtomicBitMap(70).init(true);
var all = AtomicBitmapType(70).init(true);
assert(all.count(.seq_cst) == 70);
var all_it = all.iter(.seq_cst);
+7 -5
View File
@@ -1,10 +1,10 @@
const TaggedPtr = @import("tagged_ptr.zig").TaggedPtr;
const TaggedPtrType = @import("tagged_ptr.zig").TaggedPtrType;
const std = @import("std");
pub const Node = struct {
left: *Node,
right: *Node,
parent: TaggedPtr(Node),
parent: TaggedPtrType(Node),
};
pub var nil: Node = .{
@@ -16,7 +16,9 @@ pub var nil: Node = .{
/// Base struct for binary search trees.
/// This should be used as a field in other types that are based on
/// binary search trees, such as red-black trees or AVL trees.
pub fn BST(comptime cmp: fn (*const Node, *const Node) std.math.Order) type {
pub fn BstType(
comptime cmp: fn (*const Node, *const Node) std.math.Order,
) type {
return struct {
const Tree = @This();
@@ -166,8 +168,8 @@ fn node_cmp(a: *const Node, b: *const Node) std.math.Order {
return .eq;
}
test BST {
var tree = BST(node_cmp).init();
test BstType {
var tree = BstType(node_cmp).init();
var nodes = [_]MyNode{
.{ .value = 5, .node = undefined },
+1 -1
View File
@@ -1,7 +1,7 @@
const std = @import("std");
/// Type padded to a cache line.
pub fn CachePadded(comptime T: type) type {
pub fn CachePaddedType(comptime T: type) type {
return struct {
const cache_line = std.atomic.cache_line;
const value_size = @sizeOf(T);
+3 -3
View File
@@ -3,12 +3,12 @@
const std = @import("std");
/// Used for declaring global objects that get collected by the linker
/// into a `LinkerSet`. This works by declaring each set its own section
/// into a `LinkerSetType`. This works by declaring each set its own section
/// and abusing linker-generated `__start_<name>` and `__stop_<name>` symbols.
/// Each set stores pointers to objects, so `T` must be a pointer type.
pub fn LinkerSet(name: []const u8, comptime T: type) type {
pub fn LinkerSetType(name: []const u8, comptime T: type) type {
if (@typeInfo(T) != .pointer) {
@compileError("LinkerSet type must be a pointer type");
@compileError("LinkerSetType type must be a pointer type");
}
return struct {
+3 -3
View File
@@ -8,7 +8,7 @@ pub const Node = struct {
prev: ?*Node = null,
};
pub fn PairingHeap(
pub fn PairingHeapType(
comptime order: enum { Min, Max },
comptime cmp: fn (*Node, *Node) std.math.Order,
) type {
@@ -178,8 +178,8 @@ fn my_cmp(a: *Node, b: *Node) std.math.Order {
return std.math.order(ta.value, tb.value);
}
const MinHeap = PairingHeap(.Min, my_cmp);
const MaxHeap = PairingHeap(.Max, my_cmp);
const MinHeap = PairingHeapType(.Min, my_cmp);
const MaxHeap = PairingHeapType(.Max, my_cmp);
test "empty heap" {
var heap = MinHeap.init();
+7 -7
View File
@@ -15,11 +15,11 @@ fn set_color(node: *bst.Node, color: u2) void {
node.parent.set_tag(color);
}
pub fn RBTree(
pub fn RbTreeType(
comptime cmp: fn (*const bst.Node, *const bst.Node) std.math.Order,
) type {
return struct {
tree: bst.BST(cmp),
tree: bst.BstType(cmp),
const Tree = @This();
@@ -73,7 +73,7 @@ pub fn RBTree(
pub fn init() Tree {
return Tree{
.tree = bst.BST(cmp).init(),
.tree = bst.BstType(cmp).init(),
};
}
@@ -291,7 +291,7 @@ fn cmp_test_node(a: *const bst.Node, b: *const bst.Node) std.math.Order {
return .eq;
}
fn get_black_height(tree: *RBTree(cmp_test_node), node: *bst.Node) !usize {
fn get_black_height(tree: *RbTreeType(cmp_test_node), node: *bst.Node) !usize {
if (tree.tree.is_nil(node)) return 1;
const left_bh = try get_black_height(tree, node.left);
@@ -309,17 +309,17 @@ fn get_black_height(tree: *RBTree(cmp_test_node), node: *bst.Node) !usize {
return left_bh + if (get_color(node) == black) @as(usize, 1) else 0;
}
fn check_invariants(tree: *RBTree(cmp_test_node)) !void {
fn check_invariants(tree: *RbTreeType(cmp_test_node)) !void {
if (tree.tree.is_empty()) return;
try std.testing.expect(get_color(tree.tree.root) == black);
_ = try get_black_height(tree, tree.tree.root);
}
fn make_tree() RBTree(cmp_test_node) {
fn make_tree() RbTreeType(cmp_test_node) {
return .init();
}
fn insert_all(tree: *RBTree(cmp_test_node), nodes: []TestNode) !void {
fn insert_all(tree: *RbTreeType(cmp_test_node), nodes: []TestNode) !void {
for (nodes) |*n| try tree.insert(&n.node);
}
+12 -11
View File
@@ -1,22 +1,23 @@
const std = @import("std");
pub const units = @import("units.zig");
pub const Duration = @import("units.zig").Duration;
pub const Timestamp = @import("units.zig").Timestamp;
pub const List = @import("list.zig").List;
pub const SeqLock = @import("seqlock.zig").SeqLock;
pub const SeqLockType = @import("seqlock.zig").SeqLockType;
pub const barrier = @import("barrier.zig");
pub const cmdline = @import("cmdline.zig");
pub const pairing_heap = @import("pairing_heap.zig");
pub const PairingHeap = pairing_heap.PairingHeap;
pub const TaggedPtr = @import("tagged_ptr.zig").TaggedPtr;
pub const PairingHeapType = pairing_heap.PairingHeapType;
pub const TaggedPtrType = @import("tagged_ptr.zig").TaggedPtrType;
pub const bst = @import("bst.zig");
pub const BST = bst.BST;
pub const RBTree = @import("rbtree.zig").RBTree;
pub const AVLTree = @import("avl.zig").AVLTree;
pub const BitMap = @import("bitmap.zig").BitMap;
pub const AtomicBitMap = @import("bitmap.zig").AtomicBitMap;
pub const BstType = bst.BstType;
pub const RbTreeType = @import("rbtree.zig").RbTreeType;
pub const AvlTreeType = @import("avl.zig").AvlTreeType;
pub const BitmapType = @import("bitmap.zig").BitmapType;
pub const AtomicBitmapType = @import("bitmap.zig").AtomicBitmapType;
pub const HandoffList = @import("handoff.zig").HandoffList;
pub const LinkerSet = @import("linker_set.zig").LinkerSet;
pub const CachePadded = @import("cache_padded.zig").CachePadded;
pub const LinkerSetType = @import("linker_set.zig").LinkerSetType;
pub const CachePaddedType = @import("cache_padded.zig").CachePaddedType;
pub fn comptime_error(comptime msg: []const u8, args: anytype) void {
@compileError(std.fmt.comptimePrint(msg, args));
+1 -1
View File
@@ -3,7 +3,7 @@
const std = @import("std");
const rtl = @import("rtl");
pub fn SeqLock(comptime T: type) type {
pub fn SeqLockType(comptime T: type) type {
return struct {
const Lock = @This();
+3 -3
View File
@@ -1,6 +1,6 @@
/// Struct used to wrap a pointer with a 2-bit tag in the lower bits.
/// This assumes that the pointer is at least 4-byte aligned.
pub fn TaggedPtr(comptime T: type) type {
pub fn TaggedPtrType(comptime T: type) type {
return struct {
value: usize,
@@ -39,9 +39,9 @@ pub fn TaggedPtr(comptime T: type) type {
const std = @import("std");
test TaggedPtr {
test TaggedPtrType {
var x: u32 = 0;
var tagged = TaggedPtr(u32).init(&x, 2);
var tagged = TaggedPtrType(u32).init(&x, 2);
try std.testing.expectEqual(&x, tagged.ptr());
try std.testing.expectEqual(2, tagged.tag());
+41 -52
View File
@@ -1,67 +1,56 @@
/// Type-safe measurement units.
const std = @import("std");
const Dimension = enum { Time };
fn scale_value(
comptime from_scale: u64,
comptime to_scale: u64,
value: u64,
) u64 {
if (comptime from_scale >= to_scale) {
return value * comptime (from_scale / to_scale);
} else {
return value / comptime (to_scale / from_scale);
}
}
pub fn Unit(
comptime dim: Dimension,
comptime scale: u64,
comptime unit_suffix: []const u8,
) type {
return struct {
const UnitType = @This();
pub const dimension = dim;
pub const base_per_unit = scale;
pub const Duration = struct {
/// Value, in nanoseconds.
value: u64,
fn check(comptime T: type) void {
comptime {
if (!(@typeInfo(T) == .@"struct" and @hasDecl(T, "dimension")))
@compileError(@typeName(T) ++ " is not a unit");
if (T.dimension != dim)
@compileError("cannot convert between " ++
@tagName(T.dimension) ++
" and " ++ @tagName(dim));
}
pub fn ns(count: u64) Duration {
return .{ .value = count };
}
pub fn init(value: u64) UnitType {
return .{ .value = value };
pub fn us(count: u64) Duration {
return .{ .value = std.time.ns_per_us * count };
}
pub fn to(self: UnitType, comptime T: type) T {
check(T);
return T.init(scale_value(scale, T.base_per_unit, self.value));
pub fn ms(count: u64) Duration {
return .{ .value = std.time.ns_per_ms * count };
}
pub fn from(other: anytype) UnitType {
const From = @TypeOf(other);
check(From);
return UnitType.init(
scale_value(From.base_per_unit, scale, other.value),
);
pub fn seconds(count: u64) Duration {
return .{ .value = std.time.ns_per_s * count };
}
pub inline fn format(self: UnitType, writer: *std.Io.Writer) !void {
try writer.print("{} {s}", .{ self.value, unit_suffix });
pub fn to_us(duration: Duration) u64 {
return duration.value / std.time.ns_per_us;
}
};
}
pub const Nanoseconds = Unit(.Time, 1, "ns");
pub const Microseconds = Unit(.Time, std.time.ns_per_us, "us");
pub const Milliseconds = Unit(.Time, std.time.ns_per_ms, "ms");
pub const Seconds = Unit(.Time, std.time.ns_per_s, "s");
pub fn to_ms(duration: Duration) u64 {
return duration.value / std.time.ns_per_ms;
}
pub fn to_seconds(duration: Duration) u64 {
return duration.value / std.time.ns_per_s;
}
};
pub const Timestamp = struct {
/// Value, in nanoseconds.
value: u64,
pub fn add(now: Timestamp, duration: Duration) Timestamp {
return .{ .value = now.value + duration.value };
}
pub fn to_us(timestamp: Timestamp) u64 {
return timestamp.value / std.time.ns_per_us;
}
pub fn to_ms(timestamp: Timestamp) u64 {
return timestamp.value / std.time.ns_per_ms;
}
pub fn to_seconds(timestamp: Timestamp) u64 {
return timestamp.value / std.time.ns_per_s;
}
};
+2 -1
View File
@@ -1,5 +1,6 @@
//! Fireworks test ported from the Boron operating system.
const std = @import("std");
const rtl = @import("rtl");
const r = @import("root");
const ke = r.ke;
const mm = r.mm;
@@ -110,7 +111,7 @@ fn get_random_color() u32 {
fn sleep(ms: usize, continuation: ?ke.Continuation) void {
_ = ke.wait.wait_any(&.{}, "sleep", .{
.timeout = .from(r.Milliseconds.init(ms)),
.timeout = rtl.Duration.ms(ms),
.continuation = continuation,
}) catch {
return;
+4 -3
View File
@@ -88,7 +88,7 @@ const Errors = struct {
line_num: usize,
) void {
errors.emit(
"{s}:{d}: error: type name '{s}' should be PascalCase\n",
"{s}:{d}: error: type name '{s}' should be PascalCase + Type\n",
.{ file.path, line_num, name },
);
}
@@ -298,9 +298,11 @@ fn tidy_function(
errors.add_function_not_documented(name, file, lineno + 1);
}
if (returns_type and std.ascii.isLower(name[0])) {
if (returns_type) {
if (std.ascii.isLower(name[0]) or !std.mem.endsWith(u8, name, "Type")) {
errors.add_wrong_fn_type_name(name, file, lineno + 1);
}
}
if (!returns_type) {
blk: for (name) |c| {
@@ -359,7 +361,6 @@ fn tidy_banned(file: SourceFile, errors: *Errors) void {
.{ "catch unreachable", "proper error handling or documentation" },
.{ "catch {}", "proper error handling or documentation" },
.{ "= .init(", "full type name" },
.{ ".?", "orelse unreachable" },
};
for (ban_list) |ban_item| {