aboutsummaryrefslogtreecommitdiffstats
path: root/mjit.c
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-06-27 03:14:30 +0000
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-06-27 03:14:30 +0000
commit054a412d540e7ed2de63d68da753f585ea6616c3 (patch)
tree550ff5e80d4735d7a5c3f05605a180cfcba526b2 /mjit.c
parente3e22c551d24829e0f9ce84afd3653867582034b (diff)
downloadruby-054a412d540e7ed2de63d68da753f585ea6616c3.tar.gz
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
Diffstat (limited to 'mjit.c')
-rw-r--r--mjit.c48
1 files changed, 37 insertions, 11 deletions
diff --git a/mjit.c b/mjit.c
index 07e9328aff..10aa15fb7f 100644
--- a/mjit.c
+++ b/mjit.c
@@ -80,6 +80,7 @@
#include "constant.h"
#include "id_table.h"
#include "ruby_assert.h"
+#include "ruby/thread.h"
#include "ruby/util.h"
#include "ruby/version.h"
@@ -118,6 +119,10 @@ extern void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lo
extern int rb_thread_create_mjit_thread(void (*child_hook)(void), void (*worker_func)(void));
+/* process.c */
+pid_t ruby_waitpid_locked(rb_vm_t *, rb_pid_t, int *status, int options,
+ rb_nativethread_cond_t *cond);
+
#define RB_CONDATTR_CLOCK_MONOTONIC 1
#ifdef _WIN32
@@ -401,22 +406,40 @@ start_process(const char *path, char *const *argv)
static int
exec_process(const char *path, char *const argv[])
{
- int stat, exit_code;
+ int stat, exit_code = -2;
pid_t pid;
+ rb_vm_t *vm = RUBY_SIGCHLD ? GET_VM() : 0;
+ rb_nativethread_cond_t cond;
- pid = start_process(path, argv);
- if (pid <= 0)
- return -2;
+ if (vm) {
+ rb_native_cond_initialize(&cond);
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ }
- for (;;) {
- waitpid(pid, &stat, 0);
- if (WIFEXITED(stat)) {
- exit_code = WEXITSTATUS(stat);
- break;
- } else if (WIFSIGNALED(stat)) {
- exit_code = -1;
+ pid = start_process(path, argv);
+ for (;pid > 0;) {
+ pid_t r = vm ? ruby_waitpid_locked(vm, pid, &stat, 0, &cond)
+ : waitpid(pid, &stat, 0);
+ if (r == -1) {
+ if (errno == EINTR) continue;
+ fprintf(stderr, "[%d] waitpid(%d): %s\n",
+ getpid(), pid, strerror(errno));
break;
}
+ else if (r == pid) {
+ if (WIFEXITED(stat)) {
+ exit_code = WEXITSTATUS(stat);
+ break;
+ } else if (WIFSIGNALED(stat)) {
+ exit_code = -1;
+ break;
+ }
+ }
+ }
+
+ if (vm) {
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+ rb_native_cond_destroy(&cond);
}
return exit_code;
}
@@ -1491,12 +1514,15 @@ mjit_init(struct mjit_options *opts)
static void
stop_worker(void)
{
+ rb_execution_context_t *ec = GET_EC();
+
stop_worker_p = TRUE;
while (!worker_stopped) {
verbose(3, "Sending cancel signal to worker");
CRITICAL_SECTION_START(3, "in stop_worker");
rb_native_cond_broadcast(&mjit_worker_wakeup);
CRITICAL_SECTION_FINISH(3, "in stop_worker");
+ RUBY_VM_CHECK_INTS(ec);
}
}