From 054a412d540e7ed2de63d68da753f585ea6616c3 Mon Sep 17 00:00:00 2001 From: normal Date: Wed, 27 Jun 2018 03:14:30 +0000 Subject: hijack SIGCHLD handler for internal use Use a global SIGCHLD handler to guard all callers of rb_waitpid. To work safely with multi-threaded programs, we introduce a VM-wide waitpid_lock to be acquired BEFORE fork/vfork spawns the process. This is to be combined with the new ruby_waitpid_locked function used by mjit.c in a non-Ruby thread. Ruby-level SIGCHLD handlers registered with Signal.trap(:CHLD) continues to work as before and there should be no regressions in any existing use cases. Splitting the wait queues for PID > 0 and groups (PID <= 0) ensures we favor PID > 0 callers. The disabling of SIGCHLD in rb_f_system is longer necessary, as we use deferred signal handling and no longer make ANY blocking waitpid syscalls in other threads which could "beat" the waitpid call made by rb_f_system. We prevent SIGCHLD from firing in normal Ruby Threads and only enable it in the timer-thread, to prevent spurious wakeups from in test/-ext-/gvl/test_last_thread.rb with MJIT enabled. I've tried to guard as much of the code for RUBY_SIGCHLD==0 using C "if" statements rather than CPP "#if" so to reduce the likelyhood of portability problems as the compiler will see more code. We also work to suppress false-positives from Process.wait(-1, Process::WNOHANG) to quiets warnings from spec/ruby/core/process/wait2_spec.rb with MJIT enabled. Lastly, we must implement rb_grantpt for ext/pty. We need a MJIT-compatible way of supporting grantpt(3) which may spawn the `pt_chown' binary and call waitpid(2) on it. [ruby-core:87605] [Ruby trunk Bug#14867] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63758 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/pty/pty.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'ext') diff --git a/ext/pty/pty.c b/ext/pty/pty.c index b3c7535321..1ac89061c1 100644 --- a/ext/pty/pty.c +++ b/ext/pty/pty.c @@ -246,19 +246,13 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, /* Unix98 PTY */ int masterfd = -1, slavefd = -1; char *slavedevice; - struct sigaction dfl, old; - - dfl.sa_handler = SIG_DFL; - dfl.sa_flags = 0; - sigemptyset(&dfl.sa_mask); #if defined(__sun) || (defined(__FreeBSD__) && __FreeBSD_version < 902000) /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */ /* FreeBSD 9.2 or later supports O_CLOEXEC * http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */ if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error; - if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error; - if (grantpt(masterfd) == -1) goto grantpt_error; + if (rb_grantpt(masterfd) == -1) goto error; rb_fd_fix_cloexec(masterfd); #else { @@ -272,10 +266,8 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, if ((masterfd = posix_openpt(flags)) == -1) goto error; } rb_fd_fix_cloexec(masterfd); - if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error; - if (grantpt(masterfd) == -1) goto grantpt_error; + if (rb_grantpt(masterfd) == -1) goto error; #endif - if (sigaction(SIGCHLD, &old, NULL) == -1) goto error; if (unlockpt(masterfd) == -1) goto error; if ((slavedevice = ptsname(masterfd)) == NULL) goto error; if (no_mesg(slavedevice, nomesg) == -1) goto error; @@ -293,8 +285,6 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, strlcpy(SlaveName, slavedevice, DEVICELEN); return 0; - grantpt_error: - sigaction(SIGCHLD, &old, NULL); error: if (slavefd != -1) close(slavefd); if (masterfd != -1) close(masterfd); @@ -346,21 +336,17 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg, extern char *ptsname(int); extern int unlockpt(int); - extern int grantpt(int); #if defined(__sun) /* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */ if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error; - s = signal(SIGCHLD, SIG_DFL); - if(grantpt(masterfd) == -1) goto error; + if(rb_grantpt(masterfd) == -1) goto error; rb_fd_fix_cloexec(masterfd); #else if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error; rb_update_max_fd(masterfd); - s = signal(SIGCHLD, SIG_DFL); - if(grantpt(masterfd) == -1) goto error; + if(rb_grantpt(masterfd) == -1) goto error; #endif - signal(SIGCHLD, s); if(unlockpt(masterfd) == -1) goto error; if((slavedevice = ptsname(masterfd)) == NULL) goto error; if (no_mesg(slavedevice, nomesg) == -1) goto error; -- cgit v1.2.3