diff --git a/ChangeLog b/ChangeLog index a0278fb27a..dae0b4f4eb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Wed May 4 11:42:47 2011 KOSAKI Motohiro + + * ext/socket/init.c (wait_connectable): fix error handling code. + RB_WAITFD_OUT is turned on even though an error occur. + Wed May 4 10:12:39 2011 KOSAKI Motohiro * ext/readline/readline.c (readline_event): use rb_wait_for_single_fd(). diff --git a/ext/socket/init.c b/ext/socket/init.c index 4c32ab4720..e55736cabe 100644 --- a/ext/socket/init.c +++ b/ext/socket/init.c @@ -258,22 +258,42 @@ wait_connectable(int fd) { int sockerr; socklen_t sockerrlen; - int r; + int revents; + int ret; for (;;) { - r = rb_wait_for_single_fd(fd, RB_WAITFD_OUT|RB_WAITFD_PRI, NULL); - if ((r > 0) && (r & RB_WAITFD_OUT)) - return 0; + /* + * 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); - sockerrlen = (socklen_t)sizeof(sockerr); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, - &sockerrlen) == 0) { + 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) 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 -1; } + + return ret; } #ifdef __CYGWIN__