[Python-checkins] python/dist/src/Modules socketmodule.c,1.225,1.226 socketmodule.h,1.7,1.8

gvanrossum@users.sourceforge.net gvanrossum@users.sourceforge.net
Thu, 13 Jun 2002 08:07:46 -0700


Update of /cvsroot/python/python/dist/src/Modules
In directory usw-pr-cvs1:/tmp/cvs-serv22620/Modules

Modified Files:
	socketmodule.c socketmodule.h 
Log Message:
Major overhaul of timeout sockets:

- setblocking(0) and settimeout(0) are now equivalent, and ditto for
  setblocking(1) and settimeout(None).

- Don't raise an exception from internal_select(); let the final call
  report the error (this means you will get an EAGAIN error instead of
  an ETIMEDOUT error -- I don't care).

- Move the select to inside the Py_{BEGIN,END}_ALLOW_THREADS brackets,
  so other theads can run (this was a bug in the original code).

- Redid the retry logic in connect() and connect_ex() to avoid masking
  errors.  This probably doesn't work for Windows yet; I'll fix that
  next.  It may also fail on other platforms, depending on what
  retrying a connect does; I need help with this.

- Get rid of the retry logic in accept().  I don't think it was needed
  at all.  But I may be wrong.


Index: socketmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/socketmodule.c,v
retrieving revision 1.225
retrieving revision 1.226
diff -C2 -d -r1.225 -r1.226
*** socketmodule.c	13 Jun 2002 11:53:52 -0000	1.225
--- socketmodule.c	13 Jun 2002 15:07:44 -0000	1.226
***************
*** 447,470 ****
  }
  
- /* For timeout errors */
- static PyObject *
- timeout_err(void)
- {
- 	PyObject *v;
- 
- #ifdef MS_WINDOWS
- 	v = Py_BuildValue("(is)", WSAETIMEDOUT, "Socket operation timed out");
- #else
- 	v = Py_BuildValue("(is)", ETIMEDOUT, "Socket operation timed out");
- #endif
- 
- 	if (v != NULL) {
- 		PyErr_SetObject(socket_error, v);
- 		Py_DECREF(v);
- 	}
- 
- 	return NULL;
- }
- 
  /* Function to perform the setting of socket blocking mode
     internally. block = (1 | 0). */
--- 447,450 ----
***************
*** 509,522 ****
  }
  
! /* For access to the select module to poll the socket for timeout
!    functionality. writing is 1 for writing, 0 for reading.
!    Return value: -1 if error, 0 if not ready, >= 1 if ready.
!    An exception is set when the return value is <= 0 (!). */
! static int
  internal_select(PySocketSockObject *s, int writing)
  {
  	fd_set fds;
  	struct timeval tv;
! 	int count;
  
  	/* Construct the arguments to select */
--- 489,504 ----
  }
  
! /* Do a select() on the socket, if necessary (sock_timeout > 0).
!    The argument writing indicates the direction.
!    This does not raise an exception or return a success indicator;
!    we'll let the actual socket call do that. */
! static void
  internal_select(PySocketSockObject *s, int writing)
  {
  	fd_set fds;
  	struct timeval tv;
! 
! 	if (s->sock_timeout <= 0.0)
! 		return;
  
  	/* Construct the arguments to select */
***************
*** 528,547 ****
  	/* See if the socket is ready */
  	if (writing)
! 		count = select(s->sock_fd+1, NULL, &fds, NULL, &tv);
  	else
! 		count = select(s->sock_fd+1, &fds, NULL, NULL, &tv);
! 
! 	/* Check for errors */
! 	if (count < 0) {
! 		s->errorhandler();
! 		return -1;
! 	}
! 
! 	/* Set the error if the timeout has elapsed, i.e, we were not
! 	  polled. */
! 	if (count == 0)
! 		timeout_err();
! 
! 	return count;
  }
  
--- 510,516 ----
  	/* See if the socket is ready */
  	if (writing)
