aboutsummaryrefslogtreecommitdiffstats
path: root/signal.c
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2023-03-14 20:07:59 +1300
committerGitHub <noreply@github.com>2023-03-14 20:07:59 +1300
commitac65ce16e904695ba45888d3fba641d12caf733a (patch)
tree35b467c0c77a0eef7aea7a3ffda0dba4fd025408 /signal.c
parentb27793835b858b442a7c2c39b442ab688504d3fc (diff)
downloadruby-ac65ce16e904695ba45888d3fba641d12caf733a.tar.gz
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.
Diffstat (limited to 'signal.c')
-rw-r--r--signal.c95
1 files changed, 92 insertions, 3 deletions
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)
@@ -1107,6 +1162,9 @@ default_handler(int sig)
#ifdef SIGUSR2
case SIGUSR2:
#endif
+#if RUBY_SIGCHLD
+ case RUBY_SIGCHLD:
+#endif
func = sighandler;
break;
#ifdef SIGBUS
@@ -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);
+ }
}