diff --git a/repos/libports/lib/symbols/libc b/repos/libports/lib/symbols/libc index 0bde0b6ea0a..b563ee67175 100644 --- a/repos/libports/lib/symbols/libc +++ b/repos/libports/lib/symbols/libc @@ -729,6 +729,7 @@ sigsetjmp T sigsetmask T sigsuspend W sigvec T +sigwait T sl_add T sl_find T sl_free T diff --git a/repos/libports/src/lib/libc/dummies.cc b/repos/libports/src/lib/libc/dummies.cc index 1f55acbd586..f4a3214c22f 100644 --- a/repos/libports/src/lib/libc/dummies.cc +++ b/repos/libports/src/lib/libc/dummies.cc @@ -197,7 +197,6 @@ DUMMY(int, -1, thr_kill2, (pid_t pid, long id, int sig)); __SYS_DUMMY(int, -1, sigsuspend, (const sigset_t *)) __SYS_DUMMY(int, -1, sigtimedwait, (const sigset_t *, siginfo_t *, const struct timespec *)); __SYS_DUMMY(int, -1, sigwaitinfo, (const sigset_t *, siginfo_t *)); -__SYS_DUMMY(int, -1, sigwait, (const sigset_t *, int *)); __SYS_DUMMY(int, -1, thr_kill, (long id, int sig)); diff --git a/repos/libports/src/lib/libc/internal/init.h b/repos/libports/src/lib/libc/internal/init.h index 405472a347f..6b19230ac59 100644 --- a/repos/libports/src/lib/libc/internal/init.h +++ b/repos/libports/src/lib/libc/internal/init.h @@ -150,7 +150,7 @@ namespace Libc { /** * Signal handling */ - void init_signal(Signal &); + void init_signal(Signal &, Monitor &); /** * Atexit handling diff --git a/repos/libports/src/lib/libc/internal/signal.h b/repos/libports/src/lib/libc/internal/signal.h index 823c352f83d..9e04fec0dca 100644 --- a/repos/libports/src/lib/libc/internal/signal.h +++ b/repos/libports/src/lib/libc/internal/signal.h @@ -35,7 +35,9 @@ struct Libc::Signal : Noncopyable { public: - struct sigaction signal_action[NSIG + 1] { }; + struct sigaction signal_action [NSIG + 1] { }; + unsigned signal_count [NSIG + 1] { }; + bool signal_sigwait[NSIG + 1] { }; private: diff --git a/repos/libports/src/lib/libc/kernel.cc b/repos/libports/src/lib/libc/kernel.cc index 4b1e2360590..0fc1a4ab2a1 100644 --- a/repos/libports/src/lib/libc/kernel.cc +++ b/repos/libports/src/lib/libc/kernel.cc @@ -513,7 +513,7 @@ Libc::Kernel::Kernel(Genode::Env &env, Genode::Allocator &heap) init_select(*this); init_socket_fs(*this, *this); init_passwd(_passwd_config()); - init_signal(_signal); + init_signal(_signal, *this); init_kqueue(_heap, *this); _init_file_descriptors(); diff --git a/repos/libports/src/lib/libc/signal.cc b/repos/libports/src/lib/libc/signal.cc index 76e465d0ccb..a70a4204fa1 100644 --- a/repos/libports/src/lib/libc/signal.cc +++ b/repos/libports/src/lib/libc/signal.cc @@ -7,7 +7,7 @@ */ /* - * Copyright (C) 2006-2019 Genode Labs GmbH + * Copyright (C) 2006-2024 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. @@ -26,21 +26,30 @@ extern "C" { #include #include #include +#include using namespace Libc; static Libc::Signal *_signal_ptr; +static Monitor *_monitor_ptr; -void Libc::init_signal(Signal &signal) +void Libc::init_signal(Signal &signal, Monitor &monitor) { - _signal_ptr = &signal; + _signal_ptr = &signal; + _monitor_ptr = &monitor; } -void Libc::Signal::_execute_signal_handler(unsigned n) +void Libc::Signal::_execute_signal_handler(unsigned const n) { + signal_count[n] ++; + + if (signal_sigwait[n]) + /* the blocked thread is woken by sigwait() in monitor() */ + return; + if (signal_action[n].sa_flags & SA_SIGINFO) { signal_action[n].sa_sigaction(n, 0, 0); return; @@ -263,3 +272,70 @@ extern "C" int sigaltstack(stack_t const * const ss, stack_t * const old_ss) return 0; } + + +extern "C" int __libc_sigwait(const sigset_t *, int *) __attribute__((weak, alias("sigwait"))); + +extern "C" int sigwait(const sigset_t *set, int *sig) +{ + if (!set || !sig) + return EINVAL; + + if (!_signal_ptr || !_monitor_ptr) + return EINVAL; + + auto &signals = *_signal_ptr; + + auto for_each_signal_of_set = [&](sigset_t const &set, auto const &fn) { + for (unsigned word = 0; word < _SIG_WORDS; word++) { + if (!set.__bits[word]) + continue; + + unsigned signals = set.__bits[word]; + + /* invoke functor for all signals of set */ + while (signals) { + unsigned const pos = __builtin_ctz(signals); + unsigned const signal = word * 32 + pos + 1; + + signals &= ~(1u << pos); + + fn(signal); + } + } + }; + + unsigned signal_count_before[NSIG + 1] { }; + + /* mark signals to wait for */ + for_each_signal_of_set(*set, [&](auto const signal) { + signals.signal_sigwait[signal] = true; + signal_count_before[signal] = signals.signal_count[signal]; + }); + + /* block as long none of the monitored signals are pending */ + _monitor_ptr->monitor([&]() { + bool triggered = false; + + for_each_signal_of_set(*set, [&](auto const signal) { + if (triggered) + return; + + triggered = signals.signal_count[signal] != + signal_count_before[signal]; + + if (triggered) + *sig = signal; + }); + + return triggered ? Monitor::Function_result::COMPLETE + : Monitor::Function_result::INCOMPLETE; + }); + + /* de-mark signals we had wait for */ + for_each_signal_of_set(*set, [&](auto const signal) { + signals.signal_sigwait[signal] = false; + }); + + return 0; +} diff --git a/repos/libports/src/test/libc/main.cc b/repos/libports/src/test/libc/main.cc index ba5c9f3560e..1153e48ece2 100644 --- a/repos/libports/src/test/libc/main.cc +++ b/repos/libports/src/test/libc/main.cc @@ -39,6 +39,7 @@ extern "C" { } static void test_sigalt(); +static void test_sigwait(); int main(int argc, char **argv) { @@ -269,6 +270,7 @@ int main(int argc, char **argv) } while (0); test_sigalt(); + test_sigwait(); exit(error_count); } @@ -357,3 +359,41 @@ static void test_sigalt() printf("%s done\n", __func__); } + + +static void * test_sigwait_pthread(void *arg) +{ + sigset_t set { }; + int triggered_signal { }; + + sigemptyset(&set); + sigaddset (&set, SIGBUS); + + int result = sigwait(&set, &triggered_signal); + + if (result) + abort(); + + return arg; +} + + +static void test_sigwait() +{ + pthread_t t; + + if (pthread_create(&t, 0, test_sigwait_pthread, NULL)) { + printf("error: pthread_create() failed\n"); + exit(-1); + } + + /* wait for pthread to invoke sigwait */ + sleep(3); + + /* send signal to pthread */ + kill(getpid(), SIGBUS); + + pthread_join(t, NULL); + + printf("%s done\n", __func__); +}