From: unknown Date: Tue, 23 Mar 2010 14:29:46 +0000 (-0500) Subject: Implementing suggested changes X-Git-Tag: v1.0.0~72 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9f4c6af84ac58b903cb500249bb20bac13b87d62;p=thirdparty%2Ftornado.git Implementing suggested changes --- diff --git a/tornado/fcntl_win32.py b/tornado/fcntl_win32.py deleted file mode 100644 index 2dfdae436..000000000 --- a/tornado/fcntl_win32.py +++ /dev/null @@ -1,52 +0,0 @@ -import ctypes -import ctypes.wintypes -import os - - -# See: http://msdn.microsoft.com/en-us/library/ms738573(VS.85).aspx -ioctlsocket = ctypes.windll.ws2_32.ioctlsocket -ioctlsocket.argtypes = (ctypes.wintypes.HANDLE, ctypes.wintypes.LONG, ctypes.wintypes.ULONG) -ioctlsocket.restype = ctypes.c_int - -# See: http://msdn.microsoft.com/en-us/library/ms724935(VS.85).aspx -SetHandleInformation = ctypes.windll.kernel32.SetHandleInformation -SetHandleInformation.argtypes = (ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD) -SetHandleInformation.restype = ctypes.wintypes.BOOL - -HANDLE_FLAG_INHERIT = 0x00000001 - - -F_GETFD = 1 -F_SETFD = 2 -F_GETFL = 3 -F_SETFL = 4 - -FD_CLOEXEC = 1 - -os.O_NONBLOCK = 2048 - -FIONBIO = 126 - - -def fcntl(fd, op, arg=0): - if op == F_GETFD or op == F_GETFL: - return 0 - elif op == F_SETFD: - # Check that the flag is CLOEXEC and translate - if arg == FD_CLOEXEC: - success = SetHandleInformation(fd, HANDLE_FLAG_INHERIT, arg) - if not success: - raise ctypes.GetLastError() - else: - raise ValueError("Unsupported arg") - #elif op == F_SETFL: - ## Check that the flag is NONBLOCK and translate - #if arg == os.O_NONBLOCK: - ##pass - #result = ioctlsocket(fd, FIONBIO, 1) - #if result != 0: - #raise ctypes.GetLastError() - #else: - #raise ValueError("Unsupported arg") - else: - raise ValueError("Unsupported op") diff --git a/tornado/httpserver.py b/tornado/httpserver.py index 178d8e9d8..60af4d96f 100644 --- a/tornado/httpserver.py +++ b/tornado/httpserver.py @@ -23,7 +23,7 @@ try: except ImportError: import sys if sys.platform.startswith("win"): - import fcntl_win32 as fcntl + import win32_support as fcntl else: raise import functools diff --git a/tornado/ioloop.py b/tornado/ioloop.py index 9f43b2955..9a7ea709d 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -23,7 +23,8 @@ try: import fcntl except ImportError: if os.name != 'posix': - import socket + import win32_support + import win32_support as fcntl else: raise import logging @@ -31,83 +32,6 @@ import select import time -class Pipe(object): - """Create an OS independent asynchronous pipe""" - def __init__(self): - self.reader = None - self.writer = None - self.reader_fd = -1 - if os.name == 'posix': - self.reader_fd, w = os.pipe() - IOLoop.set_nonblocking(self.reader_fd) - IOLoop.set_nonblocking(w) - IOLoop.set_close_exec(self.reader_fd) - IOLoop.set_close_exec(w) - self.reader = os.fdopen(self.reader_fd, "r", 0) - self.writer = os.fdopen(w, "w", 0) - else: - # Based on Zope async.py: http://svn.zope.org/zc.ngi/trunk/src/zc/ngi/async.py - - self.writer = socket.socket() - # Disable buffering -- pulling the trigger sends 1 byte, - # and we want that sent immediately, to wake up asyncore's - # select() ASAP. - self.writer.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - - count = 0 - while 1: - count += 1 - # Bind to a local port; for efficiency, let the OS pick - # a free port for us. - # Unfortunately, stress tests showed that we may not - # be able to connect to that port ("Address already in - # use") despite that the OS picked it. This appears - # to be a race bug in the Windows socket implementation. - # So we loop until a connect() succeeds (almost always - # on the first try). See the long thread at - # http://mail.zope.org/pipermail/zope/2005-July/160433.html - # for hideous details. - a = socket.socket() - a.bind(("127.0.0.1", 0)) - connect_address = a.getsockname() # assigned (host, port) pair - a.listen(1) - try: - self.writer.connect(connect_address) - break # success - except socket.error, detail: - if detail[0] != errno.WSAEADDRINUSE: - # "Address already in use" is the only error - # I've seen on two WinXP Pro SP2 boxes, under - # Pythons 2.3.5 and 2.4.1. - raise - # (10048, 'Address already in use') - # assert count <= 2 # never triggered in Tim's tests - if count >= 10: # I've never seen it go above 2 - a.close() - self.writer.close() - raise BindError("Cannot bind trigger!") - # Close `a` and try again. Note: I originally put a short - # sleep() here, but it didn't appear to help or hurt. - a.close() - - self.reader, addr = a.accept() - self.reader.setblocking(0) - self.writer.setblocking(0) - a.close() - self.reader_fd = self.reader.fileno() - - def read(self): - try: - return self.reader.recv(1) - except socket.error, ex: - if ex.args[0] == errno.EWOULDBLOCK: - raise IOError - raise - - def write(self, data): - return self.writer.send(data) - - class IOLoop(object): """A level-triggered I/O loop. @@ -165,7 +89,7 @@ class IOLoop(object): def __init__(self, impl=None): self._impl = impl or _poll() if hasattr(self._impl, 'fileno'): - self.set_close_exec(self._impl.fileno()) + self._set_close_exec(self._impl.fileno()) self._handlers = {} self._events = {} self._callbacks = set() @@ -175,9 +99,18 @@ class IOLoop(object): # Create a pipe that we send bogus data to when we want to wake # the I/O loop when it is idle - trigger = Pipe() - self._waker_reader = self._waker_writer = trigger - self.add_handler(trigger.reader_fd, self._read_waker, self.READ) + if os.name == 'posix': + r, w = os.pipe() + self._set_nonblocking(r) + self._set_nonblocking(w) + self._set_close_exec(r) + self._set_close_exec(w) + self._waker_reader = os.fdopen(r, "r", 0) + self._waker_writer = os.fdopen(w, "w", 0) + else: + pipe = win32_support.Pipe() + r = pipe.reader_fd + self.add_handler(r, self._read_waker, self.READ) @classmethod def instance(cls): @@ -364,13 +297,11 @@ class IOLoop(object): except IOError: pass - @staticmethod - def set_nonblocking(fd): + def _set_nonblocking(self, fd): flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) - @staticmethod - def set_close_exec(fd): + def _set_close_exec(self, fd): flags = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) diff --git a/tornado/win32_support.py b/tornado/win32_support.py new file mode 100644 index 000000000..88cfc1601 --- /dev/null +++ b/tornado/win32_support.py @@ -0,0 +1,119 @@ +import ctypes +import ctypes.wintypes +import os +import socket + + +# See: http://msdn.microsoft.com/en-us/library/ms738573(VS.85).aspx +ioctlsocket = ctypes.windll.ws2_32.ioctlsocket +ioctlsocket.argtypes = (ctypes.wintypes.HANDLE, ctypes.wintypes.LONG, ctypes.wintypes.ULONG) +ioctlsocket.restype = ctypes.c_int + +# See: http://msdn.microsoft.com/en-us/library/ms724935(VS.85).aspx +SetHandleInformation = ctypes.windll.kernel32.SetHandleInformation +SetHandleInformation.argtypes = (ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD) +SetHandleInformation.restype = ctypes.wintypes.BOOL + +HANDLE_FLAG_INHERIT = 0x00000001 + + +F_GETFD = 1 +F_SETFD = 2 +F_GETFL = 3 +F_SETFL = 4 + +FD_CLOEXEC = 1 + +os.O_NONBLOCK = 2048 + +FIONBIO = 126 + + +def fcntl(fd, op, arg=0): + if op == F_GETFD or op == F_GETFL: + return 0 + elif op == F_SETFD: + # Check that the flag is CLOEXEC and translate + if arg == FD_CLOEXEC: + success = SetHandleInformation(fd, HANDLE_FLAG_INHERIT, arg) + if not success: + raise ctypes.GetLastError() + else: + raise ValueError("Unsupported arg") + #elif op == F_SETFL: + ## Check that the flag is NONBLOCK and translate + #if arg == os.O_NONBLOCK: + ##pass + #result = ioctlsocket(fd, FIONBIO, 1) + #if result != 0: + #raise ctypes.GetLastError() + #else: + #raise ValueError("Unsupported arg") + else: + raise ValueError("Unsupported op") + + +class Pipe(object): + """Create an OS independent asynchronous pipe""" + def __init__(self): + # Based on Zope async.py: http://svn.zope.org/zc.ngi/trunk/src/zc/ngi/async.py + + self.writer = socket.socket() + # Disable buffering -- pulling the trigger sends 1 byte, + # and we want that sent immediately, to wake up ASAP. + self.writer.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + + count = 0 + while 1: + count += 1 + # Bind to a local port; for efficiency, let the OS pick + # a free port for us. + # Unfortunately, stress tests showed that we may not + # be able to connect to that port ("Address already in + # use") despite that the OS picked it. This appears + # to be a race bug in the Windows socket implementation. + # So we loop until a connect() succeeds (almost always + # on the first try). See the long thread at + # http://mail.zope.org/pipermail/zope/2005-July/160433.html + # for hideous details. + a = socket.socket() + a.bind(("127.0.0.1", 0)) + connect_address = a.getsockname() # assigned (host, port) pair + a.listen(1) + try: + self.writer.connect(connect_address) + break # success + except socket.error, detail: + if detail[0] != errno.WSAEADDRINUSE: + # "Address already in use" is the only error + # I've seen on two WinXP Pro SP2 boxes, under + # Pythons 2.3.5 and 2.4.1. + raise + # (10048, 'Address already in use') + # assert count <= 2 # never triggered in Tim's tests + if count >= 10: # I've never seen it go above 2 + a.close() + self.writer.close() + raise BindError("Cannot bind trigger!") + # Close `a` and try again. Note: I originally put a short + # sleep() here, but it didn't appear to help or hurt. + a.close() + + self.reader, addr = a.accept() + self.reader.setblocking(0) + self.writer.setblocking(0) + a.close() + self.reader_fd = self.reader.fileno() + + def read(self): + """Emulate a file descriptors read method""" + try: + return self.reader.recv(1) + except socket.error, ex: + if ex.args[0] == errno.EWOULDBLOCK: + raise IOError + raise + + def write(self, data): + """Emulate a file descriptors write method""" + return self.writer.send(data)