! 		select(s->sock_fd+1, NULL, &fds, NULL, &tv);
  	else
! 		select(s->sock_fd+1, &fds, NULL, NULL, &tv);
  }
  
***************
*** 559,563 ****
  	s->sock_type = type;
  	s->sock_proto = proto;
- 	s->sock_blocking = 1; /* Start in blocking mode */
  	s->sock_timeout = -1.0; /* Start without timeout */
  
--- 528,531 ----
***************
*** 998,1040 ****
  	memset(addrbuf, 0, addrlen);
  
- 	errno = 0; /* Reset indicator for use with timeout behavior */
- 
  	Py_BEGIN_ALLOW_THREADS
  	newfd = accept(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen);
  	Py_END_ALLOW_THREADS
  
- 	if (s->sock_timeout >= 0.0) {
- #ifdef MS_WINDOWS
- 		if (newfd == INVALID_SOCKET)
- 			if (!s->sock_blocking)
- 				return s->errorhandler();
- 			/* Check if we have a true failure
- 			   for a blocking socket */
- 			if (errno != WSAEWOULDBLOCK)
- 				return s->errorhandler();
- #else
- 		if (newfd < 0) {
- 			if (!s->sock_blocking)
- 				return s->errorhandler();
- 			/* Check if we have a true failure
- 			   for a blocking socket */
- 			if (errno != EAGAIN && errno != EWOULDBLOCK)
- 				return s->errorhandler();
- 		}
- #endif
- 
- 		/* try waiting the timeout period */
- 		if (internal_select(s, 0) <= 0)
- 			return NULL;
- 
- 		Py_BEGIN_ALLOW_THREADS
- 		newfd = accept(s->sock_fd,
- 			       (struct sockaddr *)addrbuf,
- 			       &addrlen);
- 		Py_END_ALLOW_THREADS
- 	}
- 
- 	/* At this point, we really have an error, whether using timeout
- 	   behavior or regular socket behavior */
  #ifdef MS_WINDOWS
  	if (newfd == INVALID_SOCKET)
--- 966,974 ----
  	memset(addrbuf, 0, addrlen);
  
  	Py_BEGIN_ALLOW_THREADS
+ 	internal_select(s, 0);
  	newfd = accept(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen);
  	Py_END_ALLOW_THREADS
  
  #ifdef MS_WINDOWS
  	if (newfd == INVALID_SOCKET)
***************
*** 1075,1079 ****
  info is a pair (hostaddr, port).";
  
! /* s.setblocking(1 | 0) method */
  
  static PyObject *
--- 1009,1016 ----
  info is a pair (hostaddr, port).";
  
! /* s.setblocking(flag) method.  Argument:
!    False -- non-blocking mode; same as settimeout(0)
!    True -- blocking mode; same as settimeout(None)
! */
  
  static PyObject *
***************
*** 1086,1091 ****
  		return NULL;
  
! 	s->sock_blocking = block;
! 	s->sock_timeout = -1.0; /* Always clear the timeout */
  	internal_setblocking(s, block);
  
--- 1023,1027 ----
  		return NULL;
  
! 	s->sock_timeout = block ? -1.0 : 0.0;
  	internal_setblocking(s, block);
  
***************
*** 1098,1139 ****
  \n\
  Set the socket to blocking (flag is true) or non-blocking (false).\n\
! This uses the FIONBIO ioctl with the O_NDELAY flag.";
  
