mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Revert r63758 and related commits
The change is unstable on Windows. Please re-commit it when it correctly supports Windows. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63852 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
0f24cdec9e
commit
df4a126d65
16 changed files with 246 additions and 646 deletions
|
@ -766,7 +766,6 @@ 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>
|
||||
|
@ -1783,7 +1782,6 @@ 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)
|
||||
|
|
|
@ -246,13 +246,19 @@ 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 (rb_grantpt(masterfd) == -1) goto error;
|
||||
if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
|
||||
if (grantpt(masterfd) == -1) goto grantpt_error;
|
||||
rb_fd_fix_cloexec(masterfd);
|
||||
#else
|
||||
{
|
||||
|
@ -266,8 +272,10 @@ 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 (rb_grantpt(masterfd) == -1) goto error;
|
||||
if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
|
||||
if (grantpt(masterfd) == -1) goto grantpt_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;
|
||||
|
@ -285,6 +293,8 @@ 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);
|
||||
|
@ -336,17 +346,21 @@ 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;
|
||||
if(rb_grantpt(masterfd) == -1) goto error;
|
||||
s = signal(SIGCHLD, SIG_DFL);
|
||||
if(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);
|
||||
if(rb_grantpt(masterfd) == -1) goto error;
|
||||
s = signal(SIGCHLD, SIG_DFL);
|
||||
if(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;
|
||||
|
|
|
@ -2042,9 +2042,6 @@ 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 */
|
||||
|
|
51
mjit.c
51
mjit.c
|
@ -80,7 +80,6 @@
|
|||
#include "constant.h"
|
||||
#include "id_table.h"
|
||||
#include "ruby_assert.h"
|
||||
#include "ruby/thread.h"
|
||||
#include "ruby/util.h"
|
||||
#include "ruby/version.h"
|
||||
|
||||
|
@ -119,10 +118,6 @@ 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
|
||||
|
@ -268,7 +263,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%"PRI_PIDT_PREFIX"uu%lu%s", tmp_dir, prefix, getpid(), id, suffix);
|
||||
return snprintf(str, size, "%s/%sp%luu%lu%s", tmp_dir, prefix, (unsigned long) getpid(), id, suffix);
|
||||
}
|
||||
|
||||
/* Return an unique file name in /tmp with PREFIX and SUFFIX and
|
||||
|
@ -406,41 +401,22 @@ start_process(const char *path, char *const *argv)
|
|||
static int
|
||||
exec_process(const char *path, char *const argv[])
|
||||
{
|
||||
int stat, exit_code = -2;
|
||||
int stat, exit_code;
|
||||
pid_t pid;
|
||||
rb_vm_t *vm = (RUBY_SIGCHLD || SIGCHLD_LOSSY) ? GET_VM() : 0;
|
||||
rb_nativethread_cond_t cond;
|
||||
|
||||
if (vm) {
|
||||
rb_native_cond_initialize(&cond);
|
||||
rb_native_mutex_lock(&vm->waitpid_lock);
|
||||
}
|
||||
|
||||
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);
|
||||
if (pid <= 0)
|
||||
return -2;
|
||||
|
||||
for (;;) {
|
||||
waitpid(pid, &stat, 0);
|
||||
if (WIFEXITED(stat)) {
|
||||
exit_code = WEXITSTATUS(stat);
|
||||
break;
|
||||
} else if (WIFSIGNALED(stat)) {
|
||||
exit_code = -1;
|
||||
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;
|
||||
}
|
||||
|
@ -1515,15 +1491,12 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
287
process.c
287
process.c
|
@ -885,6 +885,12 @@ 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)
|
||||
{
|
||||
|
@ -897,263 +903,45 @@ 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 *
|
||||
waitpid_nogvl(void *x)
|
||||
rb_waitpid_blocking(void *data)
|
||||
{
|
||||
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;
|
||||
struct waitpid_arg *arg = data;
|
||||
rb_pid_t result = do_waitpid(arg->pid, arg->st, arg->flags);
|
||||
return (void *)(VALUE)result;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
waitpid_sleep(VALUE x)
|
||||
static rb_pid_t
|
||||
do_waitpid_nonblocking(rb_pid_t pid, int *st, int flags)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
rb_pid_t
|
||||
rb_waitpid(rb_pid_t pid, int *st, int flags)
|
||||
{
|
||||
struct waitpid_state w;
|
||||
rb_pid_t result;
|
||||
|
||||
waitpid_state_init(&w, pid, flags);
|
||||
w.ec = GET_EC();
|
||||
|
||||
if (RUBY_SIGCHLD || SIGCHLD_LOSSY) {
|
||||
waitpid_wait(&w);
|
||||
if (flags & WNOHANG) {
|
||||
result = do_waitpid(pid, st, flags);
|
||||
}
|
||||
else {
|
||||
waitpid_no_SIGCHLD(&w);
|
||||
while ((result = do_waitpid_nonblocking(pid, st, flags)) < 0 &&
|
||||
(errno == EINTR)) {
|
||||
RUBY_VM_CHECK_INTS(GET_EC());
|
||||
}
|
||||
}
|
||||
|
||||
if (st) *st = w.status;
|
||||
if (w.ret > 0) {
|
||||
rb_last_status_set(w.status, w.ret);
|
||||
if (result > 0) {
|
||||
rb_last_status_set(*st, result);
|
||||
}
|
||||
if (w.ret == -1) errno = w.errnum;
|
||||
return w.ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -3807,8 +3595,6 @@ 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");
|
||||
|
@ -4300,6 +4086,16 @@ 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)
|
||||
|
@ -4309,6 +4105,9 @@ rb_f_system(int argc, VALUE *argv)
|
|||
if (ret == (rb_pid_t)-1)
|
||||
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) {
|
||||
|
|
147
signal.c
147
signal.c
|
@ -62,11 +62,12 @@ ruby_atomic_compare_and_swap(rb_atomic_t *ptr, rb_atomic_t cmp,
|
|||
}
|
||||
#endif
|
||||
|
||||
#define FOREACH_SIGNAL(sig, offset) \
|
||||
for (sig = siglist + (offset); sig < siglist + numberof(siglist); ++sig)
|
||||
enum { LONGEST_SIGNAME = 7 }; /* MIGRATE and RETRACT */
|
||||
#ifndef NSIG
|
||||
# define NSIG (_SIGMAX + 1) /* For QNX */
|
||||
#endif
|
||||
|
||||
static const struct signals {
|
||||
char signm[LONGEST_SIGNAME + 1];
|
||||
const char *signm;
|
||||
int signo;
|
||||
} siglist [] = {
|
||||
{"EXIT", 0},
|
||||
|
@ -128,9 +129,15 @@ static const struct signals {
|
|||
#ifdef SIGCONT
|
||||
{"CONT", SIGCONT},
|
||||
#endif
|
||||
#if RUBY_SIGCHLD
|
||||
{"CHLD", RUBY_SIGCHLD },
|
||||
{"CLD", RUBY_SIGCHLD },
|
||||
#ifdef SIGCHLD
|
||||
{"CHLD", SIGCHLD},
|
||||
#endif
|
||||
#ifdef SIGCLD
|
||||
{"CLD", SIGCLD},
|
||||
#else
|
||||
# ifdef SIGCHLD
|
||||
{"CLD", SIGCHLD},
|
||||
# endif
|
||||
#endif
|
||||
#ifdef SIGTTIN
|
||||
{"TTIN", SIGTTIN},
|
||||
|
@ -195,6 +202,7 @@ static const struct signals {
|
|||
#ifdef SIGINFO
|
||||
{"INFO", SIGINFO},
|
||||
#endif
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
static const char signame_prefix[3] = "SIG";
|
||||
|
@ -206,7 +214,7 @@ signm2signo(VALUE *sig_ptr, int negative, int exit, int *prefix_ptr)
|
|||
const struct signals *sigs;
|
||||
VALUE vsig = *sig_ptr;
|
||||
const char *nm;
|
||||
long len, nmlen;
|
||||
long len;
|
||||
int prefix = 0;
|
||||
|
||||
if (RB_SYMBOL_P(vsig)) {
|
||||
|
@ -260,12 +268,9 @@ signm2signo(VALUE *sig_ptr, int negative, int exit, int *prefix_ptr)
|
|||
}
|
||||
|
||||
if (prefix_ptr) *prefix_ptr = prefix;
|
||||
nmlen = len - prefix;
|
||||
nm += prefix;
|
||||
if (nmlen > LONGEST_SIGNAME) goto unsupported;
|
||||
FOREACH_SIGNAL(sigs, !exit) {
|
||||
if (memcmp(sigs->signm, nm, nmlen) == 0 &&
|
||||
sigs->signm[nmlen] == '\0') {
|
||||
for (sigs = siglist + !exit; sigs->signm; sigs++) {
|
||||
if (memcmp(sigs->signm, nm + prefix, len - prefix) == 0 &&
|
||||
sigs->signm[len - prefix] == '\0') {
|
||||
return negative ? -sigs->signo : sigs->signo;
|
||||
}
|
||||
}
|
||||
|
@ -277,10 +282,9 @@ signo2signm(int no)
|
|||
{
|
||||
const struct signals *sigs;
|
||||
|
||||
FOREACH_SIGNAL(sigs, 0) {
|
||||
for (sigs = siglist; sigs->signm; sigs++)
|
||||
if (sigs->signo == no)
|
||||
return sigs->signm;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -698,29 +702,12 @@ 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;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
signal_enque(sig);
|
||||
rb_thread_wakeup_timer_thread();
|
||||
#if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL)
|
||||
ruby_signal(sig, sighandler);
|
||||
|
@ -755,7 +742,6 @@ 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
|
||||
}
|
||||
|
@ -1066,17 +1052,6 @@ 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)
|
||||
{
|
||||
|
@ -1142,9 +1117,6 @@ default_handler(int sig)
|
|||
#endif
|
||||
#ifdef SIGUSR2
|
||||
case SIGUSR2:
|
||||
#endif
|
||||
#if RUBY_SIGCHLD
|
||||
case RUBY_SIGCHLD:
|
||||
#endif
|
||||
func = sighandler;
|
||||
break;
|
||||
|
@ -1183,9 +1155,6 @@ trap_handler(VALUE *cmd, int sig)
|
|||
VALUE command;
|
||||
|
||||
if (NIL_P(*cmd)) {
|
||||
if (sig == RUBY_SIGCHLD) {
|
||||
goto sig_dfl;
|
||||
}
|
||||
func = SIG_IGN;
|
||||
}
|
||||
else {
|
||||
|
@ -1206,9 +1175,6 @@ 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;
|
||||
}
|
||||
|
@ -1216,9 +1182,6 @@ 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;
|
||||
}
|
||||
|
@ -1305,7 +1268,7 @@ trap(int sig, sighandler_t func, VALUE command)
|
|||
break;
|
||||
}
|
||||
|
||||
ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig]) = command;
|
||||
vm->trap_list.cmd[sig] = command;
|
||||
vm->trap_list.safe[sig] = rb_safe_level();
|
||||
|
||||
return oldcmd;
|
||||
|
@ -1421,7 +1384,7 @@ sig_list(void)
|
|||
VALUE h = rb_hash_new();
|
||||
const struct signals *sigs;
|
||||
|
||||
FOREACH_SIGNAL(sigs, 0) {
|
||||
for (sigs = siglist; sigs->signm; sigs++) {
|
||||
rb_hash_aset(h, rb_fstring_cstr(sigs->signm), INT2FIX(sigs->signo));
|
||||
}
|
||||
return h;
|
||||
|
@ -1450,18 +1413,20 @@ install_sighandler(int signum, sighandler_t handler)
|
|||
# define install_sighandler(signum, handler) \
|
||||
INSTALL_SIGHANDLER(install_sighandler(signum, handler), #signum, signum)
|
||||
|
||||
#if RUBY_SIGCHLD
|
||||
#if defined(SIGCLD) || defined(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;
|
||||
ruby_signal(sig, func);
|
||||
ACCESS_ONCE(VALUE, GET_VM()->trap_list.cmd[sig]) = 0;
|
||||
|
||||
if (oldfunc != SIG_DFL && oldfunc != SIG_IGN) {
|
||||
ruby_signal(sig, oldfunc);
|
||||
}
|
||||
else {
|
||||
GET_VM()->trap_list.cmd[sig] = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1577,55 +1542,11 @@ Init_signal(void)
|
|||
install_sighandler(SIGSYS, sig_do_nothing);
|
||||
#endif
|
||||
|
||||
#if RUBY_SIGCHLD
|
||||
init_sigchld(RUBY_SIGCHLD);
|
||||
#if defined(SIGCLD)
|
||||
init_sigchld(SIGCLD);
|
||||
#elif defined(SIGCHLD)
|
||||
init_sigchld(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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,37 +4,31 @@ 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)
|
||||
without_feature :mjit do
|
||||
$stderr.puts "Leaked process before wait2 specs! Waiting for it"
|
||||
end
|
||||
$stderr.puts "Leaked process before wait2 specs! Waiting for it"
|
||||
leaked = Process.waitall
|
||||
$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
|
||||
$stderr.puts "leaked before wait2 specs: #{leaked}"
|
||||
rescue Errno::ECHILD # No child processes
|
||||
rescue NotImplementedError
|
||||
end
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
|
|
|
@ -8,87 +8,85 @@ 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
|
||||
|
||||
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
|
||||
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)
|
||||
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 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
platform_is_not :windows do
|
||||
it "returns its childs pid" do
|
||||
pid = Process.spawn(ruby_cmd('exit'))
|
||||
Process.wait.should == pid
|
||||
end
|
||||
|
||||
Process.wait(pid, Process::WNOHANG).should be_nil
|
||||
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
|
||||
|
||||
# wait for the child to setup its TERM handler
|
||||
write.close
|
||||
read.read(1)
|
||||
read.close
|
||||
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
|
||||
|
||||
Process.kill("TERM", pid)
|
||||
Process.wait.should == pid
|
||||
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 "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)
|
||||
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'))
|
||||
|
||||
Process.wait(0).should == pid2
|
||||
Process.wait.should == pid1
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
end
|
||||
|
|
|
@ -8,41 +8,43 @@ describe "Process.waitall" do
|
|||
end
|
||||
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
|
||||
|
||||
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)
|
||||
}
|
||||
without_feature :mjit do # [Bug #14867]
|
||||
it "returns an empty array when there are no children" do
|
||||
Process.waitall.should == []
|
||||
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)
|
||||
}
|
||||
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
|
||||
|
||||
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
|
||||
end
|
||||
|
|
|
@ -707,7 +707,7 @@ class TestRubyOptimization < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_clear_unreachable_keyword_args
|
||||
assert_separately [], <<-END, timeout: 30
|
||||
assert_separately [], <<-END, timeout: 20
|
||||
script = <<-EOS
|
||||
if true
|
||||
else
|
||||
|
|
|
@ -1426,6 +1426,7 @@ 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")
|
||||
|
@ -1434,6 +1435,7 @@ 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")
|
||||
|
@ -1442,6 +1444,7 @@ 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
|
||||
|
@ -1456,9 +1459,7 @@ class TestProcess < Test::Unit::TestCase
|
|||
def test_wait_exception
|
||||
bug11340 = '[ruby-dev:49176] [Bug #11340]'
|
||||
t0 = t1 = nil
|
||||
sec = 3
|
||||
code = "puts;STDOUT.flush;Thread.start{gets;exit};sleep(#{sec})"
|
||||
IO.popen([RUBY, '-e', code], 'r+') do |f|
|
||||
IO.popen([RUBY, '-e', 'puts;STDOUT.flush;Thread.start{gets;exit};sleep(3)'], 'r+') do |f|
|
||||
pid = f.pid
|
||||
f.gets
|
||||
t0 = Time.now
|
||||
|
@ -1472,11 +1473,10 @@ 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
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: us-ascii -*-
|
||||
require 'test/unit'
|
||||
|
||||
require 'timeout'
|
||||
require 'tmpdir'
|
||||
require 'tempfile'
|
||||
require_relative '../lib/jit_support'
|
||||
|
@ -591,18 +590,14 @@ 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
|
||||
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
||||
end until Process.wait(pid, Process::WNOHANG) || now > stop
|
||||
end until Process.wait(pid, Process::WNOHANG)
|
||||
assert_match(/hello world/, ps)
|
||||
assert_operator now, :<, stop
|
||||
Process.kill :KILL, pid
|
||||
Timeout.timeout(5) { Process.wait(pid) }
|
||||
Process.wait(pid)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -621,18 +616,14 @@ 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
|
||||
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
||||
end until Process.wait(pid, Process::WNOHANG) || now > stop
|
||||
end until Process.wait(pid, Process::WNOHANG)
|
||||
assert_match(/hello world/, ps)
|
||||
assert_operator now, :<, stop
|
||||
Process.kill :KILL, pid
|
||||
Timeout.timeout(5) { Process.wait(pid) }
|
||||
Process.wait(pid)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
34
thread.c
34
thread.c
|
@ -413,10 +413,6 @@ 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
|
||||
|
@ -4135,9 +4131,6 @@ rb_gc_set_stack_end(VALUE **stack_end_p)
|
|||
#endif
|
||||
|
||||
|
||||
/* signal.c */
|
||||
void ruby_sigchld_handler(rb_vm_t *);
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
@ -4170,7 +4163,6 @@ 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
|
||||
|
@ -4255,9 +4247,6 @@ 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;
|
||||
|
@ -5010,7 +4999,6 @@ 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);
|
||||
|
@ -5314,25 +5302,3 @@ 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 */
|
||||
|
|
|
@ -1369,7 +1369,7 @@ setup_communication_pipe(void)
|
|||
* @pre the calling context is in the timer thread.
|
||||
*/
|
||||
static inline void
|
||||
timer_thread_sleep(rb_vm_t *vm)
|
||||
timer_thread_sleep(rb_global_vm_lock_t* gvl)
|
||||
{
|
||||
int result;
|
||||
int need_polling;
|
||||
|
@ -1382,15 +1382,7 @@ timer_thread_sleep(rb_vm_t *vm)
|
|||
|
||||
need_polling = !ubf_threads_empty();
|
||||
|
||||
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) {
|
||||
if (gvl->waiting > 0 || need_polling) {
|
||||
/* polling (TIME_QUANTUM_USEC usec) */
|
||||
result = poll(pollfds, 1, TIME_QUANTUM_USEC/1000);
|
||||
}
|
||||
|
@ -1429,7 +1421,7 @@ static rb_nativethread_lock_t timer_thread_lock;
|
|||
static rb_nativethread_cond_t timer_thread_cond;
|
||||
|
||||
static inline void
|
||||
timer_thread_sleep(rb_vm_t *unused)
|
||||
timer_thread_sleep(rb_global_vm_lock_t* unused)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
|
@ -1493,14 +1485,7 @@ native_set_another_thread_name(rb_nativethread_id_t thread_id, VALUE name)
|
|||
static void *
|
||||
thread_timer(void *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
|
||||
rb_global_vm_lock_t *gvl = (rb_global_vm_lock_t *)p;
|
||||
|
||||
if (TT_DEBUG) WRITE_CONST(2, "start timer thread\n");
|
||||
|
||||
|
@ -1522,7 +1507,7 @@ thread_timer(void *p)
|
|||
if (TT_DEBUG) WRITE_CONST(2, "tick\n");
|
||||
|
||||
/* wait */
|
||||
timer_thread_sleep(vm);
|
||||
timer_thread_sleep(gvl);
|
||||
}
|
||||
#if USE_SLEEPY_TIMER_THREAD
|
||||
CLOSE_INVALIDATE(normal[0]);
|
||||
|
@ -1594,7 +1579,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);
|
||||
err = pthread_create(&timer_thread.id, &attr, thread_timer, &vm->gvl);
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
if (err == EINVAL) {
|
||||
|
@ -1605,7 +1590,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);
|
||||
err = pthread_create(&timer_thread.id, NULL, thread_timer, &vm->gvl);
|
||||
}
|
||||
if (err != 0) {
|
||||
rb_warn("pthread_create failed for timer: %s, scheduling broken",
|
||||
|
@ -1786,22 +1771,4 @@ 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 */
|
||||
|
|
20
vm_core.h
20
vm_core.h
|
@ -92,21 +92,6 @@
|
|||
|
||||
#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__) || defined(__WIN32__) || defined(_WIN32)
|
||||
# define SIGCHLD_LOSSY (1)
|
||||
#else
|
||||
# define SIGCHLD_LOSSY (0)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDARG_PROTOTYPES
|
||||
#include <stdarg.h>
|
||||
#define va_init_list(a,b) va_start((a),(b))
|
||||
|
@ -568,9 +553,6 @@ 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;
|
||||
|
@ -1579,8 +1561,6 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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 "I"
|
||||
#define PRI_PIDT_PREFIX PRI_INT_PREFIX
|
||||
#define GETGROUPS_T int
|
||||
#define RETSIGTYPE void
|
||||
#define TYPEOF_TIMEVAL_TV_SEC long
|
||||
|
|
Loading…
Reference in a new issue