Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

wait.py 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import errno
  2. from functools import partial
  3. import select
  4. import sys
  5. try:
  6. from time import monotonic
  7. except ImportError:
  8. from time import time as monotonic
  9. __all__ = ["NoWayToWaitForSocketError", "wait_for_read", "wait_for_write"]
  10. class NoWayToWaitForSocketError(Exception):
  11. pass
  12. # How should we wait on sockets?
  13. #
  14. # There are two types of APIs you can use for waiting on sockets: the fancy
  15. # modern stateful APIs like epoll/kqueue, and the older stateless APIs like
  16. # select/poll. The stateful APIs are more efficient when you have a lots of
  17. # sockets to keep track of, because you can set them up once and then use them
  18. # lots of times. But we only ever want to wait on a single socket at a time
  19. # and don't want to keep track of state, so the stateless APIs are actually
  20. # more efficient. So we want to use select() or poll().
  21. #
  22. # Now, how do we choose between select() and poll()? On traditional Unixes,
  23. # select() has a strange calling convention that makes it slow, or fail
  24. # altogether, for high-numbered file descriptors. The point of poll() is to fix
  25. # that, so on Unixes, we prefer poll().
  26. #
  27. # On Windows, there is no poll() (or at least Python doesn't provide a wrapper
  28. # for it), but that's OK, because on Windows, select() doesn't have this
  29. # strange calling convention; plain select() works fine.
  30. #
  31. # So: on Windows we use select(), and everywhere else we use poll(). We also
  32. # fall back to select() in case poll() is somehow broken or missing.
  33. if sys.version_info >= (3, 5):
  34. # Modern Python, that retries syscalls by default
  35. def _retry_on_intr(fn, timeout):
  36. return fn(timeout)
  37. else:
  38. # Old and broken Pythons.
  39. def _retry_on_intr(fn, timeout):
  40. if timeout is not None and timeout <= 0:
  41. return fn(timeout)
  42. if timeout is None:
  43. deadline = float("inf")
  44. else:
  45. deadline = monotonic() + timeout
  46. while True:
  47. try:
  48. return fn(timeout)
  49. # OSError for 3 <= pyver < 3.5, select.error for pyver <= 2.7
  50. except (OSError, select.error) as e:
  51. # 'e.args[0]' incantation works for both OSError and select.error
  52. if e.args[0] != errno.EINTR:
  53. raise
  54. else:
  55. timeout = deadline - monotonic()
  56. if timeout < 0:
  57. timeout = 0
  58. if timeout == float("inf"):
  59. timeout = None
  60. continue
  61. def select_wait_for_socket(sock, read=False, write=False, timeout=None):
  62. if not read and not write:
  63. raise RuntimeError("must specify at least one of read=True, write=True")
  64. rcheck = []
  65. wcheck = []
  66. if read:
  67. rcheck.append(sock)
  68. if write:
  69. wcheck.append(sock)
  70. # When doing a non-blocking connect, most systems signal success by
  71. # marking the socket writable. Windows, though, signals success by marked
  72. # it as "exceptional". We paper over the difference by checking the write
  73. # sockets for both conditions. (The stdlib selectors module does the same
  74. # thing.)
  75. fn = partial(select.select, rcheck, wcheck, wcheck)
  76. rready, wready, xready = _retry_on_intr(fn, timeout)
  77. return bool(rready or wready or xready)
  78. def poll_wait_for_socket(sock, read=False, write=False, timeout=None):
  79. if not read and not write:
  80. raise RuntimeError("must specify at least one of read=True, write=True")
  81. mask = 0
  82. if read:
  83. mask |= select.POLLIN
  84. if write:
  85. mask |= select.POLLOUT
  86. poll_obj = select.poll()
  87. poll_obj.register(sock, mask)
  88. # For some reason, poll() takes timeout in milliseconds
  89. def do_poll(t):
  90. if t is not None:
  91. t *= 1000
  92. return poll_obj.poll(t)
  93. return bool(_retry_on_intr(do_poll, timeout))
  94. def null_wait_for_socket(*args, **kwargs):
  95. raise NoWayToWaitForSocketError("no select-equivalent available")
  96. def _have_working_poll():
  97. # Apparently some systems have a select.poll that fails as soon as you try
  98. # to use it, either due to strange configuration or broken monkeypatching
  99. # from libraries like eventlet/greenlet.
  100. try:
  101. poll_obj = select.poll()
  102. poll_obj.poll(0)
  103. except (AttributeError, OSError):
  104. return False
  105. else:
  106. return True
  107. def wait_for_socket(*args, **kwargs):
  108. # We delay choosing which implementation to use until the first time we're
  109. # called. We could do it at import time, but then we might make the wrong
  110. # decision if someone goes wild with monkeypatching select.poll after
  111. # we're imported.
  112. global wait_for_socket
  113. if _have_working_poll():
  114. wait_for_socket = poll_wait_for_socket
  115. elif hasattr(select, "select"):
  116. wait_for_socket = select_wait_for_socket
  117. else: # Platform-specific: Appengine.
  118. wait_for_socket = null_wait_for_socket
  119. return wait_for_socket(*args, **kwargs)
  120. def wait_for_read(sock, timeout=None):
  121. """ Waits for reading to be available on a given socket.
  122. Returns True if the socket is readable, or False if the timeout expired.
  123. """
  124. return wait_for_socket(sock, read=True, timeout=timeout)
  125. def wait_for_write(sock, timeout=None):
  126. """ Waits for writing to be available on a given socket.
  127. Returns True if the socket is readable, or False if the timeout expired.
  128. """
  129. return wait_for_socket(sock, write=True, timeout=timeout)