diff --git a/src/inet.c b/src/inet.c index f15a5a4..2334f53 100644 --- a/src/inet.c +++ b/src/inet.c @@ -181,7 +181,7 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) * Tries to connect to remote address (address, port) \*-------------------------------------------------------------------------*/ const char *inet_tryconnect(p_sock ps, const char *address, - unsigned short port) + unsigned short port, int timeout) { struct sockaddr_in remote; const char *err; @@ -197,14 +197,12 @@ const char *inet_tryconnect(p_sock ps, const char *address, memcpy(&remote.sin_addr, *addr, sizeof(struct in_addr)); } } else remote.sin_family = AF_UNSPEC; - sock_setblocking(ps); - err = sock_connect(ps, (SA *) &remote, sizeof(remote)); + err = sock_connect(ps, (SA *) &remote, sizeof(remote), timeout); if (err) { sock_destroy(ps); *ps = SOCK_INVALID; return err; } else { - sock_setnonblocking(ps); return NULL; } } diff --git a/src/inet.h b/src/inet.h index b69e82d..08979c1 100644 --- a/src/inet.h +++ b/src/inet.h @@ -25,7 +25,7 @@ void inet_open(lua_State *L); const char *inet_tryconnect(p_sock ps, const char *address, - unsigned short port); + unsigned short port, int timeout); const char *inet_trybind(p_sock ps, const char *address, unsigned short port, int backlog); const char *inet_trycreate(p_sock ps, int type); diff --git a/src/io.h b/src/io.h index f56094a..33675d8 100644 --- a/src/io.h +++ b/src/io.h @@ -24,6 +24,7 @@ enum { IO_CLOSED, /* the connection has been closed */ IO_ERROR, /* something wrong... */ IO_REFUSED, /* transfer has been refused */ + IO_RETRY, /* please try again */ IO_LIMITED /* maximum number of bytes reached */ }; diff --git a/src/socket.h b/src/socket.h index cea9e0d..aeadff0 100644 --- a/src/socket.h +++ b/src/socket.h @@ -34,7 +34,7 @@ const char *sock_create(p_sock ps, int domain, int type, int protocol); void sock_destroy(p_sock ps); int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, int timeout); -const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len); +const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, int timeout); const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len); void sock_listen(p_sock ps, int backlog); void sock_shutdown(p_sock ps, int how); diff --git a/src/tcp.c b/src/tcp.c index a67b44a..fe09e8c 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -257,7 +257,10 @@ static int meth_connect(lua_State *L) p_tcp tcp = (p_tcp) aux_checkclass(L, "tcp{master}", 1); const char *address = luaL_checkstring(L, 2); unsigned short port = (unsigned short) luaL_checknumber(L, 3); - const char *err = inet_tryconnect(&tcp->sock, address, port); + p_tm tm = &tcp->tm; + const char *err; + tm_markstart(tm); + err = inet_tryconnect(&tcp->sock, address, port, tm_getfailure(tm)); if (err) { lua_pushnil(L); lua_pushstring(L, err); @@ -326,7 +329,7 @@ static int meth_getsockname(lua_State *L) \*-------------------------------------------------------------------------*/ static int meth_settimeout(lua_State *L) { - p_tcp tcp = (p_tcp) aux_checkgroup(L, "tcp{client,server}", 1); + p_tcp tcp = (p_tcp) aux_checkgroup(L, "tcp{any}", 1); return tm_meth_settimeout(L, &tcp->tm); } diff --git a/src/udp.c b/src/udp.c index 21730ab..eb2cea0 100644 --- a/src/udp.c +++ b/src/udp.c @@ -335,12 +335,14 @@ static int meth_settimeout(lua_State *L) static int meth_setpeername(lua_State *L) { p_udp udp = (p_udp) aux_checkclass(L, "udp{unconnected}", 1); + p_tm tm = &udp->tm; const char *address = luaL_checkstring(L, 2); int connecting = strcmp(address, "*"); unsigned short port = connecting ? (unsigned short) luaL_checknumber(L, 3) : (unsigned short) luaL_optnumber(L, 3, 0); - const char *err = inet_tryconnect(&udp->sock, address, port); + const char *err; + err = inet_tryconnect(&udp->sock, address, port, tm_getfailure(tm)); if (err) { lua_pushnil(L); lua_pushstring(L, err); diff --git a/src/usocket.c b/src/usocket.c index 89be85e..9fcf4e8 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -2,6 +2,17 @@ * Socket compatibilization module for Unix * LuaSocket toolkit * +* We are now treating EINTRs, but if an interrupt happens in the middle of +* a select function call, we don't guarantee values timeouts anymore. +* It's not a big deal, since we are not real-time anyways. +* +* We also exchanged the order of the calls to send/recv and select. +* The idea is that the outer loop (whoever is calling sock_send/recv) +* will call the function again if we didn't time out, so we can +* call write and then select only if it fails. This moves the penalty +* to when data is not available, maximizing the bandwidth if data is +* always available. +* * RCS ID: $Id$ \*=========================================================================*/ #include @@ -49,10 +60,27 @@ const char *sock_create(p_sock ps, int domain, int type, int protocol) /*-------------------------------------------------------------------------*\ * Connects or returns error message \*-------------------------------------------------------------------------*/ -const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len) +const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, int timeout) { - if (connect(*ps, addr, addr_len) < 0) return sock_connectstrerror(); - else return NULL; + t_sock sock = *ps; + if (sock == SOCK_INVALID) return "closed"; + if (connect(sock, addr, addr_len) < 0) { + struct timeval tv; + fd_set wfds, efds; + int err; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + FD_ZERO(&wfds); FD_ZERO(&efds); + FD_SET(sock, &wfds); FD_SET(sock, &efds); + do err = select(sock+1, NULL, &wfds, &efds, timeout >= 0 ? &tv : NULL); + while (err < 0 && errno == EINTR); + if (err <= 0) return "timeout"; + if (FD_ISSET(sock, &efds)) { + char dummy; + recv(sock, &dummy, 0, 0); + return sock_connectstrerror(); + } else return NULL; + } else return NULL; } /*-------------------------------------------------------------------------*\ @@ -91,14 +119,16 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, SA dummy_addr; socklen_t dummy_len; fd_set fds; + int err; if (sock == SOCK_INVALID) return IO_CLOSED; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; FD_ZERO(&fds); FD_SET(sock, &fds); *pa = SOCK_INVALID; - if (select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL) <= 0) - return IO_TIMEOUT; + do err = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); + while (err < 0 && errno == EINTR); + if (err <= 0) return IO_TIMEOUT; if (!addr) addr = &dummy_addr; if (!addr_len) addr_len = &dummy_len; *pa = accept(sock, addr, addr_len); @@ -108,12 +138,6 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, /*-------------------------------------------------------------------------*\ * Send with timeout -* Here we exchanged the order of the calls to write and select -* The idea is that the outer loop (whoever is calling sock_send) -* will call the function again if we didn't time out, so we can -* call write and then select only if it fails. -* Should speed things up! -* We are also treating EINTR and EPIPE errors. \*-------------------------------------------------------------------------*/ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, int timeout) @@ -138,7 +162,8 @@ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, tv.tv_usec = (timeout % 1000) * 1000; FD_ZERO(&fds); FD_SET(sock, &fds); - ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL); + do ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ?&tv : NULL); + while (ret < 0 && errno == EINTR); /* tell the caller to call us again because there is more data */ if (ret > 0) return IO_DONE; /* tell the caller there was no data before timeout */ @@ -178,7 +203,8 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent, tv.tv_usec = (timeout % 1000) * 1000; FD_ZERO(&fds); FD_SET(sock, &fds); - ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL); + do ret = select(sock+1, NULL, &fds, NULL, timeout >= 0? &tv: NULL); + while (ret < 0 && errno == EINTR); /* tell the caller to call us again because there is more data */ if (ret > 0) return IO_DONE; /* tell the caller there was no data before timeout */ @@ -199,7 +225,6 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent, * will call the function again if we didn't time out, so we can * call write and then select only if it fails. * Should speed things up! -* We are also treating EINTR errors. \*-------------------------------------------------------------------------*/ int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) { @@ -218,7 +243,8 @@ int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) tv.tv_usec = (timeout % 1000) * 1000; FD_ZERO(&fds); FD_SET(sock, &fds); - ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); + do ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); + while (ret < 0 && errno == EINTR); if (ret > 0) return IO_DONE; else return IO_TIMEOUT; } else { @@ -248,7 +274,8 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got, tv.tv_usec = (timeout % 1000) * 1000; FD_ZERO(&fds); FD_SET(sock, &fds); - ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); + do ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); + while (ret < 0 && errno == EINTR); if (ret > 0) return IO_DONE; else return IO_TIMEOUT; } else { diff --git a/src/wsocket.c b/src/wsocket.c index 30208b9..bf92a90 100644 --- a/src/wsocket.c +++ b/src/wsocket.c @@ -2,6 +2,13 @@ * Socket compatibilization module for Win32 * LuaSocket toolkit * +* We also exchanged the order of the calls to send/recv and select. +* The idea is that the outer loop (whoever is calling sock_send/recv) +* will call the function again if we didn't time out, so we can +* call write and then select only if it fails. This moves the penalty +* to when data is not available, maximizing the bandwidth if data is +* always available. +* * RCS ID: $Id$ \*=========================================================================*/ #include