From 8fe21f6758bb877efacce1fa6573e72625252585 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 15 Jun 2011 08:30:03 +0000 Subject: [PATCH] make timed kgio_wait_* implementation safer IO.select can handle fd >= 1024 safely in some Rubies while fd_set may not. We could use rb_thread_fd_select(), but rb_wait_for_single_fd() is available now so the former is not worth the maintenance hassle. --- ext/kgio/time_interval.h | 71 ------------------------------------------- ext/kgio/wait.c | 55 ++++++++++++++++++++++----------- ext/kgio/wait_for_single_fd.h | 46 ---------------------------- 3 files changed, 37 insertions(+), 135 deletions(-) delete mode 100644 ext/kgio/time_interval.h delete mode 100644 ext/kgio/wait_for_single_fd.h diff --git a/ext/kgio/time_interval.h b/ext/kgio/time_interval.h deleted file mode 100644 index 89f5556..0000000 --- a/ext/kgio/time_interval.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifdef HAVE_RB_TIME_INTERVAL -/* not declared in public headers for Ruby <= 1.9.2 */ -struct timeval rb_time_interval(VALUE num); -#else -#include -#ifndef NUM2TIMET -# define NUM2TIMET NUM2INT -#endif -#ifndef RFLOAT_VALUE -# define RFLOAT_VALUE(f) (RFLOAT(f)->value) -#endif - -static void negative_interval(void) -{ - rb_raise(rb_eArgError, "time interval must be positive"); -} - -static struct timeval kgio_time_interval(VALUE num) -{ - struct timeval tv; - - switch (TYPE(num)) { - case T_FIXNUM: - case T_BIGNUM: - tv.tv_sec = NUM2TIMET(num); - if (tv.tv_sec < 0) - negative_interval(); - tv.tv_usec = 0; - break; - case T_FLOAT: { - double f, d; - double val = RFLOAT_VALUE(num); - - if (val < 0.0) - negative_interval(); - - d = modf(val, &f); - if (d >= 0) { - tv.tv_usec = (long)(d * 1e6 + 0.5); - } else { - tv.tv_usec = (long)(-d * 1e6 + 0.5); - if (tv.tv_usec > 0) { - tv.tv_usec = 1000000 - tv.tv_usec; - f -= 1; - } - } - tv.tv_sec = (time_t)f; - if (f != tv.tv_sec) - rb_raise(rb_eRangeError, "%f out of range", val); - } - break; - default: { - VALUE f; - VALUE ary = rb_funcall(num, rb_intern("divmod"), 1, INT2FIX(1)); - - Check_Type(ary, T_ARRAY); - - tv.tv_sec = NUM2TIMET(rb_ary_entry(ary, 0)); - f = rb_ary_entry(ary, 1); - f = rb_funcall(f, '*', 1, INT2FIX(1000000)); - tv.tv_usec = NUM2LONG(f); - - if (tv.tv_sec < 0) - negative_interval(); - - } - } - return tv; -} -#define rb_time_interval(v) kgio_time_interval(v) -#endif /* HAVE_RB_TIME_INTERVAL */ diff --git a/ext/kgio/wait.c b/ext/kgio/wait.c index 68ad99f..fe3896d 100644 --- a/ext/kgio/wait.c +++ b/ext/kgio/wait.c @@ -1,23 +1,42 @@ #include "kgio.h" -#include "time_interval.h" -#include "wait_for_single_fd.h" - static ID id_wait_rd, id_wait_wr; -static int kgio_io_wait(int argc, VALUE *argv, VALUE self, int events) +#if defined(HAVE_RB_TIME_INTERVAL) && defined(HAVE_RB_WAIT_FOR_SINGLE_FD) +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); +} +#else /* ! (HAVE_RB_TIME_INTERVAL && HAVE_RB_WAIT_FOR_SINGLE_FD) */ +static int kgio_timedwait(VALUE self, VALUE timeout, int write_p) { - int fd = my_fileno(self); - VALUE t; - struct timeval *tp; - struct timeval tv; - - if (rb_scan_args(argc, argv, "01", &t) == 0) { - tp = NULL; - } else { - tv = rb_time_interval(t); - tp = &tv; - } - return rb_wait_for_single_fd(fd, events, tp); + VALUE argv[4]; + VALUE set = rb_ary_new3(1, self); + + argv[0] = write_p ? Qnil : set; + argv[1] = write_p ? set : Qnil; + argv[2] = Qnil; + argv[3] = timeout; + + set = rb_funcall2(rb_cIO, rb_intern("select"), 4, argv); + return NIL_P(set) ? 0 : 1; +} +#endif /* ! (HAVE_RB_TIME_INTERVAL && HAVE_RB_WAIT_FOR_SINGLE_FD) */ + +static int kgio_wait(int argc, VALUE *argv, VALUE self, int write_p) +{ + int fd; + VALUE timeout; + + if (rb_scan_args(argc, argv, "01", &timeout) == 1 && !NIL_P(timeout)) + return kgio_timedwait(self, timeout, write_p); + + fd = my_fileno(self); + errno = EAGAIN; + write_p ? rb_io_wait_writable(fd) : rb_io_wait_readable(fd); + return 1; } /* @@ -39,7 +58,7 @@ static int kgio_io_wait(int argc, VALUE *argv, VALUE self, int events) */ static VALUE kgio_wait_readable(int argc, VALUE *argv, VALUE self) { - int r = kgio_io_wait(argc, argv, self, RB_WAITFD_IN); + int r = kgio_wait(argc, argv, self, 0); if (r < 0) rb_sys_fail("kgio_wait_readable"); return r == 0 ? Qnil : self; @@ -59,7 +78,7 @@ static VALUE kgio_wait_readable(int argc, VALUE *argv, VALUE self) */ static VALUE kgio_wait_writable(int argc, VALUE *argv, VALUE self) { - int r = kgio_io_wait(argc, argv, self, RB_WAITFD_OUT); + int r = kgio_wait(argc, argv, self, 1); if (r < 0) rb_sys_fail("kgio_wait_writable"); return r == 0 ? Qnil : self; diff --git a/ext/kgio/wait_for_single_fd.h b/ext/kgio/wait_for_single_fd.h deleted file mode 100644 index 8e37318..0000000 --- a/ext/kgio/wait_for_single_fd.h +++ /dev/null @@ -1,46 +0,0 @@ -/* 1.9.3 uses ppoll() for this */ -#ifndef HAVE_RB_WAIT_FOR_SINGLE_FD -#ifdef HAVE_SYS_SELECT_H -# include -#endif - -#if defined(HAVE_POLL) -# include -# define RB_WAITFD_IN POLLIN -# define RB_WAITFD_PRI POLLPRI -# define RB_WAITFD_OUT POLLOUT -#else -# define RB_WAITFD_IN 0x001 -# define RB_WAITFD_PRI 0x002 -# define RB_WAITFD_OUT 0x004 -#endif - -static int kgio_wait_for_single_fd(int fd, int events, struct timeval *tv) -{ - fd_set fds; - fd_set *rfds; - fd_set *wfds; - int r; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - if (events == RB_WAITFD_IN) { - rfds = &fds; - wfds = NULL; - } else if (events == RB_WAITFD_OUT) { - rfds = NULL; - wfds = &fds; - } else { - rb_bug("incomplete rb_wait_for_single_fd emulation"); - } - - r = rb_thread_select(fd + 1, rfds, wfds, NULL, tv); - if (r <= 0) - return r; - return events; - rb_bug("rb_wait_for_single_fd emulation bug"); -} -#define rb_wait_for_single_fd(fd,events,tv) \ - kgio_wait_for_single_fd((fd),(events),(tv)) -#endif -- 2.11.4.GIT