track embedded python distribution
This commit is contained in:
231
python/gevent/threading.py
Normal file
231
python/gevent/threading.py
Normal file
@@ -0,0 +1,231 @@
|
||||
"""
|
||||
Implementation of the standard :mod:`threading` using greenlets.
|
||||
|
||||
.. note::
|
||||
|
||||
This module is a helper for :mod:`gevent.monkey` and is not
|
||||
intended to be used directly. For spawning greenlets in your
|
||||
applications, prefer higher level constructs like
|
||||
:class:`gevent.Greenlet` class or :func:`gevent.spawn`.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
||||
__implements__ = [
|
||||
'local',
|
||||
'_start_new_thread',
|
||||
'_allocate_lock',
|
||||
'Lock',
|
||||
'_get_ident',
|
||||
'_sleep',
|
||||
'_DummyThread',
|
||||
]
|
||||
|
||||
|
||||
import threading as __threading__
|
||||
_DummyThread_ = __threading__._DummyThread
|
||||
from gevent.local import local
|
||||
from gevent.thread import start_new_thread as _start_new_thread, allocate_lock as _allocate_lock, get_ident as _get_ident
|
||||
from gevent._compat import PYPY
|
||||
from gevent.hub import sleep as _sleep, getcurrent
|
||||
|
||||
# Exports, prevent unused import warnings
|
||||
local = local
|
||||
start_new_thread = _start_new_thread
|
||||
allocate_lock = _allocate_lock
|
||||
_get_ident = _get_ident
|
||||
_sleep = _sleep
|
||||
getcurrent = getcurrent
|
||||
|
||||
Lock = _allocate_lock
|
||||
|
||||
|
||||
def _cleanup(g):
|
||||
__threading__._active.pop(id(g), None)
|
||||
|
||||
def _make_cleanup_id(gid):
|
||||
def _(_r):
|
||||
__threading__._active.pop(gid, None)
|
||||
return _
|
||||
|
||||
_weakref = None
|
||||
|
||||
class _DummyThread(_DummyThread_):
|
||||
# We avoid calling the superclass constructor. This makes us about
|
||||
# twice as fast (1.16 vs 0.68usec on PyPy, 29.3 vs 17.7usec on
|
||||
# CPython 2.7), and has the important effect of avoiding
|
||||
# allocation and then immediate deletion of _Thread__block, a
|
||||
# lock. This is especially important on PyPy where locks go
|
||||
# through the cpyext API and Cython, which is known to be slow and
|
||||
# potentially buggy (e.g.,
|
||||
# https://bitbucket.org/pypy/pypy/issues/2149/memory-leak-for-python-subclass-of-cpyext#comment-22347393)
|
||||
|
||||
# These objects are constructed quite frequently in some cases, so
|
||||
# the optimization matters: for example, in gunicorn, which uses
|
||||
# pywsgi.WSGIServer, every request is handled in a new greenlet,
|
||||
# and every request uses a logging.Logger to write the access log,
|
||||
# and every call to a log method captures the current thread (by
|
||||
# default).
|
||||
#
|
||||
# (Obviously we have to duplicate the effects of the constructor,
|
||||
# at least for external state purposes, which is potentially
|
||||
# slightly fragile.)
|
||||
|
||||
# For the same reason, instances of this class will cleanup their own entry
|
||||
# in ``threading._active``
|
||||
|
||||
# Capture the static things as class vars to save on memory/
|
||||
# construction time.
|
||||
# In Py2, they're all private; in Py3, they become protected
|
||||
_Thread__stopped = _is_stopped = _stopped = False
|
||||
_Thread__initialized = _initialized = True
|
||||
_Thread__daemonic = _daemonic = True
|
||||
_Thread__args = _args = ()
|
||||
_Thread__kwargs = _kwargs = None
|
||||
_Thread__target = _target = None
|
||||
_Thread_ident = _ident = None
|
||||
_Thread__started = _started = __threading__.Event()
|
||||
_Thread__started.set()
|
||||
_tstate_lock = None
|
||||
|
||||
def __init__(self):
|
||||
#_DummyThread_.__init__(self) # pylint:disable=super-init-not-called
|
||||
|
||||
# It'd be nice to use a pattern like "greenlet-%d", but maybe somebody out
|
||||
# there is checking thread names...
|
||||
self._name = self._Thread__name = __threading__._newname("DummyThread-%d")
|
||||
self._set_ident()
|
||||
|
||||
g = getcurrent()
|
||||
gid = _get_ident(g) # same as id(g)
|
||||
__threading__._active[gid] = self
|
||||
rawlink = getattr(g, 'rawlink', None)
|
||||
if rawlink is not None:
|
||||
# raw greenlet.greenlet greenlets don't
|
||||
# have rawlink...
|
||||
rawlink(_cleanup)
|
||||
else:
|
||||
# ... so for them we use weakrefs.
|
||||
# See https://github.com/gevent/gevent/issues/918
|
||||
global _weakref
|
||||
if _weakref is None:
|
||||
_weakref = __import__('weakref')
|
||||
ref = _weakref.ref(g, _make_cleanup_id(gid))
|
||||
self.__raw_ref = ref
|
||||
|
||||
def _Thread__stop(self):
|
||||
pass
|
||||
|
||||
_stop = _Thread__stop # py3
|
||||
|
||||
def _wait_for_tstate_lock(self, *args, **kwargs):
|
||||
# pylint:disable=arguments-differ
|
||||
pass
|
||||
|
||||
if hasattr(__threading__, 'main_thread'): # py 3.4+
|
||||
def main_native_thread():
|
||||
return __threading__.main_thread() # pylint:disable=no-member
|
||||
else:
|
||||
_main_threads = [(_k, _v) for _k, _v in __threading__._active.items()
|
||||
if isinstance(_v, __threading__._MainThread)]
|
||||
assert len(_main_threads) == 1, "Too many main threads"
|
||||
|
||||
def main_native_thread():
|
||||
return _main_threads[0][1]
|
||||
|
||||
# Make sure the MainThread can be found by our current greenlet ID,
|
||||
# otherwise we get a new DummyThread, which cannot be joined.
|
||||
# Fixes tests in test_threading_2 under PyPy, and generally makes things nicer
|
||||
# when gevent.threading is imported before monkey patching or not at all
|
||||
# XXX: This assumes that the import is happening in the "main" greenlet/thread.
|
||||
# XXX: We should really only be doing this from gevent.monkey.
|
||||
if _get_ident() not in __threading__._active:
|
||||
_v = main_native_thread()
|
||||
_k = _v.ident
|
||||
del __threading__._active[_k]
|
||||
_v._ident = _v._Thread__ident = _get_ident()
|
||||
__threading__._active[_get_ident()] = _v
|
||||
del _k
|
||||
del _v
|
||||
|
||||
# Avoid printing an error on shutdown trying to remove the thread entry
|
||||
# we just replaced if we're not fully monkey patched in
|
||||
# XXX: This causes a hang on PyPy for some unknown reason (as soon as class _active
|
||||
# defines __delitem__, shutdown hangs. Maybe due to something with the GC?
|
||||
# XXX: This may be fixed in 2.6.1+
|
||||
if not PYPY:
|
||||
# pylint:disable=no-member
|
||||
_MAIN_THREAD = __threading__._get_ident() if hasattr(__threading__, '_get_ident') else __threading__.get_ident()
|
||||
|
||||
class _active(dict):
|
||||
def __delitem__(self, k):
|
||||
if k == _MAIN_THREAD and k not in self:
|
||||
return
|
||||
dict.__delitem__(self, k)
|
||||
|
||||
__threading__._active = _active(__threading__._active)
|
||||
|
||||
|
||||
import sys
|
||||
if sys.version_info[:2] >= (3, 4):
|
||||
# XXX: Issue 18808 breaks us on Python 3.4.
|
||||
# Thread objects now expect a callback from the interpreter itself
|
||||
# (threadmodule.c:release_sentinel). Because this never happens
|
||||
# when a greenlet exits, join() and friends will block forever.
|
||||
# The solution below involves capturing the greenlet when it is
|
||||
# started and deferring the known broken methods to it.
|
||||
|
||||
class Thread(__threading__.Thread):
|
||||
_greenlet = None
|
||||
|
||||
def is_alive(self):
|
||||
return bool(self._greenlet)
|
||||
|
||||
isAlive = is_alive
|
||||
|
||||
def _set_tstate_lock(self):
|
||||
self._greenlet = getcurrent()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
super(Thread, self).run()
|
||||
finally:
|
||||
# avoid ref cycles, but keep in __dict__ so we can
|
||||
# distinguish the started/never-started case
|
||||
self._greenlet = None
|
||||
self._stop() # mark as finished
|
||||
|
||||
def join(self, timeout=None):
|
||||
if '_greenlet' not in self.__dict__:
|
||||
raise RuntimeError("Cannot join an inactive thread")
|
||||
if self._greenlet is None:
|
||||
return
|
||||
self._greenlet.join(timeout=timeout)
|
||||
|
||||
def _wait_for_tstate_lock(self, *args, **kwargs):
|
||||
# pylint:disable=arguments-differ
|
||||
raise NotImplementedError()
|
||||
|
||||
__implements__.append('Thread')
|
||||
|
||||
# The main thread is patched up with more care in monkey.py
|
||||
#t = __threading__.current_thread()
|
||||
#if isinstance(t, __threading__.Thread):
|
||||
# t.__class__ = Thread
|
||||
# t._greenlet = getcurrent()
|
||||
|
||||
if sys.version_info[:2] >= (3, 3):
|
||||
__implements__.remove('_get_ident')
|
||||
__implements__.append('get_ident')
|
||||
get_ident = _get_ident
|
||||
__implements__.remove('_sleep')
|
||||
|
||||
# Python 3 changed the implementation of threading.RLock
|
||||
# Previously it was a factory function around threading._RLock
|
||||
# which in turn used _allocate_lock. Now, it wants to use
|
||||
# threading._CRLock, which is imported from _thread.RLock and as such
|
||||
# is implemented in C. So it bypasses our _allocate_lock function.
|
||||
# Fortunately they left the Python fallback in place
|
||||
assert hasattr(__threading__, '_CRLock'), "Unsupported Python version"
|
||||
_CRLock = None
|
||||
__implements__.append('_CRLock')
|
||||
Reference in New Issue
Block a user