From ac65ce16e904695ba45888d3fba641d12caf733a Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 14 Mar 2023 20:07:59 +1300 Subject: Revert SIGCHLD changes to diagnose CI failures. (#7517) * Revert "Remove special handling of `SIGCHLD`. (#7482)" This reverts commit 44a0711eab7fbc71ac2c8ff489d8c53e97a8fe75. * Revert "Remove prototypes for functions that are no longer used. (#7497)" This reverts commit 4dce12bead3bfd91fd80b5e7195f7f540ffffacb. * Revert "Remove SIGCHLD `waidpid`. (#7476)" This reverts commit 1658e7d96696a656d9bd0a0c84c82cde86914ba2. * Fix change to rjit variable name. --- signal.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 3 deletions(-) (limited to 'signal.c') diff --git a/signal.c b/signal.c index 6cfb80f83e..544854e014 100644 --- a/signal.c +++ b/signal.c @@ -508,6 +508,9 @@ static struct { rb_atomic_t cnt[RUBY_NSIG]; rb_atomic_t size; } signal_buff; +#if RUBY_SIGCHLD +volatile unsigned int ruby_nocldwait; +#endif #define sighandler_t ruby_sighandler_t @@ -605,6 +608,27 @@ ruby_signal(int signum, sighandler_t handler) #endif switch (signum) { +#if RUBY_SIGCHLD + case RUBY_SIGCHLD: + if (handler == SIG_IGN) { + ruby_nocldwait = 1; +# ifdef USE_SIGALTSTACK + if (sigact.sa_flags & SA_SIGINFO) { + sigact.sa_sigaction = (ruby_sigaction_t*)sighandler; + } + else { + sigact.sa_handler = sighandler; + } +# else + sigact.sa_handler = handler; + sigact.sa_flags = 0; +# endif + } + else { + ruby_nocldwait = 0; + } + break; +#endif #if defined(SA_ONSTACK) && defined(USE_SIGALTSTACK) case SIGSEGV: #ifdef SIGBUS @@ -683,14 +707,35 @@ signal_enque(int sig) ATOMIC_INC(signal_buff.size); } +#if RUBY_SIGCHLD +static rb_atomic_t sigchld_hit; +/* destructive getter than simple predicate */ +# define GET_SIGCHLD_HIT() ATOMIC_EXCHANGE(sigchld_hit, 0) +#else +# define GET_SIGCHLD_HIT() 0 +#endif + static void sighandler(int sig) { int old_errnum = errno; - signal_enque(sig); - rb_thread_wakeup_timer_thread(sig); + /* the VM always needs to handle SIGCHLD for rb_waitpid */ + if (sig == RUBY_SIGCHLD) { +#if RUBY_SIGCHLD + rb_vm_t *vm = GET_VM(); + ATOMIC_EXCHANGE(sigchld_hit, 1); + /* avoid spurious wakeup in main thread if and only if nobody uses trap(:CHLD) */ + if (vm && ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig])) { + signal_enque(sig); + } +#endif + } + else { + signal_enque(sig); + } + rb_thread_wakeup_timer_thread(sig); #if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL) ruby_signal(sig, sighandler); #endif @@ -1040,6 +1085,16 @@ rb_vm_trap_exit(rb_vm_t *vm) } } +void ruby_waitpid_all(rb_vm_t *); /* process.c */ + +void +ruby_sigchld_handler(rb_vm_t *vm) +{ + if (SIGCHLD_LOSSY || GET_SIGCHLD_HIT()) { + ruby_waitpid_all(vm); + } +} + /* returns true if a trap handler was run, false otherwise */ int rb_signal_exec(rb_thread_t *th, int sig) @@ -1106,6 +1161,9 @@ default_handler(int sig) #endif #ifdef SIGUSR2 case SIGUSR2: +#endif +#if RUBY_SIGCHLD + case RUBY_SIGCHLD: #endif func = sighandler; break; @@ -1172,6 +1230,9 @@ trap_handler(VALUE *cmd, int sig) break; case 14: if (memcmp(cptr, "SYSTEM_DEFAULT", 14) == 0) { + if (sig == RUBY_SIGCHLD) { + goto sig_dfl; + } func = SIG_DFL; *cmd = 0; } @@ -1562,5 +1623,33 @@ fake_grantfd(int masterfd) int rb_grantpt(int masterfd) { - return grantpt(masterfd); + if (RUBY_SIGCHLD) { + rb_vm_t *vm = GET_VM(); + int ret, e; + + /* + * Prevent waitpid calls from Ruby by taking waitpid_lock. + * Pedantically, grantpt(3) is undefined if a non-default + * SIGCHLD handler is defined, but preventing conflicting + * waitpid calls ought to be sufficient. + * + * We could install the default sighandler temporarily, but that + * could cause SIGCHLD to be missed by other threads. Blocking + * SIGCHLD won't work here, either, unless we stop and restart + * timer-thread (as only timer-thread sees SIGCHLD), but that + * seems like overkill. + */ + rb_nativethread_lock_lock(&vm->waitpid_lock); + { + ret = grantpt(masterfd); /* may spawn `pt_chown' and wait on it */ + if (ret < 0) e = errno; + } + rb_nativethread_lock_unlock(&vm->waitpid_lock); + + if (ret < 0) errno = e; + return ret; + } + else { + return grantpt(masterfd); + } } -- cgit v1.2.1