summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-07-05 03:02:33 +0000
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-07-05 03:02:33 +0000
commit44fc3d08eb3cb52df5bdd91a0d9723718654b349 (patch)
tree0034ca54aa0fbb3d6f4c46f2a0405ea20e3d23b9
parentb3799b93c20c00865c1bd5885890291c0874f029 (diff)
downloadruby-44fc3d08eb3cb52df5bdd91a0d9723718654b349.tar.gz
unrevert r63852 but keep SIGCHLD path disabled for win32
Reading win32/win32.c waitpid implementation, maybe waitpid(-1, ...) on that platform will never conflict with mjit use of waitpid. In any case, I've added WAITPID_USE_SIGCHLD macro to vm_core.h so it can be easy for Linux/BSD users to test (hopefully!) win32-compatible code. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63855 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--configure.ac2
-rw-r--r--ext/pty/pty.c22
-rw-r--r--internal.h3
-rw-r--r--mjit.c51
-rw-r--r--process.c287
-rw-r--r--signal.c125
-rw-r--r--spec/ruby/core/process/wait2_spec.rb38
-rw-r--r--spec/ruby/core/process/wait_spec.rb126
-rw-r--r--spec/ruby/core/process/waitall_spec.rb66
-rw-r--r--test/ruby/test_optimization.rb2
-rw-r--r--test/ruby/test_process.rb12
-rw-r--r--test/ruby/test_rubyoptions.rb17
-rw-r--r--thread.c34
-rw-r--r--thread_pthread.c47
-rw-r--r--vm_core.h23
-rw-r--r--win32/Makefile.sub2
16 files changed, 627 insertions, 230 deletions
diff --git a/configure.ac b/configure.ac
index b433863d64..074a3fd394 100644
--- a/configure.ac
+++ b/configure.ac
@@ -766,6 +766,7 @@ AS_CASE(["$target_os"],
AS_IF([test $gcc_major -lt 4 -o \( $gcc_major -eq 4 -a $gcc_minor -lt 3 \)], [
ac_cv_func___builtin_setjmp=no
])
+ with_setjmp_type=sigsetjmp # to hijack SIGCHLD handler
AC_CACHE_CHECK(for broken crypt with 8bit chars, rb_cv_broken_crypt,
[AC_TRY_RUN([
#include <stdio.h>
@@ -1782,6 +1783,7 @@ AC_CHECK_FUNCS(getsid)
AC_CHECK_FUNCS(gettimeofday) # for making ac_cv_func_gettimeofday
AC_CHECK_FUNCS(getuidx)
AC_CHECK_FUNCS(gmtime_r)
+AC_CHECK_FUNCS(grantpt)
AC_CHECK_FUNCS(initgroups)
AC_CHECK_FUNCS(ioctl)
AC_CHECK_FUNCS(isfinite)
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;
diff --git a/internal.h b/internal.h
index 59e855c850..b555bbab06 100644
--- a/internal.h
+++ b/internal.h
@@ -2042,6 +2042,9 @@ VALUE rb_gcd_normal(VALUE self, VALUE other);
VALUE rb_gcd_gmp(VALUE x, VALUE y);
#endif
+/* signal.c (export) */
+int rb_grantpt(int fd);
+
/* string.c (export) */
#ifdef RUBY_ENCODING_H
/* internal use */
diff --git a/mjit.c b/mjit.c
index 07e9328aff..ade1e2e6a9 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 */
+rb_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
@@ -263,7 +268,7 @@ real_ms_time(void)
static int
sprint_uniq_filename(char *str, size_t size, unsigned long id, const char *prefix, const char *suffix)
{
- return snprintf(str, size, "%s/%sp%luu%lu%s", tmp_dir, prefix, (unsigned long) getpid(), id, suffix);
+ return snprintf(str, size, "%s/%sp%"PRI_PIDT_PREFIX"uu%lu%s", tmp_dir, prefix, getpid(), id, suffix);
}
/* Return an unique file name in /tmp with PREFIX and SUFFIX and
@@ -401,22 +406,41 @@ 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 = WAITPID_USE_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, "[%"PRI_PIDT_PREFIX"d] waitpid(%"PRI_PIDT_PREFIX"d): %s (SIGCHLD=%d,%u)\n",
+ getpid(), pid, strerror(errno),
+ RUBY_SIGCHLD, SIGCHLD_LOSSY);
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 +1515,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);
}
}
diff --git a/process.c b/process.c
index 12ea6eac86..12cceba934 100644
--- a/process.c
+++ b/process.c
@@ -885,12 +885,6 @@ pst_wcoredump(VALUE st)
#endif
}
-struct waitpid_arg {
- rb_pid_t pid;
- int flags;
- int *st;
-};
-
static rb_pid_t
do_waitpid(rb_pid_t pid, int *st, int flags)
{
@@ -903,45 +897,263 @@ do_waitpid(rb_pid_t pid, int *st, int flags)
#endif
}
+struct waitpid_state {
+ struct list_node wnode;
+ rb_execution_context_t *ec;
+ rb_nativethread_cond_t *cond;
+ rb_pid_t ret;
+ rb_pid_t pid;
+ int status;
+ int options;
+ int errnum;
+};
+
+void rb_native_mutex_lock(rb_nativethread_lock_t *);
+void rb_native_mutex_unlock(rb_nativethread_lock_t *);
+void rb_native_cond_signal(rb_nativethread_cond_t *);
+void rb_native_cond_wait(rb_nativethread_cond_t *, rb_nativethread_lock_t *);
+rb_nativethread_cond_t *rb_sleep_cond_get(const rb_execution_context_t *);
+void rb_sleep_cond_put(rb_nativethread_cond_t *);
+
+static void
+waitpid_notify(struct waitpid_state *w, rb_pid_t ret)
+{
+ w->ret = ret;
+ list_del_init(&w->wnode);
+ rb_native_cond_signal(w->cond);
+}
+
+#ifdef _WIN32 /* for spawnvp result from mjit.c */
+# define waitpid_sys(pid,status,options) \
+ (WaitForSingleObject((HANDLE)(pid), 0),\
+ GetExitCodeProcess((HANDLE)(pid), (LPDWORD)(status)))
+#else
+# define waitpid_sys(pid,status,options) do_waitpid((pid),(status),(options))
+#endif
+
+/* called by timer thread */
+static void
+waitpid_each(struct list_head *head)
+{
+ struct waitpid_state *w = 0, *next;
+
+ list_for_each_safe(head, w, next, wnode) {
+ rb_pid_t ret;
+
+ if (w->ec)
+ ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG);
+ else
+ ret = waitpid_sys(w->pid, &w->status, w->options | WNOHANG);
+
+ if (!ret) continue;
+ if (ret == -1) w->errnum = errno;
+
+ if (w->ec) { /* rb_waitpid */
+ rb_thread_t *th = rb_ec_thread_ptr(w->ec);
+
+ rb_native_mutex_lock(&th->interrupt_lock);
+ waitpid_notify(w, ret);
+ rb_native_mutex_unlock(&th->interrupt_lock);
+ }
+ else { /* ruby_waitpid_locked */
+ waitpid_notify(w, ret);
+ }
+ }
+}
+
+void
+ruby_waitpid_all(rb_vm_t *vm)
+{
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ waitpid_each(&vm->waiting_pids);
+ if (list_empty(&vm->waiting_pids)) {
+ waitpid_each(&vm->waiting_grps);
+ }
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+}
+
+static void
+waitpid_state_init(struct waitpid_state *w, rb_pid_t pid, int options)
+{
+ w->ret = 0;
+ w->pid = pid;
+ w->options = options;
+}
+
+/*
+ * must be called with vm->waitpid_lock held, this is not interruptible
+ */
+rb_pid_t
+ruby_waitpid_locked(rb_vm_t *vm, rb_pid_t pid, int *status, int options,
+ rb_nativethread_cond_t *cond)
+{
+ struct waitpid_state w;
+
+ assert(!ruby_thread_has_gvl_p() && "must not have GVL");
+
+ waitpid_state_init(&w, pid, options);
+ if (w.pid > 0 || list_empty(&vm->waiting_pids))
+ w.ret = do_waitpid(w.pid, &w.status, w.options | WNOHANG);
+ if (w.ret) {
+ if (w.ret == -1) w.errnum = errno;
+ }
+ else {
+ w.cond = cond;
+ w.ec = 0;
+ list_add(w.pid > 0 ? &vm->waiting_pids : &vm->waiting_grps, &w.wnode);
+ do {
+ rb_native_cond_wait(w.cond, &vm->waitpid_lock);
+ } while (!w.ret);
+ list_del(&w.wnode);
+ }
+ if (status) {
+ *status = w.status;
+ }
+ if (w.ret == -1) errno = w.errnum;
+ return w.ret;
+}
+
+static void
+waitpid_wake(void *x)
+{
+ struct waitpid_state *w = x;
+
+ /* th->interrupt_lock is already held by rb_threadptr_interrupt_common */
+ rb_native_cond_signal(w->cond);
+}
+
static void *
-rb_waitpid_blocking(void *data)
+waitpid_nogvl(void *x)
{
- struct waitpid_arg *arg = data;
- rb_pid_t result = do_waitpid(arg->pid, arg->st, arg->flags);
- return (void *)(VALUE)result;
+ struct waitpid_state *w = x;
+ rb_thread_t *th = rb_ec_thread_ptr(w->ec);
+
+ rb_native_mutex_lock(&th->interrupt_lock);
+ /*
+ * We must check again before waiting, timer-thread may change w->ret
+ * by the time we enter this. And we may also be interrupted.
+ */
+ if (!w->ret && !RUBY_VM_INTERRUPTED_ANY(w->ec)) {
+ if (SIGCHLD_LOSSY) {
+ rb_thread_wakeup_timer_thread();
+ }
+ rb_native_cond_wait(w->cond, &th->interrupt_lock);
+ }
+ rb_native_mutex_unlock(&th->interrupt_lock);
+
+ return 0;
}
-static rb_pid_t
-do_waitpid_nonblocking(rb_pid_t pid, int *st, int flags)
+static VALUE
+waitpid_sleep(VALUE x)
{
- void *result;
- struct waitpid_arg arg;
- arg.pid = pid;
- arg.st = st;
- arg.flags = flags;
- result = rb_thread_call_without_gvl(rb_waitpid_blocking, &arg,
- RUBY_UBF_PROCESS, 0);
- return (rb_pid_t)(VALUE)result;
+ struct waitpid_state *w = (struct waitpid_state *)x;
+
+ while (!w->ret) {
+ rb_thread_call_without_gvl(waitpid_nogvl, w, waitpid_wake, w);
+ }
+
+ return Qfalse;
+}
+
+static VALUE
+waitpid_cleanup(VALUE x)
+{
+ struct waitpid_state *w = (struct waitpid_state *)x;
+
+ if (w->ret == 0) {
+ rb_vm_t *vm = rb_ec_vm_ptr(w->ec);
+
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ list_del(&w->wnode);
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+ }
+ rb_sleep_cond_put(w->cond);
+
+ return Qfalse;
+}
+
+static void
+waitpid_wait(struct waitpid_state *w)
+{
+ rb_vm_t *vm = rb_ec_vm_ptr(w->ec);
+
+ /*
+ * Lock here to prevent do_waitpid from stealing work from the
+ * ruby_waitpid_locked done by mjit workers since mjit works
+ * outside of GVL
+ */
+ rb_native_mutex_lock(&vm->waitpid_lock);
+
+ if (w->pid > 0 || list_empty(&vm->waiting_pids))
+ w->ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG);
+ if (w->ret) {
+ w->cond = 0;
+ if (w->ret == -1) w->errnum = errno;
+ }
+ else if (w->options & WNOHANG) {
+ w->cond = 0;
+ }
+ else {
+ w->cond = rb_sleep_cond_get(w->ec);
+ /* order matters, favor specified PIDs rather than -1 or 0 */
+ list_add(w->pid > 0 ? &vm->waiting_pids : &vm->waiting_grps, &w->wnode);
+ }
+
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+
+ if (w->cond) {
+ rb_ensure(waitpid_sleep, (VALUE)w, waitpid_cleanup, (VALUE)w);
+ }
+}
+
+static void *
+waitpid_blocking_no_SIGCHLD(void *x)
+{
+ struct waitpid_state *w = x;
+
+ w->ret = do_waitpid(w->pid, &w->status, w->options);
+
+ return 0;
+}
+
+static void
+waitpid_no_SIGCHLD(struct waitpid_state *w)
+{
+ if (w->options & WNOHANG) {
+ w->ret = do_waitpid(w->pid, &w->status, w->options);
+ }
+ else {
+ do {
+ rb_thread_call_without_gvl(waitpid_blocking_no_SIGCHLD, w,
+ RUBY_UBF_PROCESS, 0);
+ } while (w->ret < 0 && errno == EINTR && (RUBY_VM_CHECK_INTS(w->ec),1));
+ }
+ if (w->ret == -1)
+ w->errnum = errno;
}
rb_pid_t
rb_waitpid(rb_pid_t pid, int *st, int flags)
{
- rb_pid_t result;
+ struct waitpid_state w;
- if (flags & WNOHANG) {
- result = do_waitpid(pid, st, flags);
+ waitpid_state_init(&w, pid, flags);
+ w.ec = GET_EC();
+
+ if (WAITPID_USE_SIGCHLD) {
+ waitpid_wait(&w);
}
else {
- while ((result = do_waitpid_nonblocking(pid, st, flags)) < 0 &&
- (errno == EINTR)) {
- RUBY_VM_CHECK_INTS(GET_EC());
- }
+ waitpid_no_SIGCHLD(&w);
}
- if (result > 0) {
- rb_last_status_set(*st, result);
+
+ if (st) *st = w.status;
+ if (w.ret > 0) {
+ rb_last_status_set(w.status, w.ret);
}
- return result;
+ if (w.ret == -1) errno = w.errnum;
+ return w.ret;
}
@@ -3595,6 +3807,8 @@ disable_child_handler_fork_child(struct child_handler_disabler_state *old, char
}
}
+ /* non-Ruby child process, ensure cmake can see SIGCHLD */
+ sigemptyset(&old->sigmask);
ret = sigprocmask(SIG_SETMASK, &old->sigmask, NULL); /* async-signal-safe */
if (ret != 0) {
ERRMSG("sigprocmask");
@@ -4086,16 +4300,6 @@ rb_f_system(int argc, VALUE *argv)
VALUE execarg_obj;
struct rb_execarg *eargp;
-#if defined(SIGCLD) && !defined(SIGCHLD)
-# define SIGCHLD SIGCLD
-#endif
-
-#ifdef SIGCHLD
- RETSIGTYPE (*chfunc)(int);
-
- rb_last_status_clear();
- chfunc = signal(SIGCHLD, SIG_DFL);
-#endif
execarg_obj = rb_execarg_new(argc, argv, TRUE, TRUE);
pid = rb_execarg_spawn(execarg_obj, NULL, 0);
#if defined(HAVE_WORKING_FORK) || defined(HAVE_SPAWNV)
@@ -4106,9 +4310,6 @@ rb_f_system(int argc, VALUE *argv)
rb_sys_fail("Another thread waited the process started by system().");
}
#endif
-#ifdef SIGCHLD
- signal(SIGCHLD, chfunc);
-#endif
TypedData_Get_Struct(execarg_obj, struct rb_execarg, &exec_arg_data_type, eargp);
if (pid < 0) {
if (eargp->exception) {
diff --git a/signal.c b/signal.c
index d2231fe5ca..e9f0708510 100644
--- a/signal.c
+++ b/signal.c
@@ -62,10 +62,6 @@ ruby_atomic_compare_and_swap(rb_atomic_t *ptr, rb_atomic_t cmp,
}
#endif
-#ifndef NSIG
-# define NSIG (_SIGMAX + 1) /* For QNX */
-#endif
-
#define FOREACH_SIGNAL(sig, offset) \
for (sig = siglist + (offset); sig < siglist + numberof(siglist); ++sig)
enum { LONGEST_SIGNAME = 7 }; /* MIGRATE and RETRACT */
@@ -132,15 +128,9 @@ static const struct signals {
#ifdef SIGCONT
{"CONT", SIGCONT},
#endif
-#ifdef SIGCHLD
- {"CHLD", SIGCHLD},
-#endif
-#ifdef SIGCLD
- {"CLD", SIGCLD},
-#else
-# ifdef SIGCHLD
- {"CLD", SIGCHLD},
-# endif
+#if RUBY_SIGCHLD
+ {"CHLD", RUBY_SIGCHLD },
+ {"CLD", RUBY_SIGCHLD },
#endif
#ifdef SIGTTIN
{"TTIN", SIGTTIN},
@@ -708,12 +698,29 @@ signal_enque(int sig)
ATOMIC_INC(signal_buff.size);
}
+static rb_atomic_t sigchld_hit;
+
+/* Prevent compiler from reordering access */
+#define ACCESS_ONCE(type,x) (*((volatile type *)&(x)))
+
static RETSIGTYPE
sighandler(int sig)
{
int old_errnum = errno;
- signal_enque(sig);
+ /* the VM always needs to handle SIGCHLD for rb_waitpid */
+ if (sig == RUBY_SIGCHLD) {
+ rb_vm_t *vm = GET_VM();
+ ATOMIC_EXCHANGE(sigchld_hit, 1);
+
+ /* avoid spurious wakeup in main thread iff nobody uses trap(:CHLD) */
+ if (vm && ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig])) {
+ signal_enque(sig);
+ }
+ }
+ else {
+ signal_enque(sig);
+ }
rb_thread_wakeup_timer_thread();
#if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL)
ruby_signal(sig, sighandler);
@@ -748,6 +755,7 @@ rb_enable_interrupt(void)
#ifdef HAVE_PTHREAD_SIGMASK
sigset_t mask;
sigemptyset(&mask);
+ sigaddset(&mask, RUBY_SIGCHLD); /* timer-thread handles this */
pthread_sigmask(SIG_SETMASK, &mask, NULL);
#endif
}
@@ -1058,6 +1066,17 @@ rb_trap_exit(void)
}
}
+void ruby_waitpid_all(rb_vm_t *); /* process.c */
+
+/* only runs in the timer-thread */
+void
+ruby_sigchld_handler(rb_vm_t *vm)
+{
+ if (SIGCHLD_LOSSY || ATOMIC_EXCHANGE(sigchld_hit, 0)) {
+ ruby_waitpid_all(vm);
+ }
+}
+
void
rb_signal_exec(rb_thread_t *th, int sig)
{
@@ -1124,6 +1143,9 @@ default_handler(int sig)
#ifdef SIGUSR2
case SIGUSR2:
#endif
+#if RUBY_SIGCHLD
+ case RUBY_SIGCHLD:
+#endif
func = sighandler;
break;
#ifdef SIGBUS
@@ -1161,6 +1183,9 @@ trap_handler(VALUE *cmd, int sig)
VALUE command;
if (NIL_P(*cmd)) {
+ if (sig == RUBY_SIGCHLD) {
+ goto sig_dfl;
+ }
func = SIG_IGN;
}
else {
@@ -1181,6 +1206,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;
}
@@ -1188,6 +1216,9 @@ trap_handler(VALUE *cmd, int sig)
case 7:
if (memcmp(cptr, "SIG_IGN", 7) == 0) {
sig_ign:
+ if (sig == RUBY_SIGCHLD) {
+ goto sig_dfl;
+ }
func = SIG_IGN;
*cmd = Qtrue;
}
@@ -1274,7 +1305,7 @@ trap(int sig, sighandler_t func, VALUE command)
break;
}
- vm->trap_list.cmd[sig] = command;
+ ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig]) = command;
vm->trap_list.safe[sig] = rb_safe_level();
return oldcmd;
@@ -1419,20 +1450,18 @@ install_sighandler(int signum, sighandler_t handler)
# define install_sighandler(signum, handler) \
INSTALL_SIGHANDLER(install_sighandler(signum, handler), #signum, signum)
-#if defined(SIGCLD) || defined(SIGCHLD)
+#if RUBY_SIGCHLD
static int
init_sigchld(int sig)
{
sighandler_t oldfunc;
+ sighandler_t func = sighandler;
oldfunc = ruby_signal(sig, SIG_DFL);
if (oldfunc == SIG_ERR) return -1;
- if (oldfunc != SIG_DFL && oldfunc != SIG_IGN) {
- ruby_signal(sig, oldfunc);
- }
- else {
- GET_VM()->trap_list.cmd[sig] = 0;
- }
+ ruby_signal(sig, func);
+ ACCESS_ONCE(VALUE, GET_VM()->trap_list.cmd[sig]) = 0;
+
return 0;
}
@@ -1548,11 +1577,55 @@ Init_signal(void)
install_sighandler(SIGSYS, sig_do_nothing);
#endif
-#if defined(SIGCLD)
- init_sigchld(SIGCLD);
-#elif defined(SIGCHLD)
- init_sigchld(SIGCHLD);
+#if RUBY_SIGCHLD
+ init_sigchld(RUBY_SIGCHLD);
#endif
rb_enable_interrupt();
}
+
+#if defined(HAVE_GRANTPT)
+extern int grantpt(int);
+#else
+static int
+fake_grantfd(int masterfd)
+{
+ errno = ENOSYS;
+ return -1;
+}
+#define grantpt(fd) fake_grantfd(fd)
+#endif
+
+int
+rb_grantpt(int 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);
+ }
+}
diff --git a/spec/ruby/core/process/wait2_spec.rb b/spec/ruby/core/process/wait2_spec.rb
index 3f5aa3c7e2..d0163f80af 100644
--- a/spec/ruby/core/process/wait2_spec.rb
+++ b/spec/ruby/core/process/wait2_spec.rb
@@ -4,31 +4,37 @@ describe "Process.wait2" do
before :all do
# HACK: this kludge is temporarily necessary because some
# misbehaving spec somewhere else does not clear processes
+ # Note: background processes are unavoidable with MJIT,
+ # but we shouldn't reap them from Ruby-space
begin
Process.wait(-1, Process::WNOHANG)
- $stderr.puts "Leaked process before wait2 specs! Waiting for it"
+ without_feature :mjit do
+ $stderr.puts "Leaked process before wait2 specs! Waiting for it"
+ end
leaked = Process.waitall
- $stderr.puts "leaked before wait2 specs: #{leaked}"
+ $stderr.puts "leaked before wait2 specs: #{leaked}" unless leaked.empty?
+ with_feature :mjit do
+ # Ruby-space should not see PIDs used by mjit
+ leaked.should be_empty
+ end
rescue Errno::ECHILD # No child processes
rescue NotImplementedError
end
end
- without_feature :mjit do # [Bug #14867]
- platform_is_not :windows do
- it "returns the pid and status of child process" do
- pidf = Process.fork { Process.exit! 99 }
- results = Process.wait2
- results.size.should == 2
- pidw, status = results
- pidf.should == pidw
- status.exitstatus.should == 99
- end
+ platform_is_not :windows do
+ it "returns the pid and status of child process" do
+ pidf = Process.fork { Process.exit! 99 }
+ results = Process.wait2
+ results.size.should == 2
+ pidw, status = results
+ pidf.should == pidw
+ status.exitstatus.should == 99
end
+ end
- it "raises a StandardError if no child processes exist" do
- lambda { Process.wait2 }.should raise_error(Errno::ECHILD)
- lambda { Process.wait2 }.should raise_error(StandardError)
- end
+ it "raises a StandardError if no child processes exist" do
+ lambda { Process.wait2 }.should raise_error(Errno::ECHILD)
+ lambda { Process.wait2 }.should raise_error(StandardError)
end
end
diff --git a/spec/ruby/core/process/wait_spec.rb b/spec/ruby/core/process/wait_spec.rb
index 447c62f42a..5130bb4391 100644
--- a/spec/ruby/core/process/wait_spec.rb
+++ b/spec/ruby/core/process/wait_spec.rb
@@ -8,85 +8,87 @@ describe "Process.wait" do
begin
leaked = Process.waitall
puts "leaked before wait specs: #{leaked}" unless leaked.empty?
+ with_feature :mjit do
+ # Ruby-space should not see PIDs used by mjit
+ leaked.should be_empty
+ end
rescue NotImplementedError
end
end
- without_feature :mjit do # [Bug #14867]
- it "raises an Errno::ECHILD if there are no child processes" do
- lambda { Process.wait }.should raise_error(Errno::ECHILD)
+ it "raises an Errno::ECHILD if there are no child processes" do
+ lambda { Process.wait }.should raise_error(Errno::ECHILD)
+ end
+
+ platform_is_not :windows do
+ it "returns its childs pid" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process.wait.should == pid
end
- platform_is_not :windows do
- it "returns its childs pid" do
- pid = Process.spawn(ruby_cmd('exit'))
- Process.wait.should == pid
- end
+ it "sets $? to a Process::Status" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process.wait
+ $?.should be_kind_of(Process::Status)
+ $?.pid.should == pid
+ end
- it "sets $? to a Process::Status" do
- pid = Process.spawn(ruby_cmd('exit'))
- Process.wait
- $?.should be_kind_of(Process::Status)
- $?.pid.should == pid
- end
+ it "waits for any child process if no pid is given" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process.wait.should == pid
+ lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
+ end
- it "waits for any child process if no pid is given" do
- pid = Process.spawn(ruby_cmd('exit'))
- Process.wait.should == pid
- lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
- end
+ it "waits for a specific child if a pid is given" do
+ pid1 = Process.spawn(ruby_cmd('exit'))
+ pid2 = Process.spawn(ruby_cmd('exit'))
+ Process.wait(pid2).should == pid2
+ Process.wait(pid1).should == pid1
+ lambda { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH)
+ lambda { Process.kill(0, pid2) }.should raise_error(Errno::ESRCH)
+ end
- it "waits for a specific child if a pid is given" do
- pid1 = Process.spawn(ruby_cmd('exit'))
- pid2 = Process.spawn(ruby_cmd('exit'))
- Process.wait(pid2).should == pid2
- Process.wait(pid1).should == pid1
- lambda { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH)
- lambda { Process.kill(0, pid2) }.should raise_error(Errno::ESRCH)
- end
+ it "coerces the pid to an Integer" do
+ pid1 = Process.spawn(ruby_cmd('exit'))
+ Process.wait(mock_int(pid1)).should == pid1
+ lambda { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH)
+ end
- it "coerces the pid to an Integer" do
- pid1 = Process.spawn(ruby_cmd('exit'))
- Process.wait(mock_int(pid1)).should == pid1
- lambda { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH)
- end
+ # This spec is probably system-dependent.
+ it "waits for a child whose process group ID is that of the calling process" do
+ pid1 = Process.spawn(ruby_cmd('exit'), pgroup: true)
+ pid2 = Process.spawn(ruby_cmd('exit'))
- # This spec is probably system-dependent.
- it "waits for a child whose process group ID is that of the calling process" do
- pid1 = Process.spawn(ruby_cmd('exit'), pgroup: true)
- pid2 = Process.spawn(ruby_cmd('exit'))
+ Process.wait(0).should == pid2
+ Process.wait.should == pid1
+ end
- Process.wait(0).should == pid2
- Process.wait.should == pid1
+ # This spec is probably system-dependent.
+ it "doesn't block if no child is available when WNOHANG is used" do
+ read, write = IO.pipe
+ pid = Process.fork do
+ read.close
+ Signal.trap("TERM") { Process.exit! }
+ write << 1
+ write.close
+ sleep
end
- # This spec is probably system-dependent.
- it "doesn't block if no child is available when WNOHANG is used" do
- read, write = IO.pipe
- pid = Process.fork do
- read.close
- Signal.trap("TERM") { Process.exit! }
- write << 1
- write.close
- sleep
- end
-
- Process.wait(pid, Process::WNOHANG).should be_nil
+ Process.wait(pid, Process::WNOHANG).should be_nil
- # wait for the child to setup its TERM handler
- write.close
- read.read(1)
- read.close
+ # wait for the child to setup its TERM handler
+ write.close
+ read.read(1)
+ read.close
- Process.kill("TERM", pid)
- Process.wait.should == pid
- end
+ Process.kill("TERM", pid)
+ Process.wait.should == pid
+ end
- it "always accepts flags=0" do
- pid = Process.spawn(ruby_cmd('exit'))
- Process.wait(-1, 0).should == pid
- lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
- end
+ it "always accepts flags=0" do
+ pid = Process.spawn(ruby_cmd('exit'))
+ Process.wait(-1, 0).should == pid
+ lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
end
end
end
diff --git a/spec/ruby/core/process/waitall_spec.rb b/spec/ruby/core/process/waitall_spec.rb
index ff06ae21f9..bdc1ea7490 100644
--- a/spec/ruby/core/process/waitall_spec.rb
+++ b/spec/ruby/core/process/waitall_spec.rb
@@ -8,43 +8,41 @@ describe "Process.waitall" do
end
end
- without_feature :mjit do # [Bug #14867]
- it "returns an empty array when there are no children" do
- Process.waitall.should == []
- end
+ it "returns an empty array when there are no children" do
+ Process.waitall.should == []
+ end
- it "takes no arguments" do
- lambda { Process.waitall(0) }.should raise_error(ArgumentError)
- end
+ it "takes no arguments" do
+ lambda { Process.waitall(0) }.should raise_error(ArgumentError)
+ end
- platform_is_not :windows do
- it "waits for all children" do
- pids = []
- pids << Process.fork { Process.exit! 2 }
- pids << Process.fork { Process.exit! 1 }
- pids << Process.fork { Process.exit! 0 }
- Process.waitall
- pids.each { |pid|
- lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
- }
- end
+ platform_is_not :windows do
+ it "waits for all children" do
+ pids = []
+ pids << Process.fork { Process.exit! 2 }
+ pids << Process.fork { Process.exit! 1 }
+ pids << Process.fork { Process.exit! 0 }
+ Process.waitall
+ pids.each { |pid|
+ lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
+ }
+ end
- it "returns an array of pid/status pairs" do
- pids = []
- pids << Process.fork { Process.exit! 2 }
- pids << Process.fork { Process.exit! 1 }
- pids << Process.fork { Process.exit! 0 }
- a = Process.waitall
- a.should be_kind_of(Array)
- a.size.should == 3
- pids.each { |pid|
- pid_status = a.assoc(pid)
- pid_status.should be_kind_of(Array)
- pid_status.size.should == 2
- pid_status.first.should == pid
- pid_status.last.should be_kind_of(Process::Status)
- }
- end
+ it "returns an array of pid/status pairs" do
+ pids = []
+ pids << Process.fork { Process.exit! 2 }
+ pids << Process.fork { Process.exit! 1 }
+ pids << Process.fork { Process.exit! 0 }
+ a = Process.waitall
+ a.should be_kind_of(Array)
+ a.size.should == 3
+ pids.each { |pid|
+ pid_status = a.assoc(pid)
+ pid_status.should be_kind_of(Array)
+ pid_status.size.should == 2
+ pid_status.first.should == pid
+ pid_status.last.should be_kind_of(Process::Status)
+ }
end
end
end
diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb
index 6bfaf11d7f..32bdcef13d 100644
--- a/test/ruby/test_optimization.rb
+++ b/test/ruby/test_optimization.rb
@@ -707,7 +707,7 @@ class TestRubyOptimization < Test::Unit::TestCase
end
def test_clear_unreachable_keyword_args
- assert_separately [], <<-END, timeout: 20
+ assert_separately [], <<-END, timeout: 30
script = <<-EOS
if true
else
diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb
index 36ae94908a..6a32702b03 100644
--- a/test/ruby/test_process.rb
+++ b/test/ruby/test_process.rb
@@ -1426,7 +1426,6 @@ class TestProcess < Test::Unit::TestCase
end
def test_wait_without_arg
- skip "[Bug #14867]" if RubyVM::MJIT.enabled?
with_tmpchdir do
write_file("foo", "sleep 0.1")
pid = spawn(RUBY, "foo")
@@ -1435,7 +1434,6 @@ class TestProcess < Test::Unit::TestCase
end
def test_wait2
- skip "[Bug #14867]" if RubyVM::MJIT.enabled?
with_tmpchdir do
write_file("foo", "sleep 0.1")
pid = spawn(RUBY, "foo")
@@ -1444,7 +1442,6 @@ class TestProcess < Test::Unit::TestCase
end
def test_waitall
- skip "[Bug #14867]" if RubyVM::MJIT.enabled?
with_tmpchdir do
write_file("foo", "sleep 0.1")
ps = (0...3).map { spawn(RUBY, "foo") }.sort
@@ -1459,7 +1456,9 @@ class TestProcess < Test::Unit::TestCase
def test_wait_exception
bug11340 = '[ruby-dev:49176] [Bug #11340]'
t0 = t1 = nil
- IO.popen([RUBY, '-e', 'puts;STDOUT.flush;Thread.start{gets;exit};sleep(3)'], 'r+') do |f|
+ sec = 3
+ code = "puts;STDOUT.flush;Thread.start{gets;exit};sleep(#{sec})"
+ IO.popen([RUBY, '-e', code], 'r+') do |f|
pid = f.pid
f.gets
t0 = Time.now
@@ -1473,10 +1472,11 @@ class TestProcess < Test::Unit::TestCase
th.kill.join
end
t1 = Time.now
+ diff = t1 - t0
+ assert_operator(diff, :<, sec,
+ ->{"#{bug11340}: #{diff} seconds to interrupt Process.wait"})
f.puts
end
- assert_operator(t1 - t0, :<, 3,
- ->{"#{bug11340}: #{t1-t0} seconds to interrupt Process.wait"})
end
def test_abort
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index d67cdcb984..edd44ca0ae 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -1,6 +1,7 @@
# -*- coding: us-ascii -*-
require 'test/unit'
+require 'timeout'
require 'tmpdir'
require 'tempfile'
require_relative '../lib/jit_support'
@@ -590,14 +591,18 @@ class TestRubyOptions < Test::Unit::TestCase
pid = spawn(EnvUtil.rubybin, "test-script")
ps = nil
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ stop = now + 30
begin
sleep 0.1
ps = `#{PSCMD.join(' ')} #{pid}`
break if /hello world/ =~ ps
- end until Process.wait(pid, Process::WNOHANG)
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end until Process.wait(pid, Process::WNOHANG) || now > stop
assert_match(/hello world/, ps)
+ assert_operator now, :<, stop
Process.kill :KILL, pid
- Process.wait(pid)
+ Timeout.timeout(5) { Process.wait(pid) }
end
end
@@ -616,14 +621,18 @@ class TestRubyOptions < Test::Unit::TestCase
pid = spawn(EnvUtil.rubybin, "test-script")
ps = nil
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ stop = now + 30
begin
sleep 0.1
ps = `#{PSCMD.join(' ')} #{pid}`
break if /hello world/ =~ ps
- end until Process.wait(pid, Process::WNOHANG)
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end until Process.wait(pid, Process::WNOHANG) || now > stop
assert_match(/hello world/, ps)
+ assert_operator now, :<, stop
Process.kill :KILL, pid
- Process.wait(pid)
+ Timeout.timeout(5) { Process.wait(pid) }
end
end
diff --git a/thread.c b/thread.c
index 5a5e32379d..c58d3e0b9d 100644
--- a/thread.c
+++ b/thread.c
@@ -413,6 +413,10 @@ rb_vm_gvl_destroy(rb_vm_t *vm)
gvl_release(vm);
gvl_destroy(vm);
rb_native_mutex_destroy(&vm->thread_destruct_lock);
+ if (0) {
+ /* may be held by running threads */
+ rb_native_mutex_destroy(&vm->waitpid_lock);
+ }
}
void
@@ -4131,6 +4135,9 @@ rb_gc_set_stack_end(VALUE **stack_end_p)
#endif
+/* signal.c */
+void ruby_sigchld_handler(rb_vm_t *);
+
/*
*
*/
@@ -4163,6 +4170,7 @@ timer_thread_function(void *arg)
rb_native_mutex_unlock(&vm->thread_destruct_lock);
/* check signal */
+ ruby_sigchld_handler(vm);
rb_threadptr_check_signal(vm->main_thread);
#if 0
@@ -4247,6 +4255,9 @@ rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const r
}
rb_vm_living_threads_init(vm);
rb_vm_living_threads_insert(vm, th);
+
+ /* may be held by MJIT threads in parent */
+ rb_native_mutex_initialize(&vm->waitpid_lock);
vm->fork_gen++;
vm->sleeper = 0;
@@ -4999,6 +5010,7 @@ Init_Thread(void)
gvl_init(th->vm);
gvl_acquire(th->vm, th);
rb_native_mutex_initialize(&th->vm->thread_destruct_lock);
+ rb_native_mutex_initialize(&th->vm->waitpid_lock);
rb_native_mutex_initialize(&th->interrupt_lock);
th->pending_interrupt_queue = rb_ary_tmp_new(0);
@@ -5302,3 +5314,25 @@ rb_uninterruptible(VALUE (*b_proc)(ANYARGS), VALUE data)
return rb_ensure(b_proc, data, rb_ary_pop, cur_th->pending_interrupt_mask_stack);
}
+
+#ifndef USE_NATIVE_SLEEP_COND
+# define USE_NATIVE_SLEEP_COND (0)
+#endif
+
+#if !USE_NATIVE_SLEEP_COND
+rb_nativethread_cond_t *
+rb_sleep_cond_get(const rb_execution_context_t *ec)
+{
+ rb_nativethread_cond_t *cond = ALLOC(rb_nativethread_cond_t);
+ rb_native_cond_initialize(cond);
+
+ return cond;
+}
+
+void
+rb_sleep_cond_put(rb_nativethread_cond_t *cond)
+{
+ rb_native_cond_destroy(cond);
+ xfree(cond);
+}
+#endif /* !USE_NATIVE_SLEEP_COND */
diff --git a/thread_pthread.c b/thread_pthread.c
index 8a4825548b..722ce44487 100644
--- a/thread_pthread.c
+++ b/thread_pthread.c
@@ -1369,7 +1369,7 @@ setup_communication_pipe(void)
* @pre the calling context is in the timer thread.
*/
static inline void
-timer_thread_sleep(rb_global_vm_lock_t* gvl)
+timer_thread_sleep(rb_vm_t *vm)
{
int result;
int need_polling;
@@ -1382,7 +1382,15 @@ timer_thread_sleep(rb_global_vm_lock_t* gvl)
need_polling = !ubf_threads_empty();
- if (gvl->waiting > 0 || need_polling) {
+ if (SIGCHLD_LOSSY && !need_polling) {
+ rb_native_mutex_lock(&vm->waitpid_lock);
+ if (!list_empty(&vm->waiting_pids) || !list_empty(&vm->waiting_grps)) {
+ need_polling = 1;
+ }
+ rb_native_mutex_unlock(&vm->waitpid_lock);
+ }
+
+ if (vm->gvl.waiting > 0 || need_polling) {
/* polling (TIME_QUANTUM_USEC usec) */
result = poll(pollfds, 1, TIME_QUANTUM_USEC/1000);
}
@@ -1421,7 +1429,7 @@ static rb_nativethread_lock_t timer_thread_lock;
static rb_nativethread_cond_t timer_thread_cond;
static inline void
-timer_thread_sleep(rb_global_vm_lock_t* unused)
+timer_thread_sleep(rb_vm_t *unused)
{
struct timespec ts;
ts.tv_sec = 0;
@@ -1485,7 +1493,14 @@ native_set_another_thread_name(rb_nativethread_id_t thread_id, VALUE name)
static void *
thread_timer(void *p)
{
- rb_global_vm_lock_t *gvl = (rb_global_vm_lock_t *)p;
+ rb_vm_t *vm = p;
+#ifdef HAVE_PTHREAD_SIGMASK /* mainly to enable SIGCHLD */
+ {
+ sigset_t mask;
+ sigemptyset(&mask);
+ pthread_sigmask(SIG_SETMASK, &mask, NULL);
+ }
+#endif
if (TT_DEBUG) WRITE_CONST(2, "start timer thread\n");
@@ -1507,7 +1522,7 @@ thread_timer(void *p)
if (TT_DEBUG) WRITE_CONST(2, "tick\n");
/* wait */
- timer_thread_sleep(gvl);
+ timer_thread_sleep(vm);
}
#if USE_SLEEPY_TIMER_THREAD
CLOSE_INVALIDATE(normal[0]);
@@ -1579,7 +1594,7 @@ rb_thread_create_timer_thread(void)
if (timer_thread.created) {
rb_bug("rb_thread_create_timer_thread: Timer thread was already created\n");
}
- err = pthread_create(&timer_thread.id, &attr, thread_timer, &vm->gvl);
+ err = pthread_create(&timer_thread.id, &attr, thread_timer, vm);
pthread_attr_destroy(&attr);
if (err == EINVAL) {
@@ -1590,7 +1605,7 @@ rb_thread_create_timer_thread(void)
* default stack size is enough for them:
*/
stack_size = 0;
- err = pthread_create(&timer_thread.id, NULL, thread_timer, &vm->gvl);
+ err = pthread_create(&timer_thread.id, NULL, thread_timer, vm);
}
if (err != 0) {
rb_warn("pthread_create failed for timer: %s, scheduling broken",
@@ -1771,4 +1786,22 @@ rb_thread_create_mjit_thread(void (*child_hook)(void), void (*worker_func)(void)
return ret;
}
+#define USE_NATIVE_SLEEP_COND (1)
+
+#if USE_NATIVE_SLEEP_COND
+rb_nativethread_cond_t *
+rb_sleep_cond_get(const rb_execution_context_t *ec)
+{
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
+
+ return &th->native_thread_data.sleep_cond;
+}
+
+void
+rb_sleep_cond_put(rb_nativethread_cond_t *cond)
+{
+ /* no-op */
+}
+#endif /* USE_NATIVE_SLEEP_COND */
+
#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
diff --git a/vm_core.h b/vm_core.h
index ee151195d5..10a697f8c5 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -92,6 +92,24 @@
#define RUBY_NSIG NSIG
+#if defined(SIGCLD)
+# define RUBY_SIGCHLD (SIGCLD)
+#elif defined(SIGCHLD)
+# define RUBY_SIGCHLD (SIGCHLD)
+#else
+# define RUBY_SIGCHLD (0)
+#endif
+
+/* platforms with broken or non-existent SIGCHLD work by polling */
+#if defined(__APPLE__)
+# define SIGCHLD_LOSSY (1)
+#else
+# define SIGCHLD_LOSSY (0)
+#endif
+
+/* define to 0 to test old code path */
+#define WAITPID_USE_SIGCHLD (RUBY_SIGCHLD || SIGCHLD_LOSSY)
+
#ifdef HAVE_STDARG_PROTOTYPES
#include <stdarg.h>
#define va_init_list(a,b) va_start((a),(b))
@@ -553,6 +571,9 @@ typedef struct rb_vm_struct {
#endif
rb_serial_t fork_gen;
+ rb_nativethread_lock_t waitpid_lock;
+ struct list_head waiting_pids; /* PID > 0: <=> struct waitpid_state */
+ struct list_head waiting_grps; /* PID <= 0: <=> struct waitpid_state */
struct list_head waiting_fds; /* <=> struct waiting_fd */
struct list_head living_threads;
VALUE thgroup_default;
@@ -1561,6 +1582,8 @@ static inline void
rb_vm_living_threads_init(rb_vm_t *vm)
{
list_head_init(&vm->waiting_fds);
+ list_head_init(&vm->waiting_pids);
+ list_head_init(&vm->waiting_grps);
list_head_init(&vm->living_threads);
vm->living_thread_num = 0;
}
diff --git a/win32/Makefile.sub b/win32/Makefile.sub
index 20b47a581a..fd852c21b7 100644
--- a/win32/Makefile.sub
+++ b/win32/Makefile.sub
@@ -760,7 +760,7 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define ssize_t int
!endif
#define PRI_LL_PREFIX "I64"
-#define PRI_PIDT_PREFIX PRI_INT_PREFIX
+#define PRI_PIDT_PREFIX "I"
#define GETGROUPS_T int
#define RETSIGTYPE void
#define TYPEOF_TIMEVAL_TV_SEC long