SF patch #760257: add socket.timeout exception

(Contributed by Bob Halley)

Added a new exception, socket.timeout so that timeouts can be differentiated
from other socket exceptions.

Docs, more tests, and newsitem to follow.
This commit is contained in:
Raymond Hettinger 2003-06-29 03:08:05 +00:00
parent 3e2244c9e1
commit ef7343c6cd
1 changed files with 113 additions and 38 deletions

View File

@ -328,6 +328,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
static PyObject *socket_error; static PyObject *socket_error;
static PyObject *socket_herror; static PyObject *socket_herror;
static PyObject *socket_gaierror; static PyObject *socket_gaierror;
static PyObject *socket_timeout;
#ifdef RISCOS #ifdef RISCOS
/* Global variable which is !=0 if Python is running in a RISC OS taskwindow */ /* Global variable which is !=0 if Python is running in a RISC OS taskwindow */
@ -575,21 +576,23 @@ internal_setblocking(PySocketSockObject *s, int block)
/* Do a select() on the socket, if necessary (sock_timeout > 0). /* Do a select() on the socket, if necessary (sock_timeout > 0).
The argument writing indicates the direction. The argument writing indicates the direction.
This does not raise an exception or return a success indicator; This does not raise an exception; we'll let our caller do that
we'll let the actual socket call do that. */ after they've reacquired the interpreter lock.
static void Returns 1 on timeout, 0 otherwise. */
static int
internal_select(PySocketSockObject *s, int writing) internal_select(PySocketSockObject *s, int writing)
{ {
fd_set fds; fd_set fds;
struct timeval tv; struct timeval tv;
int n;
/* Nothing to do unless we're in timeout mode (not non-blocking) */ /* Nothing to do unless we're in timeout mode (not non-blocking) */
if (s->sock_timeout <= 0.0) if (s->sock_timeout <= 0.0)
return; return 0;
/* Guard against closed socket */ /* Guard against closed socket */
if (s->sock_fd < 0) if (s->sock_fd < 0)
return; return 0;
/* Construct the arguments to select */ /* Construct the arguments to select */
tv.tv_sec = (int)s->sock_timeout; tv.tv_sec = (int)s->sock_timeout;
@ -599,9 +602,12 @@ internal_select(PySocketSockObject *s, int writing)
/* See if the socket is ready */ /* See if the socket is ready */
if (writing) if (writing)
select(s->sock_fd+1, NULL, &fds, NULL, &tv); n = select(s->sock_fd+1, NULL, &fds, NULL, &tv);
else else
select(s->sock_fd+1, &fds, NULL, NULL, &tv); n = select(s->sock_fd+1, &fds, NULL, NULL, &tv);
if (n == 0)
return 1;
return 0;
} }
/* Initialize a new socket object. */ /* Initialize a new socket object. */
@ -1090,16 +1096,30 @@ sock_accept(PySocketSockObject *s)
PyObject *sock = NULL; PyObject *sock = NULL;
PyObject *addr = NULL; PyObject *addr = NULL;
PyObject *res = NULL; PyObject *res = NULL;
int timeout;
if (!getsockaddrlen(s, &addrlen)) if (!getsockaddrlen(s, &addrlen))
return NULL; return NULL;
memset(addrbuf, 0, addrlen); memset(addrbuf, 0, addrlen);
#ifdef MS_WINDOWS
newfd = INVALID_SOCKET;
#else
newfd = -1;
#endif
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
internal_select(s, 0); timeout = internal_select(s, 0);
newfd = accept(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen); if (!timeout)
newfd = accept(s->sock_fd, (struct sockaddr *) addrbuf,
&addrlen);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) {
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (newfd == INVALID_SOCKET) if (newfd == INVALID_SOCKET)
#else #else
@ -1405,10 +1425,12 @@ PyDoc_STRVAR(close_doc,
Close the socket. It cannot be used after this call."); Close the socket. It cannot be used after this call.");
static int static int
internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen) internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
int *timeoutp)
{ {
int res; int res, timeout;
timeout = 0;
res = connect(s->sock_fd, addr, addrlen); res = connect(s->sock_fd, addr, addrlen);
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
@ -1423,9 +1445,10 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen)
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(s->sock_fd, &fds); FD_SET(s->sock_fd, &fds);
res = select(s->sock_fd+1, NULL, &fds, NULL, &tv); res = select(s->sock_fd+1, NULL, &fds, NULL, &tv);
if (res == 0) if (res == 0) {
res = WSAEWOULDBLOCK; res = WSAEWOULDBLOCK;
else if (res > 0) timeout = 1;
} else if (res > 0)
res = 0; res = 0;
/* else if (res < 0) an error occurred */ /* else if (res < 0) an error occurred */
} }
@ -1438,7 +1461,7 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen)
if (s->sock_timeout > 0.0) { if (s->sock_timeout > 0.0) {
if (res < 0 && errno == EINPROGRESS) { if (res < 0 && errno == EINPROGRESS) {
internal_select(s, 1); timeout = internal_select(s, 1);
res = connect(s->sock_fd, addr, addrlen); res = connect(s->sock_fd, addr, addrlen);
if (res < 0 && errno == EISCONN) if (res < 0 && errno == EISCONN)
res = 0; res = 0;
@ -1449,6 +1472,7 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen)
res = errno; res = errno;
#endif #endif
*timeoutp = timeout;
return res; return res;
} }
@ -1461,14 +1485,19 @@ sock_connect(PySocketSockObject *s, PyObject *addro)
struct sockaddr *addr; struct sockaddr *addr;
int addrlen; int addrlen;
int res; int res;
int timeout;
if (!getsockaddrarg(s, addro, &addr, &addrlen)) if (!getsockaddrarg(s, addro, &addr, &addrlen))
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
res = internal_connect(s, addr, addrlen); res = internal_connect(s, addr, addrlen, &timeout);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) {
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
if (res != 0) if (res != 0)
return s->errorhandler(); return s->errorhandler();
Py_INCREF(Py_None); Py_INCREF(Py_None);
@ -1490,12 +1519,13 @@ sock_connect_ex(PySocketSockObject *s, PyObject *addro)
struct sockaddr *addr; struct sockaddr *addr;
int addrlen; int addrlen;
int res; int res;
int timeout;
if (!getsockaddrarg(s, addro, &addr, &addrlen)) if (!getsockaddrarg(s, addro, &addr, &addrlen))
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
res = internal_connect(s, addr, addrlen); res = internal_connect(s, addr, addrlen, &timeout);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
return PyInt_FromLong((long) res); return PyInt_FromLong((long) res);
@ -1716,7 +1746,7 @@ The mode and buffersize arguments are as for the built-in open() function.");
static PyObject * static PyObject *
sock_recv(PySocketSockObject *s, PyObject *args) sock_recv(PySocketSockObject *s, PyObject *args)
{ {
int len, n, flags = 0; int len, n = 0, flags = 0, timeout;
PyObject *buf; PyObject *buf;
#ifdef __VMS #ifdef __VMS
int read_length; int read_length;
@ -1738,10 +1768,16 @@ sock_recv(PySocketSockObject *s, PyObject *args)
#ifndef __VMS #ifndef __VMS
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
internal_select(s, 0); timeout = internal_select(s, 0);
n = recv(s->sock_fd, PyString_AS_STRING(buf), len, flags); if (!timeout)
n = recv(s->sock_fd, PyString_AS_STRING(buf), len, flags);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) {
Py_DECREF(buf);
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
if (n < 0) { if (n < 0) {
Py_DECREF(buf); Py_DECREF(buf);
return s->errorhandler(); return s->errorhandler();
@ -1763,10 +1799,16 @@ sock_recv(PySocketSockObject *s, PyObject *args)
} }
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
internal_select(s, 0); timeout = internal_select(s, 0);
n = recv(s->sock_fd, read_buf, segment, flags); if (!timeout)
n = recv(s->sock_fd, read_buf, segment, flags);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) {
Py_DECREF(buf);
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
if (n < 0) { if (n < 0) {
Py_DECREF(buf); Py_DECREF(buf);
return s->errorhandler(); return s->errorhandler();
@ -1805,7 +1847,7 @@ sock_recvfrom(PySocketSockObject *s, PyObject *args)
PyObject *buf = NULL; PyObject *buf = NULL;
PyObject *addr = NULL; PyObject *addr = NULL;
PyObject *ret = NULL; PyObject *ret = NULL;
int len, n, flags = 0; int len, n = 0, flags = 0, timeout;
socklen_t addrlen; socklen_t addrlen;
if (!PyArg_ParseTuple(args, "i|i:recvfrom", &len, &flags)) if (!PyArg_ParseTuple(args, "i|i:recvfrom", &len, &flags))
@ -1819,20 +1861,26 @@ sock_recvfrom(PySocketSockObject *s, PyObject *args)
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
memset(addrbuf, 0, addrlen); memset(addrbuf, 0, addrlen);
internal_select(s, 0); timeout = internal_select(s, 0);
n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags, if (!timeout)
n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags,
#ifndef MS_WINDOWS #ifndef MS_WINDOWS
#if defined(PYOS_OS2) && !defined(PYCC_GCC) #if defined(PYOS_OS2) && !defined(PYCC_GCC)
(struct sockaddr *)addrbuf, &addrlen (struct sockaddr *)addrbuf, &addrlen
#else #else
(void *)addrbuf, &addrlen (void *)addrbuf, &addrlen
#endif #endif
#else #else
(struct sockaddr *)addrbuf, &addrlen (struct sockaddr *)addrbuf, &addrlen
#endif #endif
); );
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) {
Py_DECREF(buf);
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
if (n < 0) { if (n < 0) {
Py_DECREF(buf); Py_DECREF(buf);
return s->errorhandler(); return s->errorhandler();
@ -1864,7 +1912,7 @@ static PyObject *
sock_send(PySocketSockObject *s, PyObject *args) sock_send(PySocketSockObject *s, PyObject *args)
{ {
char *buf; char *buf;
int len, n, flags = 0; int len, n = 0, flags = 0, timeout;
#ifdef __VMS #ifdef __VMS
int send_length; int send_length;
#endif #endif
@ -1874,10 +1922,15 @@ sock_send(PySocketSockObject *s, PyObject *args)
#ifndef __VMS #ifndef __VMS
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
internal_select(s, 1); timeout = internal_select(s, 1);
n = send(s->sock_fd, buf, len, flags); if (!timeout)
n = send(s->sock_fd, buf, len, flags);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) {
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
if (n < 0) if (n < 0)
return s->errorhandler(); return s->errorhandler();
#else #else
@ -1895,9 +1948,14 @@ sock_send(PySocketSockObject *s, PyObject *args)
segment = send_length; segment = send_length;
} }
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
internal_select(s, 1); timeout = internal_select(s, 1);
n = send(s->sock_fd, buf, segment, flags); if (!timeout)
n = send(s->sock_fd, buf, segment, flags);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) {
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
if (n < 0) { if (n < 0) {
return s->errorhandler(); return s->errorhandler();
} }
@ -1922,14 +1980,16 @@ static PyObject *
sock_sendall(PySocketSockObject *s, PyObject *args) sock_sendall(PySocketSockObject *s, PyObject *args)
{ {
char *buf; char *buf;
int len, n, flags = 0; int len, n = 0, flags = 0, timeout;
if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags)) if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags))
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
do { do {
internal_select(s, 1); timeout = internal_select(s, 1);
if (timeout)
break;
n = send(s->sock_fd, buf, len, flags); n = send(s->sock_fd, buf, len, flags);
if (n < 0) if (n < 0)
break; break;
@ -1938,6 +1998,10 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
} while (len > 0); } while (len > 0);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) {
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
if (n < 0) if (n < 0)
return s->errorhandler(); return s->errorhandler();
@ -1962,7 +2026,7 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
PyObject *addro; PyObject *addro;
char *buf; char *buf;
struct sockaddr *addr; struct sockaddr *addr;
int addrlen, len, n, flags; int addrlen, len, n = 0, flags, timeout;
flags = 0; flags = 0;
if (!PyArg_ParseTuple(args, "s#O:sendto", &buf, &len, &addro)) { if (!PyArg_ParseTuple(args, "s#O:sendto", &buf, &len, &addro)) {
@ -1976,10 +2040,15 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
internal_select(s, 1); timeout = internal_select(s, 1);
n = sendto(s->sock_fd, buf, len, flags, addr, addrlen); if (!timeout)
n = sendto(s->sock_fd, buf, len, flags, addr, addrlen);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (timeout) {
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
if (n < 0) if (n < 0)
return s->errorhandler(); return s->errorhandler();
return PyInt_FromLong((long)n); return PyInt_FromLong((long)n);
@ -3409,6 +3478,12 @@ init_socket(void)
return; return;
Py_INCREF(socket_gaierror); Py_INCREF(socket_gaierror);
PyModule_AddObject(m, "gaierror", socket_gaierror); PyModule_AddObject(m, "gaierror", socket_gaierror);
socket_timeout = PyErr_NewException("socket.timeout",
socket_error, NULL);
if (socket_timeout == NULL)
return;
Py_INCREF(socket_timeout);
PyModule_AddObject(m, "timeout", socket_timeout);
Py_INCREF((PyObject *)&sock_type); Py_INCREF((PyObject *)&sock_type);
if (PyModule_AddObject(m, "SocketType", if (PyModule_AddObject(m, "SocketType",
(PyObject *)&sock_type) != 0) (PyObject *)&sock_type) != 0)