kernel: SIGALRM + per-process interval timer (SYS_RTC_ALARM)#337
Open
kernel: SIGALRM + per-process interval timer (SYS_RTC_ALARM)#337
Conversation
f84a719 to
95f980e
Compare
…lag)
Cosmetic prep for SIGALRM sharing the IRQ-tail macro and the nesting
flag — both will need to handle two signals. This commit just
renames; the macro body still walks pending_sigint only and the
nesting flag still blocks just SIGINT (the only signal that exists).
Later tasks add SIGALRM and extend the macro body to walk both
pending bits.
Renames:
- in_sigint_handler -> in_signal_handler (entry.asm global +
program_enter init + signal.c references)
- SIGINT_TAIL_CHECK -> SIGNAL_TAIL_CHECK (macro definition in
irq_tail.inc + every call site: entry.asm x 3, syscall.asm x 1,
ps2.c x 1, fdc.c x 1)
- File-header generalizations in irq_tail.inc and signal.c from
"SIGINT" to "Signal" (the dispatch surface is signal-agnostic
even when only SIGINT exists).
No behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symbol-only addition, no behavior change. SYS_RTC_ALARM goes at the start of the RTC group (30h) and the existing four entries shift up by one (DATETIME 31h, MILLIS 32h, SLEEP 33h, UPTIME 34h) so the group stays alphabetical-monotonic. SIGALRM uses the POSIX-aligned signum. signal.h gets the matching SIGALRM macro for userland. cc.py's NAMED_CONSTANTS and tests/bboeos.h gain matching SIG_DFL / SIG_IGN / SIGALRM / SIGINT entries so test programs can reference the names symbolically. The SYS_RTC_MILLIS literal in tools/libc/syscall.c's gettimeofday shifts to 0x32 to match. No other code uses these symbols by hex literal yet — later tasks wire them in symbolically (NASM resolves SYS_RTC_* by name). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sigalrm_handler / pending_sigalrm / alarm_deadline / alarm_interval join the existing SIGINT globals. program_enter resets all of them so alarms do not survive exec (POSIX setitimer behavior). No reader or writer yet — later tasks wire in the IRQ 0 deadline check, the SYS_RTC_ALARM dispatch, and the macro/dispatcher generalization. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The kill path now prints a signal-specific banner — '^C' for SIGINT, '^A' for SIGALRM, '^?' for the corrupt-sigcontext validation failure in signal_resume_after_handler. Callers must load EDX before jumping; SIGINT_TAIL_CHECK and the redelivery branch now do so explicitly. No behavior change yet for SIGINT (banner unchanged). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both dispatchers now take EAX = handler, EDX = signum from the caller (SIGNAL_TAIL_CHECK or the redelivery branch). signal_dispatch_user writes EDX into sigcontext + 4 (the signum slot was reserved for this exactly), stashes the handler in EBP across the iret-frame reads (EAX gets clobbered by the [esp + 32/40/44] loads), and clears the pending bit corresponding to the signum. signal_resume_after_handler's redelivery branch walks both pending bits in priority order (SIGINT first), preserving the existing SIGINT-only behavior when no SIGALRM is ever raised. The hardcoded SIGINT constant and [sigint_handler] reference are gone from signal.c — the file is now signal-agnostic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The IRQ-tail macro now checks pending_sigint first (lower signum = higher priority) and falls through to pending_sigalrm. When pending_sigalrm is never set (current state — Task 8 wires the IRQ 0 deadline check next), behavior is byte-identical to the old SIGINT-only path. Loads EDX = signum and EAX = handler before jumping to the dispatcher; matches the contract Tasks 4-5 set. SIG_IGN now clears the right pending bit based on EDX. Also updates signal.c: alias-block comment lists pending_sigalrm and sigalrm_handler as additional kernel globals referenced from inline asm; "the SIGINT preempted it" and "SIGINT can deliver again" generalized to signal-agnostic phrasing matching the dispatchers' new contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EBX = SIGALRM (14) is now a legal signum; the handler slot is routed to sigalrm_handler vs sigint_handler based on EBX. Handler validation rules unchanged (SIG_DFL / SIG_IGN / [PROGRAM_BASE, KERNEL_VIRT_BASE)). No way to actually fire a SIGALRM yet — that needs SYS_RTC_ALARM (next commit) and the IRQ 0 deadline check. cc.py gains a signal() builtin so cc.py-compiled programs (tests, shell, edit) can call it directly; matching declaration in tests/bboeos.h for clang -fsyntax-only. docs/syscalls.md sys_signal row updated to reflect the broadened signum set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EBX = ms_until_first (0 = cancel), ECX = ms_interval (0 = one-shot). Returns EAX = ms remaining on prior alarm (or 0). CF always clear — no error path; any EBX/ECX combination is legal. Stores the deadline as a system_ticks value (system_ticks runs at 1 kHz so the conversion is identity). IRQ 0 will fire SIGALRM when system_ticks crosses alarm_deadline (next commit). cc.py gains an alarm_ms() builtin; tests/bboeos.h gains the matching declaration for clang -fsyntax-only. docs/syscalls.md gets an rtc_alarm row. alarm_cancel test exercises the cancel path (EBX=0 returns ms remaining on previously-armed alarm). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After inc system_ticks, check alarm_deadline; if armed and reached, set pending_sigalrm and either re-arm (alarm_interval != 0) or clear (one-shot). Coalescing matches POSIX: a second fire while pending_sigalrm is already 1 is dropped. Constant-time addition preserves the O(1) ISR latency invariant. SIGNAL_TAIL_CHECK at the end of the ISR delivers to the user handler if one is installed and we interrupted CPL=3 — that's the existing IRQ-tail path, unchanged. End-to-end SIGALRM smoke tests (oneshot, repeat, default_kill, coalesce, nesting) cover the user-facing alarm contract from this point forward. docs/architecture.md gains a SIGALRM section. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ns EINTR libc gains alarm() (POSIX seconds) and alarm_ms() (BBoeOS ms+interval extension). Both wrap SYS_RTC_ALARM (30h). alarm() returns prior remaining seconds (rounded up); alarm_ms() exposes the kernel's native ms+interval shape — used by test programs and (eventually) Doom's OPL queue. rtc_sleep_ms is now interruptible: pending_sigint and pending_sigalrm short-circuit the busy-wait loop with CF=1. SYS_RTC_SLEEP propagates as AL=ERROR_INTERRUPTED so libc's eventual sleep() wrapper can surface EINTR (and a future signal-aware sleep retry loop can do the right thing). docs/syscalls.md rtc_sleep row mentions the EINTR return path; alarm_during_sleep test arms a 50 ms alarm, calls sleep(500), and asserts the sleep returned around 50 ms with CF=1 and the handler having run exactly once. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both blocking syscall paths checked only pending_sigint; extend the check to pending_sigalrm so an alarm fired during read(0, ...) or MIDI_IOCTL_DRAIN unblocks promptly with ERROR_INTERRUPTED. CHANGELOG entry summarizes the user-visible surface added by the SIGALRM feature: SYS_RTC_ALARM (34h), SIGALRM signal, EINTR-aware blocking syscalls, libc alarm()/alarm_ms() wrappers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SYS_RTC_ALARM(34h).EBX = ms_until_first(0 = cancel),ECX = ms_interval(0 = one-shot); returnsEAX= ms remaining on prior alarm.SIGALRM(signum 14) via the existing SIGINT user-handler plumbing — vDSO sigreturn trampoline, 52-byte sigcontext, EFLAGS sanitization, IRQ-tail dispatch. The dispatchers andSIGNAL_TAIL_CHECK(renamed fromSIGINT_TAIL_CHECK) takeEAX = handler, EDX = signumfrom the caller, so adding a third signal in the future is a per-signal global + a macro branch.signal(7)-aligned). Kill banner is^A(SIGINT keeps^C; corrupt-sigcontext kills print^?).rtc_sleep_ms,fd_read_console, andMIDI_IOCTL_DRAINreturnEINTRwhen interrupted by either signal — same cooperative-interruption pattern that already existed for SIGINT.alarm()(POSIX seconds) andalarm_ms()(BBoeOS ms-with-interval extension).tools/libc/include/signal.hgetsSIGALRM.signal()andalarm_ms()builtins for kernel-side/test C code;tests/bboeos.hmirrors the declarations for clang's host-side typecheck.Motivation: lets userland (eventually
tools/doom/opl_bboeos.c) schedule async OPL writes via Chocolate Doom's nativeopl_queue.cpriority queue instead of the current 6-byte midi-ring shim. Doom integration is a follow-up PR — kept out of scope here.Test Plan
Six new end-to-end QEMU-boot smoke tests in
tests/programs/:alarm_oneshot— handler runs exactly once after 50 ms.alarm_repeat— 20 ms repeating alarm fires 8-11 times in 200 ms.alarm_cancel—alarm_ms(0,0)cancels a pending one-shot.alarm_default_kill— arming SIGALRM with no handler kills the program;^Abanner distinguishes it from SIGINT's^C.alarm_coalesce— 1 ms repeat with 10 ms handler ⇒ ~10 fires per 100 ms (not 100), proving pending_sigalrm coalesces.alarm_nesting— SIGINT handler busy-waits while SIGALRM becomes pending;signal_resume_after_handler's redelivery branch dispatches SIGALRM before resuming main.alarm_during_sleep—rtc_sleep_ms(500)interrupted by 50 ms alarm returns CF=1 / EINTR after ~50 ms.Local CI matrix run on the branch:
test_unit(254/254),test_archive(13/13),test_kernel_archive(12/12),test_asm(20/20),test_bboefs(6/6),test_cc_bits(86/86),test_cc_compatibility(43/43)test_programs_bbfs --slow(40/40),test_programs_ext2 --slow(71/71),test_programs_floppy(37/37)test_floppy_boot,test_ps2,test_drawtest_doom_music_qemu/test_doom_sound_qemu— skipped locally (nowads/doom1.wadin env). The change doesn't touch the Doom port; CI will exercise these.🤖 Generated with Claude Code