]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
refactor: replace O(N) alarm scan with O(1) sorted alarm queue
authorTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 22:34:04 +0000 (19:34 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 22:34:04 +0000 (19:34 -0300)
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.

include/process.h
src/kernel/scheduler.c
src/kernel/syscall.c

index 2976ac99aa6ea2d6765ef9473b0c3c9b4c83d8b5..05919156365d38a8208b005256125deaea30dda5 100644 (file)
@@ -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
index a1f16c0f05b9fb3f32c286cba77bf917d8312085..8bbc8ce2d4f2fe1fc48ebf54df13547b09895b5c 100644 (file)
@@ -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;
 }
index efca96b0f1283d89c98d1acd5b91357f5a83b5d2..58c222137929d9e5f95f704486770f7bfdae29e2 100644 (file)
@@ -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;