From 909ef745974138caf65e890e190f5c6f1b185753 Mon Sep 17 00:00:00 2001 From: Matheus Date: Sun, 8 Sep 2024 15:41:54 -0300 Subject: [PATCH] signals: signal_wait --- kernel-src/include/kernel/scheduler.h | 5 +- kernel-src/include/kernel/signal.h | 1 + kernel-src/sys/scheduler.c | 1 + kernel-src/sys/signal.c | 158 +++++++++++++++++++++++++- 4 files changed, 161 insertions(+), 4 deletions(-) diff --git a/kernel-src/include/kernel/scheduler.h b/kernel-src/include/kernel/scheduler.h index 3ac4d6e2..a3bfca51 100644 --- a/kernel-src/include/kernel/scheduler.h +++ b/kernel-src/include/kernel/scheduler.h @@ -9,6 +9,7 @@ #include #include #include +#include #define SCHED_THREAD_FLAGS_QUEUED 1 #define SCHED_THREAD_FLAGS_RUNNING 2 @@ -54,10 +55,12 @@ typedef struct thread_t { context_t *usercopyctx; struct { spinlock_t lock; + eventheader_t waitpendingevent; stack_t stack; sigset_t mask; sigset_t pending; sigset_t urgent; + sigset_t waiting; bool stopped; } signals; } thread_t; @@ -108,6 +111,7 @@ typedef struct proc_t { // these are only applicable if the process is leader spinlock_t lock; // protects the linked list } pgrp; + struct { spinlock_t lock; sigaction_t actions[NSIG]; @@ -152,7 +156,6 @@ void sched_stopcurrentthread(); int sched_yield(); void sched_preparesleep(bool interruptible); bool sched_wakeup(thread_t *thread, int reason); -int sched_sleep(); proc_t *sched_newproc(); vnode_t *sched_getcwd(); vnode_t *sched_getroot(); diff --git a/kernel-src/include/kernel/signal.h b/kernel-src/include/kernel/signal.h index 126eeaa4..e4fd041c 100644 --- a/kernel-src/include/kernel/signal.h +++ b/kernel-src/include/kernel/signal.h @@ -158,6 +158,7 @@ void signal_changemask(struct thread_t *thread, int how, sigset_t *new, sigset_t void signal_signalproc(struct proc_t *proc, int signal); void signal_signalthread(struct thread_t *thread, int signal, bool urgent); void signal_pending(struct thread_t *, sigset_t *sigset); +int signal_wait(sigset_t *sigset, timespec_t *timeout, siginfo_t *siginfo, int *signum); bool signal_check(struct thread_t *thread, context_t *context, bool syscall, uint64_t syscallret, uint64_t syscallerrno); extern int signal_defaultactions[NSIG]; diff --git a/kernel-src/sys/scheduler.c b/kernel-src/sys/scheduler.c index 1d3e4353..d152a12a 100644 --- a/kernel-src/sys/scheduler.c +++ b/kernel-src/sys/scheduler.c @@ -166,6 +166,7 @@ thread_t *sched_newthread(void *ip, size_t kstacksize, int priority, proc_t *pro CTX_IP(&thread->context) = (ctxreg_t)ip; SPINLOCK_INIT(thread->sleeplock); SPINLOCK_INIT(thread->signals.lock); + EVENT_INITHEADER(&thread->signals.waitpendingevent); return thread; } diff --git a/kernel-src/sys/signal.c b/kernel-src/sys/signal.c index 7a692777..cffd0b6c 100644 --- a/kernel-src/sys/signal.c +++ b/kernel-src/sys/signal.c @@ -139,8 +139,127 @@ void signal_pending(struct thread_t *thread, sigset_t *sigset) { PROCESS_LEAVE(proc); } +int signal_wait(sigset_t *sigset, timespec_t *timeout, siginfo_t *siginfo, int *signum) { + thread_t *thread = _cpu()->thread; + PROCESS_ENTER(thread->proc); + THREAD_ENTER(thread); + // if timeoutus is 0, this wait is just a poll + // if timeout == NULL, this will sleep indefinitely + // otherwise, use the timeout specified in timeout + uintmax_t timeoutus = -1; + int pending = 0; + + if (timeout) + timeoutus = timeout->s * 1000000 + timeout->ns / 1000; + + for (int i = 1; i < NSIG; ++i) { + if (SIGNAL_GET(sigset, i) == 0) + continue; + + if (SIGNAL_GET(&thread->signals.pending, i)) { + SIGNAL_SETOFF(&thread->signals.pending, i); + pending = i; + break; + } + + if (SIGNAL_GET(&thread->proc->signals.pending, i)) { + SIGNAL_SETOFF(&thread->proc->signals.pending, i); + pending = i; + break; + } + } + + if (pending) { + // TODO change this to actually return the siginfo when its implemented + memset(siginfo, 0, sizeof(siginfo_t)); + *signum = pending; + + THREAD_LEAVE(thread); + PROCESS_LEAVE(thread->proc); + return 0; + } + + // signal is not pending and we are just polling + // return EAGAIN + if (pending == 0 && timeoutus == 0) { + THREAD_LEAVE(thread); + PROCESS_LEAVE(thread->proc); + return EAGAIN; + } + + memcpy(&thread->signals.waiting, sigset, sizeof(sigset_t)); + + eventlistener_t eventlistener; + EVENT_INITLISTENER(&eventlistener); + + EVENT_ATTACH(&eventlistener, &thread->signals.waitpendingevent); + + THREAD_LEAVE(thread); + PROCESS_LEAVE(thread->proc); + + int err = EVENT_WAIT(&eventlistener, timeout ? timeoutus : 0); + EVENT_DETACHALL(&eventlistener); + + // now one of the following has happened: + // - the timeout expired + // - the event has been signaled, which means there is a pending signal + // in the thread pending set which was in the waiting set + // - the thread needs to return to userspace to service a signal (err == EINTR) + + if (err == 0) { + PROCESS_ENTER(thread->proc); + THREAD_ENTER(thread); + // timeout or event happened + // we still check for it even if it could have been a timeout, + // as the lock wasn't held for a while which means that inbetween the + // time out and the lock being held, a signal could've happened + + for (int i = 1; i < NSIG; ++i) { + if (SIGNAL_GET(sigset, i) == 0) + continue; + + if (SIGNAL_GET(&thread->signals.pending, i)) { + SIGNAL_SETOFF(&thread->signals.pending, i); + pending = i; + break; + } + + if (SIGNAL_GET(&thread->proc->signals.pending, i)) { + SIGNAL_SETOFF(&thread->proc->signals.pending, i); + pending = i; + break; + } + } + + if (pending) { + // there is a signal pending, and it has been removed from the pending set + // TODO change this to actually return the siginfo when its implemented + memset(siginfo, 0, sizeof(siginfo_t)); + *signum = pending; + } else { + // timed out and thre are no pending signals, return EAGAIN + __assert(timeout); + err = EAGAIN; + } + + memset(&thread->signals.waiting, 0, sizeof(sigset_t)); + + THREAD_LEAVE(thread); + PROCESS_LEAVE(thread->proc); + } else { + PROCESS_ENTER(thread->proc); + THREAD_ENTER(thread); + + memset(&thread->signals.waiting, 0, sizeof(sigset_t)); + + THREAD_LEAVE(thread); + PROCESS_LEAVE(thread->proc); + } + + return err; +} + void signal_signalthread(struct thread_t *thread, int signal, bool urgent) { - //bool notignorable = signal == SIGKILL || signal == SIGSTOP || signal == SIGCONT; PROCESS_ENTER(thread->proc); THREAD_ENTER(thread); @@ -153,7 +272,14 @@ void signal_signalthread(struct thread_t *thread, int signal, bool urgent) { bool notignored = address != SIG_IGN && ((address == SIG_DFL && signal_defaultactions[signal] != ACTION_IGN) || address != SIG_DFL); if (signal == SIGKILL || signal == SIGSTOP || urgent || (SIGNAL_GET(&thread->signals.mask, signal) == 0 && notignored)) { + // signal cannot be ignored or its urgent or its not masked and not ignored sched_wakeup(thread, SCHED_WAKEUP_REASON_INTERRUPTED); + } else if (notignored == false) { + // ignored, remove from pending set + SIGNAL_SETOFF(sigset, signal); + } else if (notignored && SIGNAL_GET(&thread->signals.mask, signal) && SIGNAL_GET(&thread->signals.waiting, signal)) { + // not ignored, masked and the thread is waiting on this signal + EVENT_SIGNAL(&thread->signals.waitpendingevent); } THREAD_LEAVE(thread); @@ -170,9 +296,11 @@ void signal_signalproc(struct proc_t *proc, int signal) { bool isdefaultaction = address == SIG_DFL; bool shouldstop = (isdefaultaction && signal_defaultactions[signal] == ACTION_STOP) || signal == SIGSTOP; - // TODO is one thread has a stop signal masked, the masked wait breaks completely + // TODO if one thread has a stop signal masked, the masked wait breaks completely thread_t *thread = proc->threadlist; + spinlock_acquire(&proc->threadlistlock); + while (thread) { if (thread->flags & SCHED_THREAD_FLAGS_DEAD) { thread = thread->procnext; @@ -216,9 +344,33 @@ void signal_signalproc(struct proc_t *proc, int signal) { thread = thread->procnext; } + // no thread has it unmasked, see if any threads are waiting for the signal + if (notignorable == false && (threadcontinued || shouldstop) == false) { + thread_t *thread = proc->threadlist; + while (thread) { + THREAD_ENTER(thread); + + thread = thread->procnext; + if (SIGNAL_GET(&thread->signals.waiting, signal)) { + // thread is waiting for this signal, set it as pending in its pending set and + // signal that it should wake up + SIGNAL_SETON(&thread->signals.pending, signal); + EVENT_SIGNAL(&thread->signals.waitpendingevent); + + THREAD_LEAVE(thread); + spinlock_release(&proc->threadlistlock); + PROCESS_LEAVE(proc); + return; + } + + THREAD_LEAVE(thread); + thread = thread->procnext; + } + } + spinlock_release(&proc->threadlistlock); - // if its ignorable and there wasn't any thread with it unmasked, set it as pending for the whole process + // if its ignorable and there wasn't any thread with it unmasked nor waiting for it, set it as pending for the whole process // not ignorable signals will be sent to all threads individually if (notignorable == false && (threadcontinued || shouldstop) == false) { SIGNAL_SETON(&proc->signals.pending, signal);