! /* s.settimeout(None | float) method.
!    Causes an exception to be raised when the given time has
!    elapsed when performing a blocking socket operation. */
  static PyObject *
  sock_settimeout(PySocketSockObject *s, PyObject *arg)
  {
! 	double value;
  
  	if (arg == Py_None)
! 		value = -1.0;
  	else {
! 		value = PyFloat_AsDouble(arg);
! 		if (value < 0.0) {
  			if (!PyErr_Occurred())
  				PyErr_SetString(PyExc_ValueError,
! 						"Invalid timeout value");
  			return NULL;
  		}
  	}
  
! 	s->sock_timeout = value;
! 
! 	/* The semantics of setting socket timeouts are:
! 	   If you settimeout(!=None):
! 	       The actual socket gets put in non-blocking mode and the select
! 	       is used to control timeouts.
! 	   Else if you settimeout(None) [then value is -1.0]:
! 	       The old behavior is used AND automatically, the socket is set
! 	       to blocking mode. That means that someone who was doing
! 	       non-blocking stuff before, sets a timeout, and then unsets
! 	       one, will have to call setblocking(0) again if he wants
! 	       non-blocking stuff. This makes sense because timeout stuff is
! 	       blocking by nature. */
! 	internal_setblocking(s, value < 0.0);
! 
! 	s->sock_blocking = 1; /* Always negate setblocking() */
  
  	Py_INCREF(Py_None);
--- 1034,1065 ----
  \n\
  Set the socket to blocking (flag is true) or non-blocking (false).\n\
! setblocking(True) is equivalent to settimeout(None);\n\
! setblocking(False) is equivalent to settimeout(0.0).";
  
! /* s.settimeout(timeout) method.  Argument:
!    None -- no timeout, blocking mode; same as setblocking(True)
!    0.0  -- non-blocking mode; same as setblocking(False)
!    > 0  -- timeout mode; operations time out after timeout seconds
!    < 0  -- illegal; raises an exception
! */
  static PyObject *
  sock_settimeout(PySocketSockObject *s, PyObject *arg)
  {
! 	double timeout;
  
  	if (arg == Py_None)
! 		timeout = -1.0;
  	else {
! 		timeout = PyFloat_AsDouble(arg);
! 		if (timeout < 0.0) {
  			if (!PyErr_Occurred())
  				PyErr_SetString(PyExc_ValueError,
! 						"Timeout value out of range");
  			return NULL;
  		}
  	}
  
! 	s->sock_timeout = timeout;
! 	internal_setblocking(s, timeout < 0.0);
  
  	Py_INCREF(Py_None);
***************
*** 1144,1149 ****
  "settimeout(timeout)\n\
  \n\
! Set a timeout on blocking socket operations.  'timeout' can be a float,\n\
! giving seconds, or None.  Setting a timeout of None disables timeout.";
  
  /* s.gettimeout() method.
--- 1070,1077 ----
  "settimeout(timeout)\n\
  \n\
! Set a timeout on socket operations.  'timeout' can be a float,\n\
! giving in seconds, or None.  Setting a timeout of None disables\n\
! the timeout feature and is equivalent to setblocking(1).\n\
! Setting a timeout of zero is the same as setblocking(0).";
  
  /* s.gettimeout() method.
***************
*** 1356,1403 ****
  		return NULL;
  
- 	errno = 0; /* Reset the err indicator for use with timeouts */
- 
  	Py_BEGIN_ALLOW_THREADS
! 	res = connect(s->sock_fd, addr, addrlen);
! 	Py_END_ALLOW_THREADS
! 
! 	if (s->sock_timeout >= 0.0) {
! 		if (res < 0) {
! 			/* Return if we're already connected */
! #ifdef MS_WINDOWS
! 			if (errno == WSAEINVAL || errno == WSAEISCONN)
! #else
! 			if (errno == EISCONN)
! #endif
! 				goto connected;
! 
! 			/* Check if we have an error */
! 			if (!s->sock_blocking)
! 				return s->errorhandler();
! 			/* Check if we have a true failure
! 			   for a blocking socket */
! #ifdef MS_WINDOWS
! 			if (errno != WSAEWOULDBLOCK)
! #else
! 			if (errno != EINPROGRESS && errno != EALREADY &&
! 			    errno != EWOULDBLOCK)
! #endif
! 				return s->errorhandler();
! 		}
! 
! 		/* Check if we're ready for the connect via select */
! 		if (internal_select(s, 1) <= 0)
! 			return NULL;
! 
! 		/* Complete the connection now */
! 		Py_BEGIN_ALLOW_THREADS
  		res = connect(s->sock_fd, addr, addrlen);
! 		Py_END_ALLOW_THREADS
  	}
  
  	if (res < 0)
  		return s->errorhandler();
- 
- connected:
  	Py_INCREF(Py_None);
  	return Py_None;
--- 1284,1301 ----
  		return NULL;
  
  	Py_BEGIN_ALLOW_THREADS
! 	if (s->sock_timeout > 0.0) {
  		res = connect(s->sock_fd, addr, addrlen);
! 		if (res == EINPROGRESS) {
! 			internal_select(s, 1);
! 			res = connect(s->sock_fd, addr, addrlen);
! 		}
  	}
+ 	else
+ 		res = connect(s->sock_fd, addr, addrlen);
+ 	Py_END_ALLOW_THREADS
  
  	if (res < 0)
  		return s->errorhandler();
  	Py_INCREF(Py_None);
  	return Py_None;
***************
*** 1423,1467 ****
  		return NULL;
  
- 	errno = 0; /* Reset the err indicator for use with timeouts */
- 
  	Py_BEGIN_ALLOW_THREADS
! 	res = connect(s->sock_fd, addr, addrlen);
! 	Py_END_ALLOW_THREADS
! 
! 	if (s->sock_timeout >= 0.0) {
! 		if (res < 0) {
! 			/* Return if we're already connected */
! #ifdef MS_WINDOWS
! 			if (errno == WSAEINVAL || errno == WSAEISCONN)
! #else
! 			if (errno == EISCONN)
! #endif
! 				goto conex_finally;
! 
! 			/* Check if we have an error */
! 			if (!s->sock_blocking)
! 				goto conex_finally;
! 			/* Check if we have a true failure
! 			   for a blocking socket */
! #ifdef MS_WINDOWS
! 			if (errno != WSAEWOULDBLOCK)
! #else
! 			if (errno != EINPROGRESS && errno != EALREADY &&
! 			    errno != EWOULDBLOCK)
! #endif
! 				goto conex_finally;
! 		}
! 
! 		/* Check if we're ready for the connect via select */
! 		if (internal_select(s, 1) <= 0)
! 			return NULL;
! 
! 		/* Complete the connection now */
! 		Py_BEGIN_ALLOW_THREADS
  		res = connect(s->sock_fd, addr, addrlen);
! 		Py_END_ALLOW_THREADS
  	}
  
- conex_finally:
  	if (res != 0) {
  #ifdef MS_WINDOWS
--- 1321,1336 ----
  		return NULL;
  
  	Py_BEGIN_ALLOW_THREADS
! 	if (s->sock_timeout > 0.0) {
  		res = connect(s->sock_fd, addr, addrlen);
! 		if (res == EINPROGRESS) {
! 			internal_select(s, 1);
! 			res = connect(s->sock_fd, addr, addrlen);
! 		}
  	}
+ 	else
+ 		res = connect(s->sock_fd, addr, addrlen);
+ 	Py_END_ALLOW_THREADS
  
  	if (res != 0) {
  #ifdef MS_WINDOWS
***************
*** 1684,1688 ****
  	if (len < 0) {
  		PyErr_SetString(PyExc_ValueError,
! 				"negative buffersize in connect");
  		return NULL;
  	}
--- 1553,1557 ----
  	if (len < 0) {
  		PyErr_SetString(PyExc_ValueError,
! 				"negative buffersize in recv");
  		return NULL;
  	}
***************
*** 1692,1703 ****
  		return NULL;
  
- 	if (s->sock_timeout >= 0.0) {
- 		if (s->sock_blocking) {
- 			if (internal_select(s, 0) <= 0)
- 				return NULL;
- 		}
- 	}
- 
  	Py_BEGIN_ALLOW_THREADS
  	n = recv(s->sock_fd, PyString_AS_STRING(buf), len, flags);
  	Py_END_ALLOW_THREADS
--- 1561,1566 ----
  		return NULL;
  
  	Py_BEGIN_ALLOW_THREADS
+ 	internal_select(s, 0);
  	n = recv(s->sock_fd, PyString_AS_STRING(buf), len, flags);
  	Py_END_ALLOW_THREADS
***************
*** 1742,1754 ****
  		return NULL;
  
- 	if (s->sock_timeout >= 0.0) {
- 		if (s->sock_blocking) {
- 			if (internal_select(s, 0) <= 0)
- 				return NULL;
- 		}
- 	}
- 
  	Py_BEGIN_ALLOW_THREADS
  	memset(addrbuf, 0, addrlen);
  	n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags,
  #ifndef MS_WINDOWS
--- 1605,1611 ----
  		return NULL;
  
  	Py_BEGIN_ALLOW_THREADS
  	memset(addrbuf, 0, addrlen);
+ 	internal_select(s, 0);
  	n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags,
  #ifndef MS_WINDOWS
***************
*** 1800,1811 ****
  		return NULL;
  
- 	if (s->sock_timeout >= 0.0) {
- 		if (s->sock_blocking) {
- 			if (internal_select(s, 1) <= 0)
- 				return NULL;
- 		}
- 	}
- 
  	Py_BEGIN_ALLOW_THREADS
  	n = send(s->sock_fd, buf, len, flags);
  	Py_END_ALLOW_THREADS
--- 1657,1662 ----
  		return NULL;
  
  	Py_BEGIN_ALLOW_THREADS
+ 	internal_select(s, 1);
  	n = send(s->sock_fd, buf, len, flags);
  	Py_END_ALLOW_THREADS
***************
*** 1835,1846 ****
  		return NULL;
  
- 	if (s->sock_timeout >= 0.0) {
- 		if (s->sock_blocking) {
- 			if (internal_select(s, 1) <= 0)
- 				return NULL;
- 		}
- 	}
- 
  	Py_BEGIN_ALLOW_THREADS
  	do {
  		n = send(s->sock_fd, buf, len, flags);
--- 1686,1691 ----
  		return NULL;
  
  	Py_BEGIN_ALLOW_THREADS
+ 	internal_select(s, 1);
  	do {
  		n = send(s->sock_fd, buf, len, flags);
***************
*** 1889,1900 ****
  		return NULL;
  
- 	if (s->sock_timeout >= 0.0) {
- 		if (s->sock_blocking) {
- 			if (internal_select(s, 1) <= 0)
- 				return NULL;
- 		}
- 	}
- 
  	Py_BEGIN_ALLOW_THREADS
  	n = sendto(s->sock_fd, buf, len, flags, addr, addrlen);
  	Py_END_ALLOW_THREADS
--- 1734,1739 ----
  		return NULL;
  
  	Py_BEGIN_ALLOW_THREADS
+ 	internal_select(s, 1);
  	n = sendto(s->sock_fd, buf, len, flags, addr, addrlen);
  	Py_END_ALLOW_THREADS

Index: socketmodule.h
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/socketmodule.h,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -d -r1.7 -r1.8
*** socketmodule.h	7 Jun 2002 02:27:50 -0000	1.7
--- socketmodule.h	13 Jun 2002 15:07:44 -0000	1.8
***************
*** 84,90 ****
  					    errno, returns NULL and
  					    sets a Python exception */
! 	int sock_blocking;		 /* Flag indicated whether the
! 					    socket is in blocking mode */
! 	double sock_timeout;		 /* Operation timeout value */
  } PySocketSockObject;
  
--- 84,89 ----
  					    errno, returns NULL and
  					    sets a Python exception */
! 	double sock_timeout;		 /* Operation timeout in seconds;
! 					    0.0 means non-blocking */
  } PySocketSockObject;