diff --git a/process.c b/process.c index fb57df5623..62801e6835 100644 --- a/process.c +++ b/process.c @@ -916,6 +916,8 @@ do_waitpid(rb_pid_t pid, int *st, int flags) #endif } +#define WAITPID_LOCK_ONLY ((struct waitpid_state *)-1) + struct waitpid_state { struct list_node wnode; rb_execution_context_t *ec; @@ -3923,12 +3925,13 @@ retry_fork_async_signal_safe(int *status, int *ep, volatile int try_gc = 1; struct child_handler_disabler_state old; int err; + rb_vm_t *vm = w && WAITPID_USE_SIGCHLD ? GET_VM() : 0; while (1) { prefork(); disable_child_handler_before_fork(&old); - if (w && WAITPID_USE_SIGCHLD) { - rb_native_mutex_lock(&rb_ec_vm_ptr(w->ec)->waitpid_lock); + if (vm) { + rb_native_mutex_lock(&vm->waitpid_lock); } #ifdef HAVE_WORKING_VFORK if (!has_privilege()) @@ -3954,12 +3957,12 @@ retry_fork_async_signal_safe(int *status, int *ep, #endif } err = errno; - if (w && WAITPID_USE_SIGCHLD) { - if (pid > 0) { + if (vm) { + if (pid > 0 && w != WAITPID_LOCK_ONLY) { w->pid = pid; - list_add(&rb_ec_vm_ptr(w->ec)->waiting_pids, &w->wnode); + list_add(&vm->waiting_pids, &w->wnode); } - rb_native_mutex_unlock(&rb_ec_vm_ptr(w->ec)->waitpid_lock); + rb_native_mutex_unlock(&vm->waitpid_lock); } disable_child_handler_fork_parent(&old); if (0 < pid) /* fork succeed, parent process */ @@ -3995,7 +3998,8 @@ fork_check_err(int *status, int (*chfunc)(void*, char *, size_t), void *charg, error_occurred = recv_child_error(ep[0], &err, errmsg, errmsg_buflen); if (error_occurred) { if (status) { - VM_ASSERT(w == 0 && "only used by extensions"); + VM_ASSERT((w == 0 || w == WAITPID_LOCK_ONLY) && + "only used by extensions"); rb_protect(proc_syswait, (VALUE)pid, status); } else if (!w) { @@ -4340,7 +4344,7 @@ rb_spawn_process(struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen) rb_last_status_set((status & 0xff) << 8, 0); pid = 1; /* dummy */ # endif - if (eargp->waitpid_state) { + if (eargp->waitpid_state && eargp->waitpid_state != WAITPID_LOCK_ONLY) { eargp->waitpid_state->pid = pid; } rb_execarg_run_options(&sarg, NULL, errmsg, errmsg_buflen); @@ -4369,6 +4373,15 @@ static rb_pid_t rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen) { struct spawn_args args; + struct rb_execarg *eargp = rb_execarg_get(execarg_obj); + + /* + * Prevent a race with MJIT where the compiler process where + * can hold an FD of ours in between vfork + execve + */ + if (!eargp->waitpid_state && mjit_enabled) { + eargp->waitpid_state = WAITPID_LOCK_ONLY; + } args.execarg = execarg_obj; args.errmsg.ptr = errmsg; diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 4c212c13f2..816751ec81 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -343,11 +343,6 @@ class TestProcess < Test::Unit::TestCase end def test_execopt_env_path - # http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1455223 - # http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1450027 - # http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1469867 - skip 'this randomly fails with MJIT' if RubyVM::MJIT.enabled? - bug8004 = '[ruby-core:53103] [Bug #8004]' Dir.mktmpdir do |d| open("#{d}/tmp_script.cmd", "w") {|f| f.puts ": ;"; f.chmod(0755)}