From: Tulio A M Mendes Date: Fri, 13 Feb 2026 22:34:04 +0000 (-0300) Subject: refactor: replace O(N) alarm scan with O(1) sorted alarm queue X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=c9c0aae8d35cbe9cb25dc97fdb980257565ccfb1;p=AdrOS.git refactor: replace O(N) alarm scan with O(1) sorted alarm queue Phase D1 complete — alarm delivery now uses a sorted doubly-linked queue identical in design to the sleep queue. - process.h: added alarm_next, alarm_prev, in_alarm_queue fields - scheduler.c: added alarm_queue_insert/alarm_queue_remove helpers, alarm_head pointer, and public process_alarm_set() API - process_wake_check: replaced O(N) scan of all processes with O(1) pop from sorted alarm queue head - syscall.c: alarm() syscall now routes through process_alarm_set() which atomically manages the queue under sched_lock - Alarm queue cleanup on process exit (process_exit_notify) and signal kill (SIG_KILL path) 20/20 smoke tests pass. --- diff --git a/include/process.h b/include/process.h index 2976ac9..0591915 100644 --- a/include/process.h +++ b/include/process.h @@ -110,6 +110,10 @@ struct process { struct process* sleep_prev; int in_sleep_queue; + struct process* alarm_next; // sorted alarm queue (by alarm_tick) + struct process* alarm_prev; + int in_alarm_queue; + /* Thread support */ uint32_t tgid; /* Thread group ID (== pid for group leader) */ uint32_t flags; /* PROCESS_FLAG_* */ @@ -132,6 +136,9 @@ void process_sleep(uint32_t ticks); // Wake up sleeping processes (called by timer) void process_wake_check(uint32_t current_tick); +// Set or cancel an alarm for a process (returns old alarm_tick) +uint32_t process_alarm_set(struct process* p, uint32_t tick); + // The magic function that switches stacks (Implemented in Assembly) // old_esp_ptr: Address where we save the OLD process's ESP // new_esp: The NEW process's ESP to load diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index a1f16c0..8bbc8ce 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -158,6 +158,37 @@ static void sleep_queue_remove(struct process* p) { p->in_sleep_queue = 0; } +/* ---------- Sorted alarm queue (by alarm_tick) ---------- */ +static struct process* alarm_head = NULL; + +static void alarm_queue_insert(struct process* p) { + p->in_alarm_queue = 1; + if (!alarm_head || p->alarm_tick <= alarm_head->alarm_tick) { + p->alarm_prev = NULL; + p->alarm_next = alarm_head; + if (alarm_head) alarm_head->alarm_prev = p; + alarm_head = p; + return; + } + struct process* cur = alarm_head; + while (cur->alarm_next && cur->alarm_next->alarm_tick < p->alarm_tick) + cur = cur->alarm_next; + p->alarm_next = cur->alarm_next; + p->alarm_prev = cur; + if (cur->alarm_next) cur->alarm_next->alarm_prev = p; + cur->alarm_next = p; +} + +static void alarm_queue_remove(struct process* p) { + if (!p->in_alarm_queue) return; + if (p->alarm_prev) p->alarm_prev->alarm_next = p->alarm_next; + else alarm_head = p->alarm_next; + if (p->alarm_next) p->alarm_next->alarm_prev = p->alarm_prev; + p->alarm_prev = NULL; + p->alarm_next = NULL; + p->in_alarm_queue = 0; +} + static struct process* rq_pick_next(void) { if (rq_active->bitmap) { uint32_t prio = bsf32(rq_active->bitmap); @@ -288,6 +319,7 @@ int process_kill(uint32_t pid, int sig) { rq_remove_if_queued(p); } sleep_queue_remove(p); + alarm_queue_remove(p); process_close_all_files_locked(p); p->exit_status = 128 + sig; p->state = PROCESS_ZOMBIE; @@ -422,6 +454,7 @@ void process_exit_notify(int status) { current_process->exit_status = status; current_process->state = PROCESS_ZOMBIE; + alarm_queue_remove(current_process); if (current_process->pid != 0) { struct process* parent = process_find_locked(current_process->parent_pid); @@ -951,15 +984,31 @@ void process_wake_check(uint32_t current_tick) { } } - /* O(N) alarm scan — alarms are rare so this is acceptable */ - struct process* start = iter; - do { - if (iter->alarm_tick != 0 && current_tick >= iter->alarm_tick) { - iter->alarm_tick = 0; - iter->sig_pending_mask |= (1U << 14); /* SIGALRM */ - } - iter = iter->next; - } while (iter != start); + /* O(1) alarm queue: pop expired entries from the sorted head */ + while (alarm_head && current_tick >= alarm_head->alarm_tick) { + struct process* p = alarm_head; + alarm_queue_remove(p); + p->alarm_tick = 0; + p->sig_pending_mask |= (1U << 14); /* SIGALRM */ + } + + spin_unlock_irqrestore(&sched_lock, flags); +} + +uint32_t process_alarm_set(struct process* p, uint32_t tick) { + uintptr_t flags = spin_lock_irqsave(&sched_lock); + uint32_t old = p->alarm_tick; + + /* Remove from alarm queue if currently queued */ + alarm_queue_remove(p); + + if (tick != 0) { + p->alarm_tick = tick; + alarm_queue_insert(p); + } else { + p->alarm_tick = 0; + } spin_unlock_irqrestore(&sched_lock, flags); + return old; } diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index efca96b..58c2221 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -2322,14 +2322,11 @@ void syscall_handler(struct registers* regs) { if (!current_process) { sc_ret(regs) = 0; return; } uint32_t seconds = sc_arg0(regs); uint32_t now = get_tick_count(); + uint32_t new_tick = (seconds == 0) ? 0 : now + seconds * 50; + uint32_t old_tick = process_alarm_set(current_process, new_tick); uint32_t old_remaining = 0; - if (current_process->alarm_tick > now) { - old_remaining = (current_process->alarm_tick - now) / 50 + 1; - } - if (seconds == 0) { - current_process->alarm_tick = 0; - } else { - current_process->alarm_tick = now + seconds * 50; + if (old_tick > now) { + old_remaining = (old_tick - now) / 50 + 1; } sc_ret(regs) = old_remaining; return;