Re: [Python-Dev] Modules/socketmodule.c: avoiding second fcntl() call worth the effort?

2013-01-20 Thread Peter Portante
FWIW, looking into Python's Lib/socket.py module, it would seem that the
dup() method of a _sockobject() just transfers the underlying fd, not
performing a new socket create. So a caller could know about that behavior
and work to call setblocking(0) only when it has a socket not seen before.

In eventlet's case, it would appear the code could be taught this fact, and
adjusted accordingly. I'll propose a patch for eventlet that will eliminate
most of these calls in their case.

Thanks for your time,

-peter


On Sun, Jan 20, 2013 at 1:31 AM, Peter Portante
wrote:

> I don't have a concrete case where a socket object's setblocking() method
> is called with a value in one module, handed off to another module (which
> does not know what the first did with it) which in turn also calls
> setblocking() with the same value. It certainly seems that that not is a
> common pattern, but perhaps one could argue a valid pattern, since the
> state of blocking/nonblocking is maintained in the kernel behind the
> fcntl() system calls.
>
> Here is what I am seeing concretely.
>
> This is the syscall pattern from eventlet/wsgi.py + eventlet/greenio.py
> (after removing redundants call to set_nonblocking (see
> https://bitbucket.org/portante/eventlet/commits/cc27508f4bbaaea566aecb51cf6c8b4629b083bd)).
> First, these are the call stacks for the three calls to the
> set_nonblocking() method, made in one HTTP request; the
> greenio.py:set_nonblocking() method wraps the socketmodule.c:setblocking()
> method:
>
> pid 1385
>   File "/usr/bin/swift-object-server", line 22, in 
> run_wsgi(conf_file, 'object-server', default_port=6000, **options)
>   File "/usr/lib/python2.6/site-packages/swift/common/wsgi.py", line 194,
> in run_wsgi
> run_server(max_clients)
>   File "/usr/lib/python2.6/site-packages/swift/common/wsgi.py", line 158,
> in run_server
> wsgi.server(sock, app, NullLogger(), custom_pool=pool)
>   File "/usr/lib/python2.6/site-packages/eventlet/wsgi.py", line 598, in
> server
> *client_socket = sock.accept()*
>   File "/usr/lib/python2.6/site-packages/eventlet/greenio.py", line 163,
> in accept
> return type(self)(client), addr
>   File "/usr/lib/python2.6/site-packages/eventlet/greenio.py", line 133,
> in __init__
> *set_nonblocking(fd)*
>
> pid 1385
>   File "/usr/lib/python2.6/site-packages/eventlet/greenpool.py", line 80,
> in _spawn_n_impl
> func(*args, **kwargs)
>   File "/usr/lib/python2.6/site-packages/eventlet/wsgi.py", line 516, in
> process_request
> proto = self.protocol(socket, address, self)
>   File "/usr/lib64/python2.6/SocketServer.py", line 616, in __init__
> self.setup()
>   File "/usr/lib/python2.6/site-packages/eventlet/wsgi.py", line 174, in
> setup
> *self.rfile = conn.makefile('rb', self.rbufsize)*
>   File "/usr/lib/python2.6/site-packages/eventlet/greenio.py", line 219,
> in makefile
> return _fileobject(self.dup(), *args, **kw)
>   File "/usr/lib/python2.6/site-packages/eventlet/greenio.py", line 214,
> in dup
> newsock = type(self)(sock)
>   File "/usr/lib/python2.6/site-packages/eventlet/greenio.py", line 133,
> in __init__
> *set_nonblocking(fd)*
>
> pid 1385
>   File "/usr/lib/python2.6/site-packages/eventlet/greenpool.py", line 80,
> in _spawn_n_impl
> func(*args, **kwargs)
>   File "/usr/lib/python2.6/site-packages/eventlet/wsgi.py", line 516, in
> process_request
> proto = self.protocol(socket, address, self)
>   File "/usr/lib64/python2.6/SocketServer.py", line 616, in __init__
> self.setup()
>   File "/usr/lib/python2.6/site-packages/eventlet/wsgi.py", line 175, in
> setup
> *self.wfile = conn.makefile('wb', self.wbufsize)*
>   File "/usr/lib/python2.6/site-packages/eventlet/greenio.py", line 219,
> in makefile
> return _fileobject(self.dup(), *args, **kw)
>   File "/usr/lib/python2.6/site-packages/eventlet/greenio.py", line 214,
> in dup
> newsock = type(self)(sock)
>   File "/usr/lib/python2.6/site-packages/eventlet/greenio.py", line 133,
> in __init__
> *set_nonblocking(fd)*
>
> The first one above is expected, the next two unexpectedly result in
> fcntl() calls on the same fd. The strace looks like:
>
> accept(8, {sa_family=AF_INET, sin_port=htons(54375),
> sin_addr=inet_addr("127.0.0.1")}, [16]) = 14
> fcntl(14, F_GETFL)  = 0x2 (flags O_RDWR)
> fcntl(14, F_SETFL, O_RDWR|O_NONBLOCK)   = 0
> # *self.rfile = conn.makefile('rb', self.rbufsize)*
> fcntl(14, F_GETFL)  = 0x802 (flags O_RDWR|O_NONBLOCK)
> fcntl(14, F_SETFL, O_RDWR|O_NONBLOCK)   = 0
> # *self.wfile = conn.makefile('wb', self.wbufsize)*
> fcntl(14, F_GETFL)  = 0x802 (flags O_RDWR|O_NONBLOCK)
> fcntl(14, F_SETFL, O_RDWR|O_NONBLOCK)   = 0
> recvfrom(14, "GET /sdb1/234456/AUTH_del0/gprfc"..., 8192, 0, NULL, NULL) =
> 353
> getsockname(14, {sa_family=AF_INET, sin_port=htons(6010),
> sin_addr=inet_addr("127.0.0.1")}, [16]) = 0
> ...
>
> It appears that conn.makefile() is attempting to dup() t

Re: [Python-Dev] Modules/socketmodule.c: avoiding second fcntl() call worth the effort?

2013-01-20 Thread Victor Stinner
Since Linux 2.6.27, it's possible to set SOCK_NONBLOCK directly at the
creation of the socket (using socket() and accept4()). So you don't
need any extra call.

I implemented something similar for SOCK_CLOEXEC flag to implement the
PEP 433. See for example:
http://hg.python.org/features/pep-433/file/1097ffc652f4/Modules/socketmodule.c#l3918
and:
http://hg.python.org/features/pep-433/file/1097ffc652f4/Modules/socketmodule.c#l1950

The PEP 433 (Easier suppression of file descriptor inheritance):
http://python.org/dev/peps/pep-0433/

But I added a new keyword argument to socket.socket() and
socket.socket.accept() for that. I don't know if you can do something
similar without adding a new argument.

--

Instead of two calls to fcntl() (2 syscalls), you can also use "int
opt = 1; ioctl(fd, FIONBIO, &opt);" (1 syscall).

Victor
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com