]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: setitimer/getitimer syscalls with repeating interval timer support
authorTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 22:45:48 +0000 (19:45 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 22:45:48 +0000 (19:45 -0300)
Phase D2+D3 complete — implements POSIX interval timers.

Kernel changes:
- process.h: added alarm_interval (repeat ticks for ITIMER_REAL),
  itimer_virt_value/interval, itimer_prof_value/interval fields
- scheduler.c process_wake_check: alarm queue now auto-requeues
  repeating timers (alarm_interval != 0) on expiry; added per-tick
  ITIMER_VIRTUAL (user mode) and ITIMER_PROF (user+kernel) accounting
  that delivers SIGVTALRM/SIGPROF respectively
- syscall.c: implemented SYSCALL_SETITIMER (92) and SYSCALL_GETITIMER (93)
  supporting ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF; alarm() updated
  to clear alarm_interval (one-shot only) and use TICKS_PER_SEC constant
- syscall.h: added SYSCALL_SETITIMER=92, SYSCALL_GETITIMER=93

Userland:
- signal.h: added SIGVTALRM=26, SIGPROF=27
- sys/time.h: new header with struct timeval, struct itimerval,
  ITIMER_REAL/VIRTUAL/PROF defines, setitimer/getitimer prototypes
- syscall.h: added SYS_SETITIMER=92, SYS_GETITIMER=93
- unistd.c: added setitimer() and getitimer() wrappers

Timer resolution: 20ms (50Hz tick). Conversions use
USEC_PER_TICK=20000 for timeval<->ticks.

20/20 smoke tests pass.

include/process.h
include/syscall.h
src/kernel/scheduler.c
src/kernel/syscall.c
user/ulibc/include/signal.h
user/ulibc/include/sys/time.h [new file with mode: 0644]
user/ulibc/include/syscall.h
user/ulibc/src/unistd.c

index 05919156365d38a8208b005256125deaea30dda5..8d1b9e8af8e00d2d2bca85feb01688e0a3eed199 100644 (file)
@@ -60,8 +60,15 @@ struct process {
     process_state_t state;
     uint32_t wake_at_tick;
     uint32_t alarm_tick;
+    uint32_t alarm_interval;    /* repeat interval in ticks (0 = one-shot) */
     uint32_t utime;             /* ticks spent in user mode */
     uint32_t stime;             /* ticks spent in kernel mode */
+
+    /* POSIX interval timers (values in ticks, 0 = disabled) */
+    uint32_t itimer_virt_value;     /* ITIMER_VIRTUAL: remaining user ticks */
+    uint32_t itimer_virt_interval;  /* ITIMER_VIRTUAL: reload value */
+    uint32_t itimer_prof_value;     /* ITIMER_PROF: remaining user+sys ticks */
+    uint32_t itimer_prof_interval;  /* ITIMER_PROF: reload value */
     int exit_status;
 
     int has_user_regs;
index decd7e72320634b53e7e9e4dfe2852d1ddcde1cc..c2b4b0cbda2d3712b1c9729068f132c7335a5861 100644 (file)
@@ -115,6 +115,8 @@ enum {
     SYSCALL_GETEGID   = 89,
     SYSCALL_SETEUID   = 90,
     SYSCALL_SETEGID   = 91,
+    SYSCALL_SETITIMER  = 92,
+    SYSCALL_GETITIMER  = 93,
 };
 
 #endif
index 8bbc8ce2d4f2fe1fc48ebf54df13547b09895b5c..334079286c16f8bba5d8fb1137f13e55718ddeb6 100644 (file)
@@ -988,8 +988,33 @@ void process_wake_check(uint32_t current_tick) {
     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 */
+        /* Re-arm repeating ITIMER_REAL */
+        if (p->alarm_interval != 0) {
+            p->alarm_tick = current_tick + p->alarm_interval;
+            alarm_queue_insert(p);
+        } else {
+            p->alarm_tick = 0;
+        }
+    }
+
+    /* ITIMER_VIRTUAL: decrement when running in user mode */
+    if (current_process && current_process->state == PROCESS_RUNNING) {
+        if (current_process->itimer_virt_value > 0) {
+            current_process->itimer_virt_value--;
+            if (current_process->itimer_virt_value == 0) {
+                current_process->sig_pending_mask |= (1U << 26); /* SIGVTALRM */
+                current_process->itimer_virt_value = current_process->itimer_virt_interval;
+            }
+        }
+        /* ITIMER_PROF: decrement when running (user + kernel) */
+        if (current_process->itimer_prof_value > 0) {
+            current_process->itimer_prof_value--;
+            if (current_process->itimer_prof_value == 0) {
+                current_process->sig_pending_mask |= (1U << 27); /* SIGPROF */
+                current_process->itimer_prof_value = current_process->itimer_prof_interval;
+            }
+        }
     }
 
     spin_unlock_irqrestore(&sched_lock, flags);
index 58c222137929d9e5f95f704486770f7bfdae29e2..c7c4a32cc18afb5fe081af8d1433d41ee030946c 100644 (file)
@@ -36,6 +36,29 @@ enum {
     O_CLOEXEC  = 0x80000,
 };
 
+/* Kernel-side itimerval for setitimer/getitimer (matches userland layout) */
+struct k_timeval {
+    uint32_t tv_sec;
+    uint32_t tv_usec;
+};
+struct k_itimerval {
+    struct k_timeval it_interval;
+    struct k_timeval it_value;
+};
+#define ITIMER_REAL    0
+#define ITIMER_VIRTUAL 1
+#define ITIMER_PROF    2
+#define TICKS_PER_SEC  50
+#define USEC_PER_TICK  (1000000U / TICKS_PER_SEC)  /* 20000 */
+
+static uint32_t timeval_to_ticks(const struct k_timeval* tv) {
+    return tv->tv_sec * TICKS_PER_SEC + tv->tv_usec / USEC_PER_TICK;
+}
+static void ticks_to_timeval(uint32_t ticks, struct k_timeval* tv) {
+    tv->tv_sec  = ticks / TICKS_PER_SEC;
+    tv->tv_usec = (ticks % TICKS_PER_SEC) * USEC_PER_TICK;
+}
+
 enum {
     FD_CLOEXEC = 1,
 };
@@ -2322,16 +2345,112 @@ 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 new_tick = (seconds == 0) ? 0 : now + seconds * TICKS_PER_SEC;
+        current_process->alarm_interval = 0; /* alarm() is always one-shot */
         uint32_t old_tick = process_alarm_set(current_process, new_tick);
         uint32_t old_remaining = 0;
         if (old_tick > now) {
-            old_remaining = (old_tick - now) / 50 + 1;
+            old_remaining = (old_tick - now) / TICKS_PER_SEC + 1;
         }
         sc_ret(regs) = old_remaining;
         return;
     }
 
+    if (syscall_no == SYSCALL_SETITIMER) {
+        if (!current_process) { sc_ret(regs) = (uint32_t)-EINVAL; return; }
+        int which = (int)sc_arg0(regs);
+        const void* user_new = (const void*)sc_arg1(regs);
+        void* user_old = (void*)sc_arg2(regs);
+
+        struct k_itimerval knew;
+        memset(&knew, 0, sizeof(knew));
+        if (user_new) {
+            if (copy_from_user(&knew, user_new, sizeof(knew)) < 0) {
+                sc_ret(regs) = (uint32_t)-EFAULT; return;
+            }
+        }
+
+        uint32_t now = get_tick_count();
+
+        if (which == ITIMER_REAL) {
+            /* Return old value */
+            if (user_old) {
+                struct k_itimerval kold;
+                memset(&kold, 0, sizeof(kold));
+                ticks_to_timeval(current_process->alarm_interval, &kold.it_interval);
+                if (current_process->alarm_tick > now)
+                    ticks_to_timeval(current_process->alarm_tick - now, &kold.it_value);
+                if (copy_to_user(user_old, &kold, sizeof(kold)) < 0) {
+                    sc_ret(regs) = (uint32_t)-EFAULT; return;
+                }
+            }
+            /* Set new value */
+            uint32_t val_ticks = timeval_to_ticks(&knew.it_value);
+            uint32_t int_ticks = timeval_to_ticks(&knew.it_interval);
+            current_process->alarm_interval = int_ticks;
+            process_alarm_set(current_process, val_ticks ? now + val_ticks : 0);
+        } else if (which == ITIMER_VIRTUAL) {
+            if (user_old) {
+                struct k_itimerval kold;
+                memset(&kold, 0, sizeof(kold));
+                ticks_to_timeval(current_process->itimer_virt_interval, &kold.it_interval);
+                ticks_to_timeval(current_process->itimer_virt_value, &kold.it_value);
+                if (copy_to_user(user_old, &kold, sizeof(kold)) < 0) {
+                    sc_ret(regs) = (uint32_t)-EFAULT; return;
+                }
+            }
+            current_process->itimer_virt_value = timeval_to_ticks(&knew.it_value);
+            current_process->itimer_virt_interval = timeval_to_ticks(&knew.it_interval);
+        } else if (which == ITIMER_PROF) {
+            if (user_old) {
+                struct k_itimerval kold;
+                memset(&kold, 0, sizeof(kold));
+                ticks_to_timeval(current_process->itimer_prof_interval, &kold.it_interval);
+                ticks_to_timeval(current_process->itimer_prof_value, &kold.it_value);
+                if (copy_to_user(user_old, &kold, sizeof(kold)) < 0) {
+                    sc_ret(regs) = (uint32_t)-EFAULT; return;
+                }
+            }
+            current_process->itimer_prof_value = timeval_to_ticks(&knew.it_value);
+            current_process->itimer_prof_interval = timeval_to_ticks(&knew.it_interval);
+        } else {
+            sc_ret(regs) = (uint32_t)-EINVAL; return;
+        }
+        sc_ret(regs) = 0;
+        return;
+    }
+
+    if (syscall_no == SYSCALL_GETITIMER) {
+        if (!current_process) { sc_ret(regs) = (uint32_t)-EINVAL; return; }
+        int which = (int)sc_arg0(regs);
+        void* user_val = (void*)sc_arg1(regs);
+        if (!user_val) { sc_ret(regs) = (uint32_t)-EFAULT; return; }
+
+        struct k_itimerval kval;
+        memset(&kval, 0, sizeof(kval));
+        uint32_t now = get_tick_count();
+
+        if (which == ITIMER_REAL) {
+            ticks_to_timeval(current_process->alarm_interval, &kval.it_interval);
+            if (current_process->alarm_tick > now)
+                ticks_to_timeval(current_process->alarm_tick - now, &kval.it_value);
+        } else if (which == ITIMER_VIRTUAL) {
+            ticks_to_timeval(current_process->itimer_virt_interval, &kval.it_interval);
+            ticks_to_timeval(current_process->itimer_virt_value, &kval.it_value);
+        } else if (which == ITIMER_PROF) {
+            ticks_to_timeval(current_process->itimer_prof_interval, &kval.it_interval);
+            ticks_to_timeval(current_process->itimer_prof_value, &kval.it_value);
+        } else {
+            sc_ret(regs) = (uint32_t)-EINVAL; return;
+        }
+
+        if (copy_to_user(user_val, &kval, sizeof(kval)) < 0) {
+            sc_ret(regs) = (uint32_t)-EFAULT; return;
+        }
+        sc_ret(regs) = 0;
+        return;
+    }
+
     if (syscall_no == SYSCALL_SIGSUSPEND) {
         if (!current_process) { sc_ret(regs) = (uint32_t)-EINVAL; return; }
         uint32_t new_mask = 0;
index 68dc55387133db24abd3405d707accdef3901fdc..ca73c19924c92cee16bbfce4e40868b6371555ef 100644 (file)
@@ -24,6 +24,8 @@
 #define SIGTSTP  20
 #define SIGTTIN  21
 #define SIGTTOU  22
+#define SIGVTALRM 26
+#define SIGPROF  27
 
 #define SA_SIGINFO 0x00000004U
 
diff --git a/user/ulibc/include/sys/time.h b/user/ulibc/include/sys/time.h
new file mode 100644 (file)
index 0000000..bfcd90d
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef ULIBC_SYS_TIME_H
+#define ULIBC_SYS_TIME_H
+
+#include <stdint.h>
+
+struct timeval {
+    uint32_t tv_sec;
+    uint32_t tv_usec;
+};
+
+struct itimerval {
+    struct timeval it_interval;  /* timer interval (reload value) */
+    struct timeval it_value;     /* current value */
+};
+
+#define ITIMER_REAL    0
+#define ITIMER_VIRTUAL 1
+#define ITIMER_PROF    2
+
+int getitimer(int which, struct itimerval *curr_value);
+int setitimer(int which, const struct itimerval *new_value,
+              struct itimerval *old_value);
+
+#endif
index a0595889e12cff918b243c96cf3ab8c27240fae0..5a5e214caa3bee997e2eba071ef4e900bc82e590 100644 (file)
@@ -78,6 +78,8 @@ enum {
     SYS_GETEGID = 89,
     SYS_SETEUID = 90,
     SYS_SETEGID = 91,
+    SYS_SETITIMER = 92,
+    SYS_GETITIMER = 93,
 };
 
 /* Raw syscall wrappers — up to 5 args via INT 0x80 */
index b0a820d9c79920855c024fc4ddced23360bc2612..ae7ad37b06d670181b7be0e59aaf9cd77193a437 100644 (file)
@@ -150,6 +150,14 @@ unsigned int alarm(unsigned int seconds) {
     return (unsigned int)_syscall1(SYS_ALARM, (int)seconds);
 }
 
+int setitimer(int which, const void* new_value, void* old_value) {
+    return __syscall_ret(_syscall3(SYS_SETITIMER, which, (int)new_value, (int)old_value));
+}
+
+int getitimer(int which, void* curr_value) {
+    return __syscall_ret(_syscall2(SYS_GETITIMER, which, (int)curr_value));
+}
+
 int flock(int fd, int operation) {
     return __syscall_ret(_syscall2(SYS_FLOCK, fd, operation));
 }