common.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. """Lowest-common-denominator implementations of platform functionality."""
  2. from __future__ import absolute_import, division, print_function
  3. import errno
  4. import socket
  5. import time
  6. from tornado.platform import interface
  7. from tornado.util import errno_from_exception
  8. def try_close(f):
  9. # Avoid issue #875 (race condition when using the file in another
  10. # thread).
  11. for i in range(10):
  12. try:
  13. f.close()
  14. except IOError:
  15. # Yield to another thread
  16. time.sleep(1e-3)
  17. else:
  18. break
  19. # Try a last time and let raise
  20. f.close()
  21. class Waker(interface.Waker):
  22. """Create an OS independent asynchronous pipe.
  23. For use on platforms that don't have os.pipe() (or where pipes cannot
  24. be passed to select()), but do have sockets. This includes Windows
  25. and Jython.
  26. """
  27. def __init__(self):
  28. from .auto import set_close_exec
  29. # Based on Zope select_trigger.py:
  30. # https://github.com/zopefoundation/Zope/blob/master/src/ZServer/medusa/thread/select_trigger.py
  31. self.writer = socket.socket()
  32. set_close_exec(self.writer.fileno())
  33. # Disable buffering -- pulling the trigger sends 1 byte,
  34. # and we want that sent immediately, to wake up ASAP.
  35. self.writer.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
  36. count = 0
  37. while 1:
  38. count += 1
  39. # Bind to a local port; for efficiency, let the OS pick
  40. # a free port for us.
  41. # Unfortunately, stress tests showed that we may not
  42. # be able to connect to that port ("Address already in
  43. # use") despite that the OS picked it. This appears
  44. # to be a race bug in the Windows socket implementation.
  45. # So we loop until a connect() succeeds (almost always
  46. # on the first try). See the long thread at
  47. # http://mail.zope.org/pipermail/zope/2005-July/160433.html
  48. # for hideous details.
  49. a = socket.socket()
  50. set_close_exec(a.fileno())
  51. a.bind(("127.0.0.1", 0))
  52. a.listen(1)
  53. connect_address = a.getsockname() # assigned (host, port) pair
  54. try:
  55. self.writer.connect(connect_address)
  56. break # success
  57. except socket.error as detail:
  58. if (not hasattr(errno, 'WSAEADDRINUSE') or
  59. errno_from_exception(detail) != errno.WSAEADDRINUSE):
  60. # "Address already in use" is the only error
  61. # I've seen on two WinXP Pro SP2 boxes, under
  62. # Pythons 2.3.5 and 2.4.1.
  63. raise
  64. # (10048, 'Address already in use')
  65. # assert count <= 2 # never triggered in Tim's tests
  66. if count >= 10: # I've never seen it go above 2
  67. a.close()
  68. self.writer.close()
  69. raise socket.error("Cannot bind trigger!")
  70. # Close `a` and try again. Note: I originally put a short
  71. # sleep() here, but it didn't appear to help or hurt.
  72. a.close()
  73. self.reader, addr = a.accept()
  74. set_close_exec(self.reader.fileno())
  75. self.reader.setblocking(0)
  76. self.writer.setblocking(0)
  77. a.close()
  78. self.reader_fd = self.reader.fileno()
  79. def fileno(self):
  80. return self.reader.fileno()
  81. def write_fileno(self):
  82. return self.writer.fileno()
  83. def wake(self):
  84. try:
  85. self.writer.send(b"x")
  86. except (IOError, socket.error, ValueError):
  87. pass
  88. def consume(self):
  89. try:
  90. while True:
  91. result = self.reader.recv(1024)
  92. if not result:
  93. break
  94. except (IOError, socket.error):
  95. pass
  96. def close(self):
  97. self.reader.close()
  98. try_close(self.writer)