[Python-Dev] Socket timeout patch

Michael Gilfix mgilfix@eecs.tufts.edu
Mon, 27 May 2002 15:50:37 -0400


  Whew. Lots of stuff. Give me a while to address all these issues,
rework the patch, and then submit a comprehensive answer. Should
have an answer by the end of the week latest.

                 -- Mike

On Thu, May 23 @ 16:13, Guido van Rossum wrote:
> Sorry for taking such a long time to review this.  (Apparently nobody
> else had time either.)  And thanks for doing this!  I think this is a
> great start.  Rather than typing them in the SF bug manager issue I am
> mailing them (I'll add a link to the archived version of this message
> to the SF bug manager to track the activity).
> 
> 
> General style nits:
> 
> - You submitted a reverse diff!  Customary is diff(old, new).
> 
> - Please don't put a space between the function name and the open
>   parenthesis.  (You do this both in C and in Python code.)
> 
> - Also please don't put a space between the open parenthesis and the
>   first argument (you do this almost consistently in test_timeout.py).
> 
> - Please don't introduce lines longer than 78 columns.
> 
> 
> Feedback on the patch to socket.py:
> 
> - I think you're changing the semantics of unbuffered and
>   line-buffered reads/writes on Windows.  For one thing, you no longer
>   implement line-buffered writes correctly.  The idea is that if the
>   buffer size is set to 1, data is flushed at \n only, so that if
>   the code builds up the line using many small writes, this doesn't
>   result in many small sends. There was code for this in write() --
>   why did you delete it?
> 
> - It also looks like you've broken the semantics of size<0 in read().
> 
> - Maybe changing the write buffer to a list makes sense too?
> 
> - Since this code appears independent from the timeoutsocket code,
>   maybe we can discuss this separately?
> 
> 
> Feedback on the documentation:
> 
> - I would document that the argument to settimeout() should be int or
>   float (hm, can it be a long?  that should be acceptable even if it's
>   strange), and that the return value of gettimeout() is None or a
>   float.
> 
> - You may want to document the interaction with blocking mode.
> 
> 
> Feedback on the C socket module changes:
> 
> - Why import the select module?  It's much more efficient to use the
>   select system call directly.  You don't need all the overhead and
>   generality that the select module adds on top of it, and it costs a
>   lot (select allocates lots of objects and lots of memory and hence
>   is very expensive).
> 
> - <errno.h> is already included by Python.h.
> 
> - Please don't introduce more static functions with a 'Py' name
>   prefix.
> 
> - You should raise TypeError if the type of the argument is wrong, and
>   ValueError if the value is wrong (out of range).  Not SocketError.
> 
> - I believe that you can't reliably maintain a "sock_blocking" flag;
>   there are setsockopt() or ioctl() calls that can make a socket
>   blocking or non-blocking.  Also, there's no way to know whether a
>   socket passed to fromfd() is in blocking mode or not.
> 
> - There are refcount bugs.  I didn't do a detailed review of these,
>   but I note that the return value from PyFloat_FromDouble() in
>   PySocketSock_settimeout() is leaked.  (There's an INCREF that's only
>   needed for the None case.)
> 
> - The function internal_select() is *always* used like this:
> 
>         count = internal_select (s, 1);
>         if (count < 0)
>                 return NULL;
>         else if (count ==  0) /* Timeout elapsed */
>                 return PyTimeout_Err ();
> 
>   If internal_select() called PyTimeout_Err() itself, all call sites
>   could be simplified to this:
> 
>         count = internal_select (s, 1);
>         if (count < 0)
>                 return NULL;
> 
>   or even (now that the count variable is no longer needed) to this:
> 
>         if(internal_select (s, 1) < 0)
>                 return NULL;
> 
> - The accept() wrapper contains this bit of code (only showing the
>   Unix variant):
> 
> 	if (newfd < 0)
> 		if (!s->sock_blocking || (errno != EAGAIN && errno != EWOULDBLOCK))
> 			return s->errorhandler ();
> 
>   Isn't the sense of testing s->sock_blocking wrong?  I would think
>   that if we're in blocking mode we'd want to return immediately
>   without even checking the errno.  I recommend writing this out more
>   clearly, e.g. like this:
> 
>     if (s->sock_blocking)
>        return s->errorhandler();
>     /* A non-blocking accept() failed */
>     if (errno != EAGAIN && errno != EWOULDBLOCK)
>        return s->errorhandler();
> 
> - What is s->errorhandler() for?  AFAICT, this is always equal to
>   PySocket_Err!
> 
> - The whole interaction between non-blocking mode and timeout mode is
>   confusing to me.  Are you sure that this always does the right
>   thing?  Have you even thought about what "the right thing" is in all
>   4 combinations?
> 
> --Guido van Rossum (home page: http://www.python.org/~guido/)
`-> (guido)

-- 
Michael Gilfix
mgilfix@eecs.tufts.edu

For my gpg public key:
http://www.eecs.tufts.edu/~mgilfix/contact.html