Bug #4683 » 0001-io.c-copy_stream-execute-interrupts-and-retry.patch
| io.c | ||
|---|---|---|
|
#include "ruby/ruby.h"
|
||
|
#include "ruby/io.h"
|
||
|
#include "vm_core.h"
|
||
|
#include "dln.h"
|
||
|
#include <ctype.h>
|
||
|
#include <errno.h>
|
||
| ... | ... | |
|
VALUE th;
|
||
|
};
|
||
|
static void *
|
||
|
exec_interrupts(void *arg)
|
||
|
{
|
||
|
rb_thread_t *th = arg;
|
||
|
rb_threadptr_execute_interrupts(th);
|
||
|
return NULL;
|
||
|
}
|
||
|
/*
|
||
|
* returns TRUE if the preceding system call was interrupted
|
||
|
* so we can continue. If the thread was interrupted, we
|
||
|
* reacquire the GVL to execute interrupts before continuing.
|
||
|
*/
|
||
|
static int
|
||
|
nogvl_copy_stream_continue_p(struct copy_stream_struct *stp)
|
||
|
{
|
||
|
switch (errno) {
|
||
|
case EINTR:
|
||
|
#if defined(ERESTART)
|
||
|
case ERESTART:
|
||
|
#endif
|
||
|
if (rb_thread_interrupted(stp->th))
|
||
|
rb_thread_call_with_gvl(exec_interrupts, (void *)stp->th);
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
static int
|
||
|
maygvl_copy_stream_wait_read(struct copy_stream_struct *stp)
|
||
|
{
|
||
|
int ret;
|
||
|
rb_fd_zero(&stp->fds);
|
||
|
rb_fd_set(stp->src_fd, &stp->fds);
|
||
|
ret = rb_fd_select(rb_fd_max(&stp->fds), &stp->fds, NULL, NULL, NULL);
|
||
|
do {
|
||
|
rb_fd_zero(&stp->fds);
|
||
|
rb_fd_set(stp->src_fd, &stp->fds);
|
||
|
ret = rb_fd_select(rb_fd_max(&stp->fds), &stp->fds, NULL, NULL, NULL);
|
||
|
} while (ret == -1 && nogvl_copy_stream_continue_p(stp));
|
||
|
if (ret == -1) {
|
||
|
stp->syserr = "select";
|
||
|
stp->error_no = errno;
|
||
| ... | ... | |
|
nogvl_copy_stream_wait_write(struct copy_stream_struct *stp)
|
||
|
{
|
||
|
int ret;
|
||
|
rb_fd_zero(&stp->fds);
|
||
|
rb_fd_set(stp->dst_fd, &stp->fds);
|
||
|
ret = rb_fd_select(rb_fd_max(&stp->fds), NULL, &stp->fds, NULL, NULL);
|
||
|
do {
|
||
|
rb_fd_zero(&stp->fds);
|
||
|
rb_fd_set(stp->dst_fd, &stp->fds);
|
||
|
ret = rb_fd_select(rb_fd_max(&stp->fds), NULL, &stp->fds, NULL, NULL);
|
||
|
} while (ret == -1 && nogvl_copy_stream_continue_p(stp));
|
||
|
if (ret == -1) {
|
||
|
stp->syserr = "select";
|
||
|
stp->error_no = errno;
|
||
| ... | ... | |
|
}
|
||
|
}
|
||
|
if (ss == -1) {
|
||
|
if (nogvl_copy_stream_continue_p(stp))
|
||
|
goto retry_sendfile;
|
||
|
switch (errno) {
|
||
|
case EINVAL:
|
||
|
#ifdef ENOSYS
|
||
| ... | ... | |
|
#endif
|
||
|
if (maygvl_copy_stream_wait_readwrite(stp) == -1)
|
||
|
return -1;
|
||
|
if (rb_thread_interrupted(stp->th))
|
||
|
return -1;
|
||
|
goto retry_sendfile;
|
||
|
}
|
||
|
stp->syserr = "sendfile";
|
||
| ... | ... | |
|
return 0;
|
||
|
}
|
||
|
if (ss == -1) {
|
||
|
if (nogvl_copy_stream_continue_p(stp))
|
||
|
goto retry_read;
|
||
|
switch (errno) {
|
||
|
case EAGAIN:
|
||
|
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
|
||
| ... | ... | |
|
while (len) {
|
||
|
ss = write(stp->dst_fd, buf+off, len);
|
||
|
if (ss == -1) {
|
||
|
if (nogvl_copy_stream_continue_p(stp))
|
||
|
continue;
|
||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||
|
if (nogvl_copy_stream_wait_write(stp) == -1)
|
||
|
return -1;
|
||
| ... | ... | |
|
if (!use_eof)
|
||
|
copy_length -= ss;
|
||
|
if (rb_thread_interrupted(stp->th))
|
||
|
return;
|
||
|
}
|
||
|
}
|
||