Skip to content
Browse files

Implementing suggested changes

  • Loading branch information...
1 parent ae7f90b commit 9f4c6af84ac58b903cb500249bb20bac13b87d62 unknown committed Mar 23, 2010
Showing with 137 additions and 139 deletions.
  1. +0 −52 tornado/fcntl_win32.py
  2. +1 −1 tornado/httpserver.py
  3. +17 −86 tornado/ioloop.py
  4. +119 −0 tornado/win32_support.py
View
52 tornado/fcntl_win32.py
@@ -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")
View
2 tornado/httpserver.py
@@ -23,7 +23,7 @@
except ImportError:
import sys
if sys.platform.startswith("win"):
- import fcntl_win32 as fcntl
+ import win32_support as fcntl
else:
raise
import functools
View
103 tornado/ioloop.py
@@ -23,91 +23,15 @@
import fcntl
except ImportError:
if os.name != 'posix':
- import socket
+ import win32_support
+ import win32_support as fcntl
else:
raise
import logging
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 @@ def connection_ready(sock, fd, events):
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 @@ def __init__(self, impl=None):
# 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 @@ def _read_waker(self, fd, events):
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)
View
119 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)

0 comments on commit 9f4c6af

Please sign in to comment.
Something went wrong with that request. Please try again.