* process.c (retry_fork): extracted from rb_fork_err.

(send_child_error): ditto.
  (recv_child_error): ditto.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35985 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
akr 2012-06-09 12:51:01 +00:00
parent 588d10180f
commit 1ad3f4b05d
2 changed files with 130 additions and 88 deletions

View File

@ -1,3 +1,9 @@
Sat Jun 9 21:50:04 2012 Tanaka Akira <akr@fsij.org>
* process.c (retry_fork): extracted from rb_fork_err.
(send_child_error): ditto.
(recv_child_error): ditto.
Sat Jun 9 17:21:48 2012 Nobuyoshi Nakada <nobu@ruby-lang.org> Sat Jun 9 17:21:48 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
* iseq.c (iseq_load): type is a symbol, and invalid as ID in common. * iseq.c (iseq_load): type is a symbol, and invalid as ID in common.

138
process.c
View File

@ -2754,29 +2754,19 @@ chfunc_protect(VALUE arg)
* *
* +chfunc+ must not raise any exceptions. * +chfunc+ must not raise any exceptions.
*/ */
rb_pid_t
rb_fork_err(int *status, int (*chfunc)(void*, char *, size_t), void *charg, VALUE fds, static rb_pid_t
char *errmsg, size_t errmsg_buflen) retry_fork(int *status, int *ep)
{ {
rb_pid_t pid; rb_pid_t pid;
int err, state = 0; int state = 0;
int ep[2];
VALUE io = Qnil;
#define prefork() ( \ #define prefork() ( \
rb_io_flush(rb_stdout), \ rb_io_flush(rb_stdout), \
rb_io_flush(rb_stderr) \ rb_io_flush(rb_stderr) \
) )
prefork();
if (chfunc) { prefork();
if (status) *status = 0;
if (pipe_nocrash(ep, fds)) return -1;
if (fcntl(ep[1], F_SETFD, FD_CLOEXEC)) {
preserving_errno((close(ep[0]), close(ep[1])));
return -1;
}
}
for (; before_fork(), (pid = fork()) < 0; prefork()) { for (; before_fork(), (pid = fork()) < 0; prefork()) {
after_fork(); after_fork();
switch (errno) { switch (errno) {
@ -2784,76 +2774,70 @@ rb_fork_err(int *status, int (*chfunc)(void*, char *, size_t), void *charg, VALU
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK: case EWOULDBLOCK:
#endif #endif
if (!status && !chfunc) { if (!status && !ep) {
rb_thread_sleep(1); rb_thread_sleep(1);
continue; continue;
} }
else { else {
/* rb_protect() is required not only for non-NULL status
* but also for non-NULL chfunc because
* ep[0] and ep[1] should be closed on exceptions.
* If status is NULL, the catched exception is re-raised
* by rb_jump_tag() below, after closing them. */
rb_protect((VALUE (*)())rb_thread_sleep, 1, &state); rb_protect((VALUE (*)())rb_thread_sleep, 1, &state);
if (status) *status = state; if (status) *status = state;
if (!state) continue; if (!state) continue;
} }
/* fall through */ /* fall through */
default: default:
if (chfunc) { if (ep) {
preserving_errno((close(ep[0]), close(ep[1]))); preserving_errno((close(ep[0]), close(ep[1])));
} }
if (state && !status) rb_jump_tag(state); if (state && !status) rb_jump_tag(state);
return -1; return -1;
} }
} }
if (!pid) { return pid;
forked_child = 1; }
if (chfunc) {
struct chfunc_protect_t arg; static void
arg.chfunc = chfunc; send_child_error(int fd, int state, char *errmsg, size_t errmsg_buflen)
arg.arg = charg; {
arg.errmsg = errmsg; VALUE io = Qnil;
arg.buflen = errmsg_buflen; int err;
close(ep[0]);
if (!(int)rb_protect(chfunc_protect, (VALUE)&arg, &state)) _exit(EXIT_SUCCESS); if (write(fd, &state, sizeof(state)) == sizeof(state) && state) {
if (write(ep[1], &state, sizeof(state)) == sizeof(state) && state) {
VALUE errinfo = rb_errinfo(); VALUE errinfo = rb_errinfo();
io = rb_io_fdopen(ep[1], O_WRONLY|O_BINARY, NULL); io = rb_io_fdopen(fd, O_WRONLY|O_BINARY, NULL);
rb_marshal_dump(errinfo, io); rb_marshal_dump(errinfo, io);
rb_io_flush(io); rb_io_flush(io);
} }
err = errno; err = errno;
if (write(ep[1], &err, sizeof(err)) < 0) err = errno; if (write(fd, &err, sizeof(err)) < 0) err = errno;
if (errmsg && 0 < errmsg_buflen) { if (errmsg && 0 < errmsg_buflen) {
errmsg[errmsg_buflen-1] = '\0'; errmsg[errmsg_buflen-1] = '\0';
errmsg_buflen = strlen(errmsg); errmsg_buflen = strlen(errmsg);
if (errmsg_buflen > 0 &&write(ep[1], errmsg, errmsg_buflen) < 0) if (errmsg_buflen > 0 && write(fd, errmsg, errmsg_buflen) < 0)
err = errno; err = errno;
} }
if (!NIL_P(io)) rb_io_close(io); if (!NIL_P(io)) rb_io_close(io);
#if EXIT_SUCCESS == 127
_exit(EXIT_FAILURE);
#else
_exit(127);
#endif
} }
}
after_fork(); static int
if (pid && chfunc) { recv_child_error(int fd, int *statep, VALUE *excp, int *errp, char *errmsg, size_t errmsg_buflen)
{
int err, state = 0;
VALUE io = Qnil;
ssize_t size; ssize_t size;
VALUE exc = Qnil; VALUE exc = Qnil;
close(ep[1]); if ((read(fd, &state, sizeof(state))) == sizeof(state) && state) {
if ((read(ep[0], &state, sizeof(state))) == sizeof(state) && state) { io = rb_io_fdopen(fd, O_RDONLY|O_BINARY, NULL);
io = rb_io_fdopen(ep[0], O_RDONLY|O_BINARY, NULL);
exc = rb_marshal_load(io); exc = rb_marshal_load(io);
rb_set_errinfo(exc); rb_set_errinfo(exc);
} }
if (!*statep && state) *statep = state;
*excp = exc;
#define READ_FROM_CHILD(ptr, len) \ #define READ_FROM_CHILD(ptr, len) \
(NIL_P(io) ? read(ep[0], (ptr), (len)) : rb_io_bufread(io, (ptr), (len))) (NIL_P(io) ? read(fd, (ptr), (len)) : rb_io_bufread(io, (ptr), (len)))
if ((size = READ_FROM_CHILD(&err, sizeof(err))) < 0) { if ((size = READ_FROM_CHILD(&err, sizeof(err))) < 0) {
err = errno; err = errno;
} }
*errp = err;
if (size == sizeof(err) && if (size == sizeof(err) &&
errmsg && 0 < errmsg_buflen) { errmsg && 0 < errmsg_buflen) {
ssize_t ret = READ_FROM_CHILD(errmsg, errmsg_buflen-1); ssize_t ret = READ_FROM_CHILD(errmsg, errmsg_buflen-1);
@ -2862,10 +2846,62 @@ rb_fork_err(int *status, int (*chfunc)(void*, char *, size_t), void *charg, VALU
} }
} }
if (NIL_P(io)) if (NIL_P(io))
close(ep[0]); close(fd);
else else
rb_io_close(io); rb_io_close(io);
if (state || size) { return size != 0;
}
rb_pid_t
rb_fork_err(int *status, int (*chfunc)(void*, char *, size_t), void *charg, VALUE fds,
char *errmsg, size_t errmsg_buflen)
{
rb_pid_t pid;
int err, state = 0;
int ep[2];
VALUE exc;
int error_occured;
if (status) *status = 0;
if (!chfunc) {
pid = retry_fork(status, NULL);
if (pid < 0)
return pid;
if (!pid)
forked_child = 1;
after_fork();
return pid;
}
else {
if (pipe_nocrash(ep, fds)) return -1;
if (fcntl(ep[1], F_SETFD, FD_CLOEXEC)) {
preserving_errno((close(ep[0]), close(ep[1])));
return -1;
}
pid = retry_fork(status, ep);
if (pid < 0)
return pid;
if (!pid) {
struct chfunc_protect_t arg;
forked_child = 1;
close(ep[0]);
arg.chfunc = chfunc;
arg.arg = charg;
arg.errmsg = errmsg;
arg.buflen = errmsg_buflen;
if (!(int)rb_protect(chfunc_protect, (VALUE)&arg, &state)) _exit(EXIT_SUCCESS);
send_child_error(ep[1], state, errmsg, errmsg_buflen);
#if EXIT_SUCCESS == 127
_exit(EXIT_FAILURE);
#else
_exit(127);
#endif
}
after_fork();
close(ep[1]);
error_occured = recv_child_error(ep[0], &state, &exc, &err, errmsg, errmsg_buflen);
if (state || error_occured) {
if (status) { if (status) {
rb_protect(proc_syswait, (VALUE)pid, status); rb_protect(proc_syswait, (VALUE)pid, status);
if (state) *status = state; if (state) *status = state;
@ -2877,9 +2913,9 @@ rb_fork_err(int *status, int (*chfunc)(void*, char *, size_t), void *charg, VALU
errno = err; errno = err;
return -1; return -1;
} }
}
return pid; return pid;
} }
}
struct chfunc_wrapper_t { struct chfunc_wrapper_t {
int (*chfunc)(void*); int (*chfunc)(void*);