/* * call-seq: * io.wait_writable -> IO * io.wait_writable(timeout) -> IO or nil * * Waits until IO writable is available or times out and returns self or * nil when EOF is reached. */ static VALUE io_wait_writable(int argc, VALUE *argv, VALUE io) { rb_io_t *fptr; int i; VALUE timeout; struct timeval timerec; struct timeval *tv; GetOpenFile(io, fptr); rb_io_check_writable(fptr); rb_scan_args(argc, argv, "01", &timeout); if (NIL_P(timeout)) { tv = NULL; } else { timerec = rb_time_interval(timeout); tv = &timerec; } i = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_OUT, tv); if (i < 0) rb_sys_fail(0); rb_io_check_closed(fptr); if (i & RB_WAITFD_OUT) return io; return Qnil; }
static VALUE io_wait_readable(int argc, VALUE *argv, VALUE io) { rb_io_t *fptr; int i; ioctl_arg n; VALUE timeout; struct timeval timerec; struct timeval *tv; GetOpenFile(io, fptr); rb_io_check_readable(fptr); rb_scan_args(argc, argv, "01", &timeout); if (NIL_P(timeout)) { tv = NULL; } else { timerec = rb_time_interval(timeout); tv = &timerec; } if (rb_io_read_pending(fptr)) return Qtrue; if (!FIONREAD_POSSIBLE_P(fptr->fd)) return Qfalse; i = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_IN, tv); if (i < 0) rb_sys_fail(0); rb_io_check_closed(fptr); if (ioctl(fptr->fd, FIONREAD, &n)) rb_sys_fail(0); if (n > 0) return io; return Qnil; }
static int kgio_timedwait(VALUE self, VALUE timeout, int write_p) { struct timeval tv = rb_time_interval(timeout); int events = write_p ? RB_WAITFD_OUT : RB_WAITFD_IN; return rb_wait_for_single_fd(my_fileno(self), events, &tv); }
/* emulate blocking connect behavior on EINTR or non-blocking socket */ static int wait_connectable(int fd) { int sockerr, revents; socklen_t sockerrlen; /* only to clear pending error */ sockerrlen = (socklen_t)sizeof(sockerr); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen) < 0) return -1; /* * Stevens book says, successful finish turn on RB_WAITFD_OUT and * failure finish turn on both RB_WAITFD_IN and RB_WAITFD_OUT. * So it's enough to wait only RB_WAITFD_OUT and check the pending error * by getsockopt(). * * Note: rb_wait_for_single_fd already retries on EINTR/ERESTART */ revents = rb_wait_for_single_fd(fd, RB_WAITFD_IN|RB_WAITFD_OUT, NULL); if (revents < 0) return -1; sockerrlen = (socklen_t)sizeof(sockerr); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen) < 0) return -1; switch (sockerr) { case 0: /* * be defensive in case some platforms set SO_ERROR on the original, * interrupted connect() */ case EINTR: #ifdef ERESTART case ERESTART: #endif case EAGAIN: #ifdef EINPROGRESS case EINPROGRESS: #endif #ifdef EALREADY case EALREADY: #endif #ifdef EISCONN case EISCONN: #endif return 0; /* success */ default: /* likely (but not limited to): ECONNREFUSED, ETIMEDOUT, EHOSTUNREACH */ errno = sockerr; return -1; } return 0; }
static int wait_connectable(int fd) { int sockerr; socklen_t sockerrlen; int revents; int ret; for (;;) { /* * Stevens book says, succuessful finish turn on RB_WAITFD_OUT and * failure finish turn on both RB_WAITFD_IN and RB_WAITFD_OUT. */ revents = rb_wait_for_single_fd(fd, RB_WAITFD_IN|RB_WAITFD_OUT, NULL); if (revents & (RB_WAITFD_IN|RB_WAITFD_OUT)) { sockerrlen = (socklen_t)sizeof(sockerr); ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen); /* * Solaris getsockopt(SO_ERROR) return -1 and set errno * in getsockopt(). Let's return immediately. */ if (ret < 0) break; if (sockerr == 0) { if (revents & RB_WAITFD_OUT) break; else continue; /* workaround for winsock */ } /* BSD and Linux use sockerr. */ errno = sockerr; ret = -1; break; } if ((revents & (RB_WAITFD_IN|RB_WAITFD_OUT)) == RB_WAITFD_OUT) { ret = 0; break; } } return ret; }
static VALUE do_query(void *args) { struct async_query_args *async_args; struct timeval tv; struct timeval* tvp; long int sec; int retval; VALUE read_timeout; async_args = (struct async_query_args *)args; read_timeout = rb_iv_get(async_args->self, "@read_timeout"); tvp = NULL; if (!NIL_P(read_timeout)) { Check_Type(read_timeout, T_FIXNUM); tvp = &tv; sec = FIX2INT(read_timeout); // TODO: support partial seconds? // also, this check is here for sanity, we also check up in Ruby if (sec >= 0) { tvp->tv_sec = sec; } else { rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec); } tvp->tv_usec = 0; } for(;;) { retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp); if (retval == 0) { rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout)); } if (retval < 0) { rb_sys_fail(0); } if (retval > 0) { break; } } return Qnil; }