From c3983353b359416be6630d8f4e467b866febaebe Mon Sep 17 00:00:00 2001 From: Matheus Date: Wed, 22 May 2024 13:49:11 -0300 Subject: [PATCH] signal: support the stopping of processes --- kernel-src/sys/signal.c | 97 +++++++++++++++++++++++++++++++++++------ 1 file changed, 84 insertions(+), 13 deletions(-) diff --git a/kernel-src/sys/signal.c b/kernel-src/sys/signal.c index 64fa908a..7d204f77 100644 --- a/kernel-src/sys/signal.c +++ b/kernel-src/sys/signal.c @@ -139,9 +139,12 @@ void signal_pending(struct thread_t *thread, sigset_t *sigset) { } 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); + // TODO kill/stop/continue entire process if default actions call for it etc + sigset_t *sigset = urgent ? &thread->signals.urgent : &thread->signals.pending; SIGNAL_SETON(sigset, signal); @@ -162,6 +165,10 @@ void signal_signalproc(struct proc_t *proc, int signal) { PROCESS_ENTER(proc); void *address = proc->signals.actions[signal].address; bool notignored = address != SIG_IGN && ((address == SIG_DFL && defaultactions[signal] != ACTION_IGN) || address != SIG_DFL); + bool notignorable = signal == SIGKILL || signal == SIGSTOP || signal == SIGCONT; + bool threadcontinued = false; + bool isdefaultaction = address == SIG_DFL; + bool shouldstop = (isdefaultaction && defaultactions[signal] == ACTION_STOP) || signal == SIGSTOP; for (int i = 0; i < proc->threadtablesize; ++i) { if (proc->threads[i] == NULL || (proc->threads[i]->flags & SCHED_THREAD_FLAGS_DEAD)) @@ -169,28 +176,69 @@ void signal_signalproc(struct proc_t *proc, int signal) { THREAD_ENTER(proc->threads[i]); - // sigcont will wake up all the threads or unset sigstop as pending - if (signal == SIGCONT) { - // TODO this - } else if (SIGNAL_GET(&proc->threads[i]->signals.mask, signal) == 0 || signal == SIGKILL || signal == SIGSTOP) { + if (SIGNAL_GET(&proc->threads[i]->signals.mask, signal) == 0 || notignorable) { SIGNAL_SETON(&proc->threads[i]->signals.pending, signal); - if (notignored) { - sched_wakeup(proc->threads[i], SCHED_WAKEUP_REASON_INTERRUPTED); + if (notignored || notignorable) { + int reason = SCHED_WAKEUP_REASON_INTERRUPTED; + if (proc->threads[i]->signals.stopped && (signal == SIGCONT || signal == SIGKILL)) { + // thread is stopped and we have to wake it up + proc->threads[i]->signals.stopped = false; + reason = 0; // wake it up from the non interruptible sleep + threadcontinued = true; + } else if (signal == SIGCONT) { + // thread is not stopped, ignore SIGCONT + SIGNAL_SETOFF(&proc->threads[i]->signals.pending, signal); + goto threadleave; + } + + sched_wakeup(proc->threads[i], reason); // TODO ipi if running isn't self } - THREAD_LEAVE(proc->threads[i]); - PROCESS_LEAVE(proc); - MUTEX_RELEASE(&proc->mutex); - return; + + // if we are stopping or continuing, this will be sent to all individual threads + // XXX possible race condition where a thread will change the process mask or the action of stopping signals + // after it was sent to everyone + if ((shouldstop || signal == SIGCONT) == false) { + THREAD_LEAVE(proc->threads[i]); + PROCESS_LEAVE(proc); + MUTEX_RELEASE(&proc->mutex); + return; + } } + threadleave: THREAD_LEAVE(proc->threads[i]); } + + // tell the parent that a child stopped + // TODO when stopping make sure that a thread was actually stopped + if ((notignorable || notignored) && defaultactions[signal] == ACTION_STOP) { + proc->status = 0x7f; + proc->status |= signal << 8; + proc->signals.stopunwaited = true; + if ((proc->parent->signals.actions[SIGCHLD].flags & SA_NOCLDSTOP) == 0) { + signal_signalproc(proc->parent, SIGCHLD); + } + semaphore_signal(&proc->parent->waitsem); + } + + // tell the parent that a child continued + if (threadcontinued) { + proc->status = 0xffff; + proc->signals.continueunwaited = true; + if ((proc->parent->signals.actions[SIGCHLD].flags & SA_NOCLDSTOP) == 0) { + signal_signalproc(proc->parent, SIGCHLD); + } + semaphore_signal(&proc->parent->waitsem); + } + MUTEX_RELEASE(&proc->mutex); - // there wasn't any thread with it unmasked, so set it as pending for the whole process - - SIGNAL_SETON(&proc->signals.pending, signal); + // if its ignorable and there wasn't any thread with it unmasked, 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); + } PROCESS_LEAVE(proc); } @@ -200,10 +248,12 @@ bool signal_check(struct thread_t *thread, context_t *context, bool syscall, uin bool retry = false; proc_t *proc = thread->proc; + bool intstatus = interrupt_set(false); PROCESS_ENTER(proc); THREAD_ENTER(thread); if (SIGNAL_GET(&proc->signals.pending, SIGKILL) || SIGNAL_GET(&thread->signals.pending, SIGKILL)) { + // TODO terminate entire program THREAD_LEAVE(thread); PROCESS_LEAVE(proc); proc->status = SIGKILL; @@ -262,12 +312,32 @@ bool signal_check(struct thread_t *thread, context_t *context, bool syscall, uin goto leave; case ACTION_CORE: case ACTION_TERM: + // TODO terminate entire program proc->status = signal; THREAD_LEAVE(thread); PROCESS_LEAVE(proc); interrupt_set(true); sched_threadexit(); case ACTION_STOP: + // TODO what if a thread unmasks a stop signal? + // check if sigcont is not pending for the thread (SIGCONT is always sent to the thread) + if (SIGNAL_GET(&thread->signals.pending, SIGCONT)) { + retry = true; + goto leave; + } + // if not, just sleep. the sigcont won't be sent until THREAD_LEAVE and preparesleep is protecting it from a lost wakeup + sched_preparesleep(false); + thread->signals.stopped = true; + THREAD_LEAVE(thread); + PROCESS_LEAVE(proc); + sched_yield(); + interrupt_set(intstatus); + // make sure a SIGCONT happened + __assert(SIGNAL_GET(&thread->signals.pending, SIGCONT)); + __assert(thread->signals.stopped == false); + // the stopped variable is already set by the signaling function + // see if theres any other signal to deal with (such as the SIGCONT handler) + return true; default: __assert(!"unsupported signal action"); } @@ -335,5 +405,6 @@ bool signal_check(struct thread_t *thread, context_t *context, bool syscall, uin leave: THREAD_LEAVE(thread); PROCESS_LEAVE(proc); + interrupt_set(intstatus); return retry; }