# gevent

# Source code for gevent.threading

    """
    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`. Attributes
        in this module like ``__threading__`` are implementation artifacts subject
        to change at any time.

    .. versionchanged:: 1.2.3

       Defer adjusting the stdlib's list of active threads until we are
       monkey patched. Previously this was done at import time. We are
       documented to only be used as a helper for monkey patching, so this should
       functionally be the same, but some applications ignore the documentation and
       directly import this module anyway.

       A positive consequence is that ``import gevent.threading,
       threading; threading.current_thread()`` will no longer return a DummyThread
       before monkey-patching.
    """
    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.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(_get_ident(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``

        # This class also solves a problem forking process with subprocess: after forking,
        # Thread.__stop is called, which throws an exception when __block doesn't
        # exist.

        # 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): # pylint:disable=super-init-not-called
            #_DummyThread_.__init__(self)

            # 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")
            # All dummy threads in the same native thread share the same ident
            # (that of the native thread)
            self._set_ident()

            g = getcurrent()
            gid = _get_ident(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:
        def main_native_thread():
            main_threads = [v for v in __threading__._active.values()
                            if isinstance(v, __threading__._MainThread)]
            assert len(main_threads) == 1, "Too many main threads"

            return main_threads[0]

    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')

        class Timer(Thread, __threading__.Timer): # pylint:disable=abstract-method,inherit-non-class
            pass

        __implements__.append('Timer')

        # The main thread is patched up with more care
        # in _gevent_will_monkey_patch

    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')

    def _gevent_will_monkey_patch(native_module, items, warn): # pylint:disable=unused-argument
        # 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.
        main_thread = main_native_thread()
        if __threading__.current_thread() != main_thread:
            warn("Monkey-patching outside the main native thread. Some APIs "
                 "will not be available. Expect a KeyError to be printed at shutdown.")
            return

        if _get_ident() not in __threading__._active:
            main_id = main_thread.ident
            del __threading__._active[main_id]
            main_thread._ident = main_thread._Thread__ident = _get_ident()
            __threading__._active[_get_ident()] = main_thread