图片解析应用
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.

208 lines
6.1 KiB

  1. """A threading based handler.
  2. The :class:`SequentialThreadingHandler` is intended for regular Python
  3. environments that use threads.
  4. .. warning::
  5. Do not use :class:`SequentialThreadingHandler` with applications
  6. using asynchronous event loops (like gevent). Use the
  7. :class:`~kazoo.handlers.gevent.SequentialGeventHandler` instead.
  8. """
  9. from __future__ import absolute_import
  10. import atexit
  11. import logging
  12. import queue
  13. import socket
  14. import threading
  15. import time
  16. from kazoo.handlers import utils
  17. from kazoo.handlers.utils import selector_select
  18. # sentinel objects
  19. _STOP = object()
  20. log = logging.getLogger(__name__)
  21. def _to_fileno(obj):
  22. if isinstance(obj, int):
  23. fd = int(obj)
  24. elif hasattr(obj, "fileno"):
  25. fd = obj.fileno()
  26. if not isinstance(fd, int):
  27. raise TypeError("fileno() returned a non-integer")
  28. fd = int(fd)
  29. else:
  30. raise TypeError("argument must be an int, or have a fileno() method.")
  31. if fd < 0:
  32. raise ValueError(
  33. "file descriptor cannot be a negative integer (%d)" % (fd,)
  34. )
  35. return fd
  36. class KazooTimeoutError(Exception):
  37. pass
  38. class AsyncResult(utils.AsyncResult):
  39. """A one-time event that stores a value or an exception"""
  40. def __init__(self, handler):
  41. super(AsyncResult, self).__init__(
  42. handler, threading.Condition, KazooTimeoutError
  43. )
  44. class SequentialThreadingHandler(object):
  45. """Threading handler for sequentially executing callbacks.
  46. This handler executes callbacks in a sequential manner. A queue is
  47. created for each of the callback events, so that each type of event
  48. has its callback type run sequentially. These are split into two
  49. queues, one for watch events and one for async result completion
  50. callbacks.
  51. Each queue type has a thread worker that pulls the callback event
  52. off the queue and runs it in the order the client sees it.
  53. This split helps ensure that watch callbacks won't block session
  54. re-establishment should the connection be lost during a Zookeeper
  55. client call.
  56. Watch and completion callbacks should avoid blocking behavior as
  57. the next callback of that type won't be run until it completes. If
  58. you need to block, spawn a new thread and return immediately so
  59. callbacks can proceed.
  60. .. note::
  61. Completion callbacks can block to wait on Zookeeper calls, but
  62. no other completion callbacks will execute until the callback
  63. returns.
  64. """
  65. name = "sequential_threading_handler"
  66. timeout_exception = KazooTimeoutError
  67. sleep_func = staticmethod(time.sleep)
  68. queue_impl = queue.Queue
  69. queue_empty = queue.Empty
  70. def __init__(self):
  71. """Create a :class:`SequentialThreadingHandler` instance"""
  72. self.callback_queue = self.queue_impl()
  73. self.completion_queue = self.queue_impl()
  74. self._running = False
  75. self._state_change = threading.Lock()
  76. self._workers = []
  77. @property
  78. def running(self):
  79. return self._running
  80. def _create_thread_worker(self, work_queue):
  81. def _thread_worker(): # pragma: nocover
  82. while True:
  83. try:
  84. func = work_queue.get()
  85. try:
  86. if func is _STOP:
  87. break
  88. func()
  89. except Exception:
  90. log.exception("Exception in worker queue thread")
  91. finally:
  92. work_queue.task_done()
  93. del func # release before possible idle
  94. except self.queue_empty:
  95. continue
  96. t = self.spawn(_thread_worker)
  97. return t
  98. def start(self):
  99. """Start the worker threads."""
  100. with self._state_change:
  101. if self._running:
  102. return
  103. # Spawn our worker threads, we have
  104. # - A callback worker for watch events to be called
  105. # - A completion worker for completion events to be called
  106. for work_queue in (self.completion_queue, self.callback_queue):
  107. w = self._create_thread_worker(work_queue)
  108. self._workers.append(w)
  109. self._running = True
  110. atexit.register(self.stop)
  111. def stop(self):
  112. """Stop the worker threads and empty all queues."""
  113. with self._state_change:
  114. if not self._running:
  115. return
  116. self._running = False
  117. for work_queue in (self.completion_queue, self.callback_queue):
  118. work_queue.put(_STOP)
  119. self._workers.reverse()
  120. while self._workers:
  121. worker = self._workers.pop()
  122. worker.join()
  123. # Clear the queues
  124. self.callback_queue = self.queue_impl()
  125. self.completion_queue = self.queue_impl()
  126. atexit.unregister(self.stop)
  127. def select(self, *args, **kwargs):
  128. return selector_select(*args, **kwargs)
  129. def socket(self):
  130. return utils.create_tcp_socket(socket)
  131. def create_connection(self, *args, **kwargs):
  132. return utils.create_tcp_connection(socket, *args, **kwargs)
  133. def create_socket_pair(self):
  134. return utils.create_socket_pair(socket)
  135. def event_object(self):
  136. """Create an appropriate Event object"""
  137. return threading.Event()
  138. def lock_object(self):
  139. """Create a lock object"""
  140. return threading.Lock()
  141. def rlock_object(self):
  142. """Create an appropriate RLock object"""
  143. return threading.RLock()
  144. def async_result(self):
  145. """Create a :class:`AsyncResult` instance"""
  146. return AsyncResult(self)
  147. def spawn(self, func, *args, **kwargs):
  148. t = threading.Thread(target=func, args=args, kwargs=kwargs)
  149. t.daemon = True
  150. t.start()
  151. return t
  152. def dispatch_callback(self, callback):
  153. """Dispatch to the callback object
  154. The callback is put on separate queues to run depending on the
  155. type as documented for the :class:`SequentialThreadingHandler`.
  156. """
  157. self.callback_queue.put(lambda: callback.func(*callback.args))