threading.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. """
  2. Implementation of the standard :mod:`threading` using greenlets.
  3. .. note::
  4. This module is a helper for :mod:`gevent.monkey` and is not
  5. intended to be used directly. For spawning greenlets in your
  6. applications, prefer higher level constructs like
  7. :class:`gevent.Greenlet` class or :func:`gevent.spawn`.
  8. """
  9. from __future__ import absolute_import
  10. __implements__ = [
  11. 'local',
  12. '_start_new_thread',
  13. '_allocate_lock',
  14. 'Lock',
  15. '_get_ident',
  16. '_sleep',
  17. '_DummyThread',
  18. ]
  19. import threading as __threading__
  20. _DummyThread_ = __threading__._DummyThread
  21. from gevent.local import local
  22. from gevent.thread import start_new_thread as _start_new_thread, allocate_lock as _allocate_lock, get_ident as _get_ident
  23. from gevent._compat import PYPY
  24. from gevent.hub import sleep as _sleep, getcurrent
  25. # Exports, prevent unused import warnings
  26. local = local
  27. start_new_thread = _start_new_thread
  28. allocate_lock = _allocate_lock
  29. _get_ident = _get_ident
  30. _sleep = _sleep
  31. getcurrent = getcurrent
  32. Lock = _allocate_lock
  33. def _cleanup(g):
  34. __threading__._active.pop(id(g), None)
  35. def _make_cleanup_id(gid):
  36. def _(_r):
  37. __threading__._active.pop(gid, None)
  38. return _
  39. _weakref = None
  40. class _DummyThread(_DummyThread_):
  41. # We avoid calling the superclass constructor. This makes us about
  42. # twice as fast (1.16 vs 0.68usec on PyPy, 29.3 vs 17.7usec on
  43. # CPython 2.7), and has the important effect of avoiding
  44. # allocation and then immediate deletion of _Thread__block, a
  45. # lock. This is especially important on PyPy where locks go
  46. # through the cpyext API and Cython, which is known to be slow and
  47. # potentially buggy (e.g.,
  48. # https://bitbucket.org/pypy/pypy/issues/2149/memory-leak-for-python-subclass-of-cpyext#comment-22347393)
  49. # These objects are constructed quite frequently in some cases, so
  50. # the optimization matters: for example, in gunicorn, which uses
  51. # pywsgi.WSGIServer, every request is handled in a new greenlet,
  52. # and every request uses a logging.Logger to write the access log,
  53. # and every call to a log method captures the current thread (by
  54. # default).
  55. #
  56. # (Obviously we have to duplicate the effects of the constructor,
  57. # at least for external state purposes, which is potentially
  58. # slightly fragile.)
  59. # For the same reason, instances of this class will cleanup their own entry
  60. # in ``threading._active``
  61. # Capture the static things as class vars to save on memory/
  62. # construction time.
  63. # In Py2, they're all private; in Py3, they become protected
  64. _Thread__stopped = _is_stopped = _stopped = False
  65. _Thread__initialized = _initialized = True
  66. _Thread__daemonic = _daemonic = True
  67. _Thread__args = _args = ()
  68. _Thread__kwargs = _kwargs = None
  69. _Thread__target = _target = None
  70. _Thread_ident = _ident = None
  71. _Thread__started = _started = __threading__.Event()
  72. _Thread__started.set()
  73. _tstate_lock = None
  74. def __init__(self):
  75. #_DummyThread_.__init__(self) # pylint:disable=super-init-not-called
  76. # It'd be nice to use a pattern like "greenlet-%d", but maybe somebody out
  77. # there is checking thread names...
  78. self._name = self._Thread__name = __threading__._newname("DummyThread-%d")
  79. self._set_ident()
  80. g = getcurrent()
  81. gid = _get_ident(g) # same as id(g)
  82. __threading__._active[gid] = self
  83. rawlink = getattr(g, 'rawlink', None)
  84. if rawlink is not None:
  85. # raw greenlet.greenlet greenlets don't
  86. # have rawlink...
  87. rawlink(_cleanup)
  88. else:
  89. # ... so for them we use weakrefs.
  90. # See https://github.com/gevent/gevent/issues/918
  91. global _weakref
  92. if _weakref is None:
  93. _weakref = __import__('weakref')
  94. ref = _weakref.ref(g, _make_cleanup_id(gid))
  95. self.__raw_ref = ref
  96. def _Thread__stop(self):
  97. pass
  98. _stop = _Thread__stop # py3
  99. def _wait_for_tstate_lock(self, *args, **kwargs):
  100. # pylint:disable=arguments-differ
  101. pass
  102. if hasattr(__threading__, 'main_thread'): # py 3.4+
  103. def main_native_thread():
  104. return __threading__.main_thread() # pylint:disable=no-member
  105. else:
  106. _main_threads = [(_k, _v) for _k, _v in __threading__._active.items()
  107. if isinstance(_v, __threading__._MainThread)]
  108. assert len(_main_threads) == 1, "Too many main threads"
  109. def main_native_thread():
  110. return _main_threads[0][1]
  111. # Make sure the MainThread can be found by our current greenlet ID,
  112. # otherwise we get a new DummyThread, which cannot be joined.
  113. # Fixes tests in test_threading_2 under PyPy, and generally makes things nicer
  114. # when gevent.threading is imported before monkey patching or not at all
  115. # XXX: This assumes that the import is happening in the "main" greenlet/thread.
  116. # XXX: We should really only be doing this from gevent.monkey.
  117. if _get_ident() not in __threading__._active:
  118. _v = main_native_thread()
  119. _k = _v.ident
  120. del __threading__._active[_k]
  121. _v._ident = _v._Thread__ident = _get_ident()
  122. __threading__._active[_get_ident()] = _v
  123. del _k
  124. del _v
  125. # Avoid printing an error on shutdown trying to remove the thread entry
  126. # we just replaced if we're not fully monkey patched in
  127. # XXX: This causes a hang on PyPy for some unknown reason (as soon as class _active
  128. # defines __delitem__, shutdown hangs. Maybe due to something with the GC?
  129. # XXX: This may be fixed in 2.6.1+
  130. if not PYPY:
  131. # pylint:disable=no-member
  132. _MAIN_THREAD = __threading__._get_ident() if hasattr(__threading__, '_get_ident') else __threading__.get_ident()
  133. class _active(dict):
  134. def __delitem__(self, k):
  135. if k == _MAIN_THREAD and k not in self:
  136. return
  137. dict.__delitem__(self, k)
  138. __threading__._active = _active(__threading__._active)
  139. import sys
  140. if sys.version_info[:2] >= (3, 4):
  141. # XXX: Issue 18808 breaks us on Python 3.4.
  142. # Thread objects now expect a callback from the interpreter itself
  143. # (threadmodule.c:release_sentinel). Because this never happens
  144. # when a greenlet exits, join() and friends will block forever.
  145. # The solution below involves capturing the greenlet when it is
  146. # started and deferring the known broken methods to it.
  147. class Thread(__threading__.Thread):
  148. _greenlet = None
  149. def is_alive(self):
  150. return bool(self._greenlet)
  151. isAlive = is_alive
  152. def _set_tstate_lock(self):
  153. self._greenlet = getcurrent()
  154. def run(self):
  155. try:
  156. super(Thread, self).run()
  157. finally:
  158. # avoid ref cycles, but keep in __dict__ so we can
  159. # distinguish the started/never-started case
  160. self._greenlet = None
  161. self._stop() # mark as finished
  162. def join(self, timeout=None):
  163. if '_greenlet' not in self.__dict__:
  164. raise RuntimeError("Cannot join an inactive thread")
  165. if self._greenlet is None:
  166. return
  167. self._greenlet.join(timeout=timeout)
  168. def _wait_for_tstate_lock(self, *args, **kwargs):
  169. # pylint:disable=arguments-differ
  170. raise NotImplementedError()
  171. __implements__.append('Thread')
  172. # The main thread is patched up with more care in monkey.py
  173. #t = __threading__.current_thread()
  174. #if isinstance(t, __threading__.Thread):
  175. # t.__class__ = Thread
  176. # t._greenlet = getcurrent()
  177. if sys.version_info[:2] >= (3, 3):
  178. __implements__.remove('_get_ident')
  179. __implements__.append('get_ident')
  180. get_ident = _get_ident
  181. __implements__.remove('_sleep')
  182. # Python 3 changed the implementation of threading.RLock
  183. # Previously it was a factory function around threading._RLock
  184. # which in turn used _allocate_lock. Now, it wants to use
  185. # threading._CRLock, which is imported from _thread.RLock and as such
  186. # is implemented in C. So it bypasses our _allocate_lock function.
  187. # Fortunately they left the Python fallback in place
  188. assert hasattr(__threading__, '_CRLock'), "Unsupported Python version"
  189. _CRLock = None
  190. __implements__.append('_CRLock')