common.py 3.2 KB

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