Store errno in struct rb_process_status

To propagate errno in the fiber thread scheduler hook.
Returns nil when no terminated process.
This commit is contained in:
Nobuyoshi Nakada 2020-12-24 16:44:38 +09:00
parent c752d29bbf
commit bdbbfd1fa5
Notes: git 2020-12-24 23:00:01 +09:00
2 changed files with 37 additions and 17 deletions

View File

@ -572,6 +572,7 @@ static VALUE rb_cProcessStatus;
struct rb_process_status { struct rb_process_status {
rb_pid_t pid; rb_pid_t pid;
int status; int status;
int error;
}; };
static const rb_data_type_t rb_process_status_type = { static const rb_data_type_t rb_process_status_type = {
@ -619,13 +620,14 @@ proc_s_last_status(VALUE mod)
} }
VALUE VALUE
rb_process_status_new(rb_pid_t pid, int status) rb_process_status_new(rb_pid_t pid, int status, int error)
{ {
VALUE last_status = rb_process_status_allocate(rb_cProcessStatus); VALUE last_status = rb_process_status_allocate(rb_cProcessStatus);
struct rb_process_status *data = RTYPEDDATA_DATA(last_status); struct rb_process_status *data = RTYPEDDATA_DATA(last_status);
data->pid = pid; data->pid = pid;
data->status = status; data->status = status;
data->error = error;
rb_obj_freeze(last_status); rb_obj_freeze(last_status);
return last_status; return last_status;
@ -657,7 +659,7 @@ process_status_load(VALUE real_obj, VALUE load_obj)
void void
rb_last_status_set(int status, rb_pid_t pid) rb_last_status_set(int status, rb_pid_t pid)
{ {
GET_THREAD()->last_status = rb_process_status_new(pid, status); GET_THREAD()->last_status = rb_process_status_new(pid, status, 0);
} }
void void
@ -1164,6 +1166,7 @@ waitpid_state_init(struct waitpid_state *w, rb_pid_t pid, int options)
w->pid = pid; w->pid = pid;
w->options = options; w->options = options;
w->errnum = 0; w->errnum = 0;
w->status = 0;
} }
static const rb_hrtime_t * static const rb_hrtime_t *
@ -1349,20 +1352,17 @@ rb_process_status_wait(rb_pid_t pid, int flags)
waitpid_no_SIGCHLD(w); waitpid_no_SIGCHLD(w);
} }
VALUE status = Qnil; rb_pid_t ret = w->ret;
if (w->ret == -1) { int s = w->status, e = w->errnum;
errno = w->errnum;
}
else if (w->ret > 0 && ruby_nocldwait) {
errno = ECHILD;
}
else {
status = rb_process_status_new(w->ret, w->status);
}
COROUTINE_STACK_FREE(w); COROUTINE_STACK_FREE(w);
return status; if (ret == 0) return Qnil;
if (ret > 0 && ruby_nocldwait) {
ret = -1;
e = ECHILD;
}
return rb_process_status_new(ret, s, e);
} }
/* /*
@ -1432,14 +1432,19 @@ rb_pid_t
rb_waitpid(rb_pid_t pid, int *st, int flags) rb_waitpid(rb_pid_t pid, int *st, int flags)
{ {
VALUE status = rb_process_status_wait(pid, flags); VALUE status = rb_process_status_wait(pid, flags);
if (NIL_P(status)) return -1; if (NIL_P(status)) return 0;
struct rb_process_status *data = RTYPEDDATA_DATA(status); struct rb_process_status *data = RTYPEDDATA_DATA(status);
pid = data->pid; pid = data->pid;
if (st) *st = data->status; if (st) *st = data->status;
if (pid == -1) {
errno = data->error;
}
else {
GET_THREAD()->last_status = status; GET_THREAD()->last_status = status;
}
return pid; return pid;
} }

View File

@ -1466,7 +1466,22 @@ class TestProcess < Test::Unit::TestCase
end end
def test_status_fail def test_status_fail
assert_nil(Process::Status.wait($$)) ret = Process::Status.wait($$)
assert_instance_of(Process::Status, ret)
assert_equal(-1, ret.pid)
end
def test_status_wait
IO.popen([RUBY, "-e", "gets"], "w") do |io|
pid = io.pid
assert_nil(Process::Status.wait(pid, Process::WNOHANG))
io.puts
ret = Process::Status.wait(pid)
assert_instance_of(Process::Status, ret)
assert_equal(pid, ret.pid)
assert_predicate(ret, :exited?)
end
end end
def test_wait_without_arg def test_wait_without_arg