diff --git a/AUTHORS b/AUTHORS index 541de37..04b1a41 100644 --- a/AUTHORS +++ b/AUTHORS @@ -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 diff --git a/eventlet/greenio.py b/eventlet/greenio.py index b97266a..b6946e8 100644 --- a/eventlet/greenio.py +++ b/eventlet/greenio.py @@ -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 diff --git a/tests/greenio_test.py b/tests/greenio_test.py index 72c5529..c15ebd5 100644 --- a/tests/greenio_test.py +++ b/tests/greenio_test.py @@ -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()