This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Fix for 926423: socket timeouts + Ctrl-C don't play nice
Type: Stage:
Components: Extension Modules Versions:
process
Status: closed Resolution: out of date
Dependencies: Superseder:
Assigned To: Nosy List: irmen, loewis, tony_nelson
Priority: normal Keywords: patch

Created on 2005-01-15 11:37 by irmen, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
patch.txt irmen, 2005-01-15 11:37 patch for Modules/socketmodule.c
Messages (7)
msg47541 - (view) Author: Irmen de Jong (irmen) (Python triager) Date: 2005-01-15 11:37
Please have a close look at the attached patch.
Essentially, it fixes internal_select to not return
zero on error condition, and also adds a test for errno
at all calls to internal_select.

A few remarks:
1) as indicated in the patch, I'm not sure if errno
should also be tested in the internal_connect function;
2) 'timeout' is no longer a boolean indicating a
timeout condition, it should probably be renamed to
'selectresult' or something similar;
3) I'm not too happy with the fact that the
if(timeout==-1 && erro) test must be duplicated in a
lot of functions;
4) does it do the right thing? The check for
timeout==-1 MUST be there otherwise a lot of errors
turn up in the regression tests. With this patch they
run fine, btw

msg47542 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2005-02-24 20:42
Logged In: YES 
user_id=21627

Thanks for the patch.

1) I could not see where you dealt with internal_connect in
the patch. However, as a connect can also be cancelled with
Ctrl-C, I think you need to deal with it, too.

2) I agree, renaming it would be good

3) you could come up with a macro to avoid code duplication
(relying on consistent naming of variables, but that might
confure more than help

4) This is almost right. The critical thing is that you read
errno much too late. errno is meant to be read immediately
after the system call. Any later system call can modify the
value - including all of the pthread calls that we do
in-between. So the right solution is to copy errno right
after select, into a local variable of the caller, and read
*that* variable. In order to avoid modifying SetFromErrno,
you can assign back to errno before calling it.
msg47543 - (view) Author: Tony Nelson (tony_nelson) Date: 2006-06-18 18:00
Logged In: YES 
user_id=1356214

Martin, I'm working on an update to this patch so that it
will finally get fixed.  I have a question about your answer
to #4.  You say that errno is read "much too late", but
that's the same way that errorhandler() does it, and it
seems acceptable there.  I don't see any calls in between
that would trash errno (at least after my patch is applied
:).  Is there really a problem?  I do have it fixed, by
returning errno from internal_select(), but if it isn't
needed I'd like to simplify the patch.

BTW, I'm handling #1 (internal_connect()).  There is still
more work to do -- I think that it would be better to handle
Ctl-C as the interpreter does on its periodic checks, so
that it becomes KeyboardInterrupt instead of some other
error with a ' (4, ' in it.  Umm, I need to do more research
on that.
msg47544 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2006-06-18 18:23
Logged In: YES 
user_id=21627

If I understand correctly, you have Py_END_ALLOW_THREADS
between the system call and the access to errno.
Py_END_ALLOW_THREADS expands to PyEval_RestoreThread, which
calls PyThread_acquire_lock, which (on pthreads) calls
sem_wait, which may set errno to EINTR.

It may well be that the same mistake is made in other
places. That the code is in Python is no proof that it is
"acceptable" - only that earlier reviewers did not catch the
mistake. If so, this should perhaps be discussed on
python-dev: if we want an explicit guarantee that errno
won't change across these macros, we should change the
functions to provide that guarantee.

In general, you should assume that any C library call may
change errno, unless it does not document the values that
errno may receive due to this system call. So for any
library call between the original call and the usage of
errno, you have to analyse whether it may change errno
(directly or indirectly).
msg47545 - (view) Author: Tony Nelson (tony_nelson) Date: 2006-06-18 19:22
Logged In: YES 
user_id=1356214

Hmm, not me, but yes, socketmodule.c wraps its system calls
in Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS, and then
does its error checking.  Here's a small function (from trunk):

static PyObject *
sock_connect(PySocketSockObject *s, PyObject *addro)
{
	struct sockaddr *addr;
	int addrlen;
	int res;
	int timeout;

	if (!getsockaddrarg(s, addro, &addr, &addrlen))
		return NULL;

	Py_BEGIN_ALLOW_THREADS
	res = internal_connect(s, addr, addrlen, &timeout);
	Py_END_ALLOW_THREADS

	if (timeout) {
		PyErr_SetString(socket_timeout, "timed out");
		return NULL;
	}
	if (res != 0)
		return s->errorhandler();
	Py_INCREF(Py_None);
	return Py_None;
}

Note that both the check for timeout and also the
s->errorhandler() call (which defaults to set_error() and
probably pre-dates timeout) use errno, everywhere.  If this
is wrong, I'll bring it up on python-dev.

Do you have an example of a properly-coded module?  For
example, the first one I checked, fctlmodule.c, does it the
same way.
msg47546 - (view) Author: Tony Nelson (tony_nelson) Date: 2006-06-18 19:37
Logged In: YES 
user_id=1356214

Ehh, we can stop worrying.  From ceval.h:

"For convenience, that the value of 'errno' is restored
across Py_END_ALLOW_THREADS and Py_BLOCK_THREADS."

This means that I can remove from my patch the extra code to
restore errno.
msg47547 - (view) Author: Tony Nelson (tony_nelson) Date: 2006-07-07 23:03
Logged In: YES 
user_id=1356214

I've submitted a new patch as 1519025.
History
Date User Action Args
2022-04-11 14:56:09adminsetgithub: 41443
2005-01-15 11:37:24irmencreate