diff --git a/kernel-src/include/kernel/scheduler.h b/kernel-src/include/kernel/scheduler.h index a3bfca51..327306f2 100644 --- a/kernel-src/include/kernel/scheduler.h +++ b/kernel-src/include/kernel/scheduler.h @@ -59,8 +59,10 @@ typedef struct thread_t { stack_t stack; sigset_t mask; sigset_t pending; - sigset_t urgent; - sigset_t waiting; + sigset_t urgent; // always handled before returning to userspace + sigset_t waiting; // for signal_wait + sigset_t oldmask; // for signal_suspend + bool suspending; bool stopped; } signals; } thread_t; diff --git a/kernel-src/include/kernel/signal.h b/kernel-src/include/kernel/signal.h index e4fd041c..065bfb33 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); +void signal_suspend(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/signal.c b/kernel-src/sys/signal.c index 7935c6ce..65a1c991 100644 --- a/kernel-src/sys/signal.c +++ b/kernel-src/sys/signal.c @@ -139,6 +139,20 @@ void signal_pending(struct thread_t *thread, sigset_t *sigset) { PROCESS_LEAVE(proc); } +void signal_suspend(sigset_t *sigset) { + thread_t *thread = _cpu()->thread; + THREAD_ENTER(thread); + + memcpy(&thread->signals.oldmask, &thread->signals.mask, sizeof(sigset_t)); + memcpy(&thread->signals.mask, sigset, sizeof(sigset_t)); + thread->signals.suspending = true; + + THREAD_LEAVE(thread); + + sched_preparesleep(true); + sched_yield(); +} + int signal_wait(sigset_t *sigset, timespec_t *timeout, siginfo_t *siginfo, int *signum) { thread_t *thread = _cpu()->thread; PROCESS_ENTER(thread->proc); @@ -531,7 +545,16 @@ bool signal_check(struct thread_t *thread, context_t *context, bool syscall, uin memcpy(&sigframe.oldstack, &thread->signals.stack, sizeof(stack_t)); memset(&thread->signals.stack, 0, sizeof(stack_t)); } - memcpy(&sigframe.oldmask, &thread->signals.mask, sizeof(sigset_t)); + + if (thread->signals.suspending) { + // if we were waiting in signal_suspend, push the old mask into the return frame + // of the first signal and set it to not act this way the next check + memcpy(&sigframe.oldmask, &thread->signals.oldmask, sizeof(sigset_t)); + thread->signals.suspending = false; + } else { + // else, just push the current mask + memcpy(&sigframe.oldmask, &thread->signals.mask, sizeof(sigset_t)); + } memcpy(&sigframe.context, context, sizeof(context_t)); memcpy(&sigframe.extracontext, &thread->extracontext, sizeof(extracontext_t)); // TODO siginfo diff --git a/kernel-src/sys/syscalls/sigsuspend.c b/kernel-src/sys/syscalls/sigsuspend.c index 1ae5214f..61dee8dd 100644 --- a/kernel-src/sys/syscalls/sigsuspend.c +++ b/kernel-src/sys/syscalls/sigsuspend.c @@ -8,18 +8,12 @@ syscallret_t syscall_sigsuspend(context_t *, sigset_t *umask) { }; sigset_t mask; - sigset_t old; ret.errno = usercopy_fromuser(&mask, umask, sizeof(sigset_t)); if (ret.errno) return ret; - signal_changemask(_cpu()->thread, SIG_SETMASK, &mask, &old); - - sched_preparesleep(true); - __assert(sched_yield() == SCHED_WAKEUP_REASON_INTERRUPTED); - - signal_changemask(_cpu()->thread, SIG_SETMASK, &old, NULL); + signal_suspend(&mask); ret.errno = EINTR; return ret;