greenio: socket.dup() made excess fcntl syscalls, added set_nonblocking=True kwarg to skip it; Thanks to Peter Portante

https://bitbucket.org/which_linden/eventlet/pull-request/27
This commit is contained in:
Sergey Shepelev
2013-01-24 01:58:30 +04:00
parent 1720c5bb7c
commit 36f9a7dc8c
3 changed files with 56 additions and 7 deletions

View File

@@ -78,3 +78,4 @@ Thanks To
* Eric Windisch, zmq getsockopt(EVENTS) wake correct threads (pull request 22)
* Raymond Lu, fixing busy-wait in eventlet.green.ssl.socket.sendall()
* Thomas Grainger, webcrawler example small fix, "requests" library import bug report
* Peter Portante, save syscalls in socket.dup(), environ[REMOTE_PORT] in wsgi

View File

@@ -97,8 +97,10 @@ def set_nonblocking(fd):
"(Windows pipes don't support non-blocking I/O)")
# We managed to import fcntl.
fileno = fd.fileno()
flags = fcntl.fcntl(fileno, fcntl.F_GETFL)
fcntl.fcntl(fileno, fcntl.F_SETFL, flags | os.O_NONBLOCK)
orig_flags = fcntl.fcntl(fileno, fcntl.F_GETFL)
new_flags = orig_flags | os.O_NONBLOCK
if new_flags != orig_flags:
fcntl.fcntl(fileno, fcntl.F_SETFL, new_flags)
else:
# socket supports setblocking()
setblocking(0)
@@ -114,8 +116,13 @@ class GreenSocket(object):
"""
Green version of socket.socket class, that is intended to be 100%
API-compatible.
It also recognizes the keyword parameter, 'set_nonblocking=True'.
Pass False to indicate that socket is already in non-blocking mode
to save syscalls.
"""
def __init__(self, family_or_realsock=socket.AF_INET, *args, **kwargs):
should_set_nonblocking = kwargs.pop('set_nonblocking', True)
if isinstance(family_or_realsock, (int, long)):
fd = _original_socket(family_or_realsock, *args, **kwargs)
else:
@@ -129,7 +136,8 @@ class GreenSocket(object):
except AttributeError:
self._timeout = socket.getdefaulttimeout()
set_nonblocking(fd)
if should_set_nonblocking:
set_nonblocking(fd)
self.fd = fd
# when client calls setblocking(0) or settimeout(0) the socket must
# act non-blocking
@@ -209,8 +217,7 @@ class GreenSocket(object):
def dup(self, *args, **kw):
sock = self.fd.dup(*args, **kw)
set_nonblocking(sock)
newsock = type(self)(sock)
newsock = type(self)(sock, set_nonblocking=False)
newsock.settimeout(self.gettimeout())
return newsock

View File

@@ -4,12 +4,13 @@ from eventlet import event, greenio, debug
from eventlet.hubs import get_hub
from eventlet.green import socket, time
from eventlet.support import get_errno
import errno
import array
import errno
import eventlet
import fcntl
import os
import sys
import array
import tempfile, shutil
@@ -551,6 +552,35 @@ class TestGreenSocket(LimitedTestCase):
self.assertRaises(socket.timeout, client.recv, 1)
server.wait()
def test_default_nonblocking(self):
sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
flags = fcntl.fcntl(sock1.fd.fileno(), fcntl.F_GETFL)
assert flags & os.O_NONBLOCK
sock2 = socket.socket(sock1.fd)
flags = fcntl.fcntl(sock2.fd.fileno(), fcntl.F_GETFL)
assert flags & os.O_NONBLOCK
def test_dup_nonblocking(self):
sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
flags = fcntl.fcntl(sock1.fd.fileno(), fcntl.F_GETFL)
assert flags & os.O_NONBLOCK
sock2 = sock1.dup()
flags = fcntl.fcntl(sock2.fd.fileno(), fcntl.F_GETFL)
assert flags & os.O_NONBLOCK
def test_skip_nonblocking(self):
sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fd = sock1.fd.fileno()
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
flags = fcntl.fcntl(fd, fcntl.F_SETFL, flags & ~os.O_NONBLOCK)
assert flags & os.O_NONBLOCK == 0
sock2 = socket.socket(sock1.fd, set_nonblocking=False)
flags = fcntl.fcntl(sock2.fd.fileno(), fcntl.F_GETFL)
assert flags & os.O_NONBLOCK == 0
class TestGreenPipe(LimitedTestCase):
@skip_on_windows
@@ -821,5 +851,16 @@ class TestGreenIoStarvation(LimitedTestCase):
assert maxstartdiff * 2 < runlengths[0], "Largest difference in starting times more than twice the shortest running time!"
assert runlengths[0] * 2 > runlengths[-1], "Longest runtime more than twice as long as shortest!"
def test_set_nonblocking():
sock = _orig_sock.socket(socket.AF_INET, socket.SOCK_DGRAM)
fileno = sock.fileno()
orig_flags = fcntl.fcntl(fileno, fcntl.F_GETFL)
assert orig_flags & os.O_NONBLOCK == 0
greenio.set_nonblocking(sock)
new_flags = fcntl.fcntl(fileno, fcntl.F_GETFL)
assert new_flags == (orig_flags | os.O_NONBLOCK)
if __name__ == '__main__':
main()