From cefc69e1b5ef04ef555f5eb82325dea49eee1f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gr=C3=A9goire?= Date: Thu, 8 Oct 2026 18:01:42 -0400 Subject: [PATCH] all: more tidying --- src/arch/amd64/paravirt/kvm.zig | 2 +- src/arch/amd64/root.zig | 10 +-- src/ex/string.zig | 4 +- src/ex/vmem.zig | 4 +- src/ex/work.zig | 16 ++--- src/kern/amd64/int.zig | 2 +- src/kern/cpu.zig | 10 +-- src/kern/dpc.zig | 6 +- src/kern/ipi.zig | 2 +- src/kern/ipl.zig | 4 +- src/kern/log.zig | 4 +- src/kern/log_ring.zig | 9 +-- src/kern/queue.zig | 2 +- src/kern/root.zig | 6 +- src/kern/sched.zig | 24 +++---- src/kern/shootdown.zig | 2 +- src/kern/smr.zig | 119 ++++++++++++++++++-------------- src/kern/spinlock.zig | 8 +-- src/kern/thread.zig | 6 +- src/kern/time.zig | 12 ++-- src/kern/timer.zig | 18 ++--- src/kern/tunable.zig | 6 +- src/kern/wait.zig | 2 +- src/kern/warden.zig | 6 +- src/loader/mem.zig | 6 +- src/mm/balance.zig | 5 +- src/mm/zone.zig | 26 +++---- src/platform/pc/apic.zig | 19 ++--- src/platform/pc/impl.zig | 5 +- src/platform/pc/smp.zig | 71 +++++++++---------- src/platform/pc/tsc.zig | 10 +-- src/platform/riscv64/impl.zig | 8 ++- src/platform/root.zig | 5 +- src/proc/private.zig | 2 +- src/proc/thread.zig | 2 +- src/rcu/hashtable.zig | 2 +- src/rcu/root.zig | 2 +- src/root.zig | 7 +- src/rtl/avl.zig | 6 +- src/rtl/bitmap.zig | 51 +++++++------- src/rtl/bst.zig | 12 ++-- src/rtl/cache_padded.zig | 2 +- src/rtl/linker_set.zig | 6 +- src/rtl/pairing_heap.zig | 6 +- src/rtl/rbtree.zig | 14 ++-- src/rtl/root.zig | 23 +++--- src/rtl/seqlock.zig | 2 +- src/rtl/tagged_ptr.zig | 6 +- src/rtl/units.zig | 97 ++++++++++++-------------- src/tests/fireworks.zig | 3 +- src/tidy.zig | 9 +-- 51 files changed, 352 insertions(+), 339 deletions(-) diff --git a/src/arch/amd64/paravirt/kvm.zig b/src/arch/amd64/paravirt/kvm.zig index 5c51300..9600d01 100644 --- a/src/arch/amd64/paravirt/kvm.zig +++ b/src/arch/amd64/paravirt/kvm.zig @@ -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 { diff --git a/src/arch/amd64/root.zig b/src/arch/amd64/root.zig index ae36435..60183af 100644 --- a/src/arch/amd64/root.zig +++ b/src/arch/amd64/root.zig @@ -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, diff --git a/src/ex/string.zig b/src/ex/string.zig index 944ad61..b5bf30f 100644 --- a/src/ex/string.zig +++ b/src/ex/string.zig @@ -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; diff --git a/src/ex/vmem.zig b/src/ex/vmem.zig index 826c1ef..31d9503 100644 --- a/src/ex/vmem.zig +++ b/src/ex/vmem.zig @@ -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. diff --git a/src/ex/work.zig b/src/ex/work.zig index e6f7c9c..953e283 100644 --- a/src/ex/work.zig +++ b/src/ex/work.zig @@ -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. diff --git a/src/kern/amd64/int.zig b/src/kern/amd64/int.zig index 6c51043..a274957 100644 --- a/src/kern/amd64/int.zig +++ b/src/kern/amd64/int.zig @@ -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(); diff --git a/src/kern/cpu.zig b/src/kern/cpu.zig index 1dd2cfb..23663b4 100644 --- a/src/kern/cpu.zig +++ b/src/kern/cpu.zig @@ -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); diff --git a/src/kern/dpc.zig b/src/kern/dpc.zig index 27235fd..ad4e33d 100644 --- a/src/kern/dpc.zig +++ b/src/kern/dpc.zig @@ -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 }, ); } diff --git a/src/kern/ipi.zig b/src/kern/ipi.zig index 475130c..d7cfc94 100644 --- a/src/kern/ipi.zig +++ b/src/kern/ipi.zig @@ -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); diff --git a/src/kern/ipl.zig b/src/kern/ipl.zig index 8c3d4a6..b7dd1db 100644 --- a/src/kern/ipl.zig +++ b/src/kern/ipl.zig @@ -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), }); diff --git a/src/kern/log.zig b/src/kern/log.zig index ff08232..47d773a 100644 --- a/src/kern/log.zig +++ b/src/kern/log.zig @@ -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(); } diff --git a/src/kern/log_ring.zig b/src/kern/log_ring.zig index e1be7a8..69ddac8 100644 --- a/src/kern/log_ring.zig +++ b/src/kern/log_ring.zig @@ -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; diff --git a/src/kern/queue.zig b/src/kern/queue.zig index 30e9657..801f9b3 100644 --- a/src/kern/queue.zig +++ b/src/kern/queue.zig @@ -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; diff --git a/src/kern/root.zig b/src/kern/root.zig index ff0386e..48d3054 100644 --- a/src/kern/root.zig +++ b/src/kern/root.zig @@ -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; diff --git a/src/kern/sched.zig b/src/kern/sched.zig index f3458fc..88d68e9 100644 --- a/src/kern/sched.zig +++ b/src/kern/sched.zig @@ -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, }, diff --git a/src/kern/shootdown.zig b/src/kern/shootdown.zig index dd65cd8..3efc981 100644 --- a/src/kern/shootdown.zig +++ b/src/kern/shootdown.zig @@ -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; diff --git a/src/kern/smr.zig b/src/kern/smr.zig index e2a68b9..a3217bc 100644 --- a/src/kern/smr.zig +++ b/src/kern/smr.zig @@ -132,59 +132,7 @@ fn scan( } if (dom.preempt) { - rtl.barrier.fence(.seq_cst); - - // Preemption is enabled, check stalled readers. - for (0..ke.ncpus) |i| { - const cpu = &dom.cpus[i]; - - var seq = cpu.stall_seq.load(.monotonic); - - while (seq != seq_invalid) { - if (seq < clk_read) { - seq = clk_read; - } - - if (goal <= seq) { - // The goal has been reached. - break; - } - - if (!should_wait) break; - - const ipl = ke.ipl.raise(.Dispatch); - - cpu.stall_lock.acquire_no_ipl(); - cpu.stall_goal = goal; - cpu.stall_lock.release_no_ipl(); - - const ts = kep.turnstile.lookup(cpu); - - seq = cpu.stall_seq.load(.monotonic); - - // Re-check under the chain lock. - if (seq == seq_invalid or goal <= seq) { - kep.turnstile.exit(cpu, ts); - ke.ipl.lower(ipl); - break; - } - - kep.turnstile.block( - ts, - cpu, - .{ .shared = &cpu.stalled }, - .Exclusive, - ); - - ke.ipl.lower(ipl); - seq = cpu.stall_seq.load(.monotonic); - } - - if (seq != seq_invalid) { - // Update the minimum read sequence. - read_seq = @min(read_seq, seq); - } - } + read_seq = scan_preempt(dom, clk_read, goal, should_wait, read_seq); } // The orderings here serve two purposes: @@ -215,6 +163,71 @@ fn scan( 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| { + const cpu = &dom.cpus[i]; + + var seq = cpu.stall_seq.load(.monotonic); + + while (seq != seq_invalid) { + if (seq < clk_read) { + seq = clk_read; + } + + if (goal <= seq) { + // The goal has been reached. + break; + } + + if (!should_wait) break; + + const ipl = ke.ipl.raise(.Dispatch); + + cpu.stall_lock.acquire_no_ipl(); + cpu.stall_goal = goal; + cpu.stall_lock.release_no_ipl(); + + const ts = kep.turnstile.lookup(cpu); + + seq = cpu.stall_seq.load(.monotonic); + + // Re-check under the chain lock. + if (seq == seq_invalid or goal <= seq) { + kep.turnstile.exit(cpu, ts); + ke.ipl.lower(ipl); + break; + } + + kep.turnstile.block( + ts, + cpu, + .{ .shared = &cpu.stalled }, + .Exclusive, + ); + + ke.ipl.lower(ipl); + seq = cpu.stall_seq.load(.monotonic); + } + + if (seq != seq_invalid) { + // Update the minimum read sequence. + read_seq = @min(read_seq, seq); + } + } + + return read_seq; +} + /// Poll to determine whether all CPUs have reached `goal`. /// If wait is true then this will spinloop until the goal is met. /// This will advance the domain read sequence if applicable. diff --git a/src/kern/spinlock.zig b/src/kern/spinlock.zig index 3ca2dfd..8451734 100644 --- a/src/kern/spinlock.zig +++ b/src/kern/spinlock.zig @@ -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, diff --git a/src/kern/thread.zig b/src/kern/thread.zig index 2e974ee..6e3834d 100644 --- a/src/kern/thread.zig +++ b/src/kern/thread.zig @@ -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 = .{}, }; } diff --git a/src/kern/time.zig b/src/kern/time.zig index fbc12d3..db9c7d1 100644 --- a/src/kern/time.zig +++ b/src/kern/time.zig @@ -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) { diff --git a/src/kern/timer.zig b/src/kern/timer.zig index 9d7e753..6872c0e 100644 --- a/src/kern/timer.zig +++ b/src/kern/timer.zig @@ -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; } diff --git a/src/kern/tunable.zig b/src/kern/tunable.zig index 6d5c1d3..2457c63 100644 --- a/src/kern/tunable.zig +++ b/src/kern/tunable.zig @@ -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, diff --git a/src/kern/wait.zig b/src/kern/wait.zig index 74a3ec9..e542956 100644 --- a/src/kern/wait.zig +++ b/src/kern/wait.zig @@ -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, }; diff --git a/src/kern/warden.zig b/src/kern/warden.zig index a107347..21a65c9 100644 --- a/src/kern/warden.zig +++ b/src/kern/warden.zig @@ -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. diff --git a/src/loader/mem.zig b/src/loader/mem.zig index 111e30b..72cb958 100644 --- a/src/loader/mem.zig +++ b/src/loader/mem.zig @@ -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; diff --git a/src/mm/balance.zig b/src/mm/balance.zig index ec9e73d..b80b09c 100644 --- a/src/mm/balance.zig +++ b/src/mm/balance.zig @@ -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()), .{}, ); } diff --git a/src/mm/zone.zig b/src/mm/zone.zig index ba60bfb..9b32120 100644 --- a/src/mm/zone.zig +++ b/src/mm/zone.zig @@ -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, diff --git a/src/platform/pc/apic.zig b/src/platform/pc/apic.zig index 70dd335..4b2932b 100644 --- a/src/platform/pc/apic.zig +++ b/src/platform/pc/apic.zig @@ -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. diff --git a/src/platform/pc/impl.zig b/src/platform/pc/impl.zig index 9985fe6..c356f14 100644 --- a/src/platform/pc/impl.zig +++ b/src/platform/pc/impl.zig @@ -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 { diff --git a/src/platform/pc/smp.zig b/src/platform/pc/smp.zig index 6434ecb..3a93140 100644 --- a/src/platform/pc/smp.zig +++ b/src/platform/pc/smp.zig @@ -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); diff --git a/src/platform/pc/tsc.zig b/src/platform/pc/tsc.zig index 68da5c0..4904d30 100644 --- a/src/platform/pc/tsc.zig +++ b/src/platform/pc/tsc.zig @@ -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. diff --git a/src/platform/riscv64/impl.zig b/src/platform/riscv64/impl.zig index e4c8b52..53ac8ae 100644 --- a/src/platform/riscv64/impl.zig +++ b/src/platform/riscv64/impl.zig @@ -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); } diff --git a/src/platform/root.zig b/src/platform/root.zig index 42bd26c..05402ea 100644 --- a/src/platform/root.zig +++ b/src/platform/root.zig @@ -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); } diff --git a/src/proc/private.zig b/src/proc/private.zig index 7449adc..2286cd7 100644 --- a/src/proc/private.zig +++ b/src/proc/private.zig @@ -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.* = .{ diff --git a/src/proc/thread.zig b/src/proc/thread.zig index 474d576..8649d4a 100644 --- a/src/proc/thread.zig +++ b/src/proc/thread.zig @@ -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 { diff --git a/src/rcu/hashtable.zig b/src/rcu/hashtable.zig index 548e979..4ba74a7 100644 --- a/src/rcu/hashtable.zig +++ b/src/rcu/hashtable.zig @@ -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, diff --git a/src/rcu/root.zig b/src/rcu/root.zig index 2914d91..cb263c4 100644 --- a/src/rcu/root.zig +++ b/src/rcu/root.zig @@ -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 { diff --git a/src/root.zig b/src/root.zig index 36609a5..4f14a4f 100644 --- a/src/root.zig +++ b/src/root.zig @@ -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; diff --git a/src/rtl/avl.zig b/src/rtl/avl.zig index 7c84125..8193cd9 100644 --- a/src/rtl/avl.zig +++ b/src/rtl/avl.zig @@ -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(), }; } diff --git a/src/rtl/bitmap.zig b/src/rtl/bitmap.zig index 8199817..3987b18 100644 --- a/src/rtl/bitmap.zig +++ b/src/rtl/bitmap.zig @@ -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); diff --git a/src/rtl/bst.zig b/src/rtl/bst.zig index d43ce5e..b1923db 100644 --- a/src/rtl/bst.zig +++ b/src/rtl/bst.zig @@ -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 }, diff --git a/src/rtl/cache_padded.zig b/src/rtl/cache_padded.zig index ba7aaf7..e0acf59 100644 --- a/src/rtl/cache_padded.zig +++ b/src/rtl/cache_padded.zig @@ -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); diff --git a/src/rtl/linker_set.zig b/src/rtl/linker_set.zig index 389605d..1d11d13 100644 --- a/src/rtl/linker_set.zig +++ b/src/rtl/linker_set.zig @@ -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_` and `__stop_` 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 { diff --git a/src/rtl/pairing_heap.zig b/src/rtl/pairing_heap.zig index 1053d98..7b7a585 100644 --- a/src/rtl/pairing_heap.zig +++ b/src/rtl/pairing_heap.zig @@ -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(); diff --git a/src/rtl/rbtree.zig b/src/rtl/rbtree.zig index 6e96a90..9f6dd14 100644 --- a/src/rtl/rbtree.zig +++ b/src/rtl/rbtree.zig @@ -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); } diff --git a/src/rtl/root.zig b/src/rtl/root.zig index 6430265..ccf1029 100644 --- a/src/rtl/root.zig +++ b/src/rtl/root.zig @@ -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)); diff --git a/src/rtl/seqlock.zig b/src/rtl/seqlock.zig index ba59808..5e2d8a2 100644 --- a/src/rtl/seqlock.zig +++ b/src/rtl/seqlock.zig @@ -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(); diff --git a/src/rtl/tagged_ptr.zig b/src/rtl/tagged_ptr.zig index 1053b74..9eadbde 100644 --- a/src/rtl/tagged_ptr.zig +++ b/src/rtl/tagged_ptr.zig @@ -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()); diff --git a/src/rtl/units.zig b/src/rtl/units.zig index dccd159..8ccb4da 100644 --- a/src/rtl/units.zig +++ b/src/rtl/units.zig @@ -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, +pub const Duration = struct { + /// Value, in nanoseconds. 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 ns(count: u64) Duration { + return .{ .value = count }; } -} -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 fn us(count: u64) Duration { + return .{ .value = std.time.ns_per_us * count }; + } - value: u64, + pub fn ms(count: u64) Duration { + return .{ .value = std.time.ns_per_ms * count }; + } - 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 seconds(count: u64) Duration { + return .{ .value = std.time.ns_per_s * count }; + } - pub fn init(value: u64) UnitType { - return .{ .value = value }; - } + pub fn to_us(duration: Duration) u64 { + return duration.value / std.time.ns_per_us; + } - 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 to_ms(duration: Duration) u64 { + return duration.value / std.time.ns_per_ms; + } - 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 to_seconds(duration: Duration) u64 { + return duration.value / std.time.ns_per_s; + } +}; - pub inline fn format(self: UnitType, writer: *std.Io.Writer) !void { - try writer.print("{} {s}", .{ self.value, unit_suffix }); - } - }; -} +pub const Timestamp = struct { + /// Value, in nanoseconds. + value: u64, -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 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; + } +}; diff --git a/src/tests/fireworks.zig b/src/tests/fireworks.zig index d30b9fa..d509209 100644 --- a/src/tests/fireworks.zig +++ b/src/tests/fireworks.zig @@ -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; diff --git a/src/tidy.zig b/src/tidy.zig index 94af9a1..5ef739a 100644 --- a/src/tidy.zig +++ b/src/tidy.zig @@ -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,8 +298,10 @@ fn tidy_function( errors.add_function_not_documented(name, file, lineno + 1); } - if (returns_type and std.ascii.isLower(name[0])) { - errors.add_wrong_fn_type_name(name, file, lineno + 1); + 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) { @@ -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| {