has_gevent = True
use_gevent = False
try:
    import gevent

    def enable_gevent():
        global use_gevent
        use_gevent = True

    def _disable_gevent():  # for testing
        global use_gevent
        use_gevent = False

    def is_gevent_enabled():
        global use_gevent
        return use_gevent
except ImportError:
    has_gevent = False

    def enable_gevent():
        pass

    def _disable_gevent():
        pass

    def is_gevent_enabled():
        return False


if has_gevent:
    from gevent.monkey import get_original as _get_original
    ThreadLock = _get_original('threading', 'Lock')
    ThreadRLock = _get_original('threading', 'RLock')
    try:
        thread_get_ident = _get_original('threading', 'get_ident')
    except AttributeError:
        # In 2.7, this is called _get_ident
        thread_get_ident = _get_original('threading', '_get_ident')
    thread_local = _get_original('threading', 'local')

    from gevent.thread import get_ident as greenlet_get_ident
    from gevent.local import local as greenlet_local
    from gevent.lock import BoundedSemaphore
    from gevent.threading import __threading__

    def thread_get_name():
        return __threading__.currentThread().getName()

    class GreenletRLock(object):
        def __init__(self):
            self._thread_local = thread_local()
            self._owner = None
            self._wait_queue = []
            self._count = 0

        def __repr__(self):
            owner = self._owner
            return "<%s owner=%r count=%d>" % (self.__class__.__name__, owner,
                                               self._count)

        def acquire(self, blocking=1):
            tid = thread_get_ident()
            gid = greenlet_get_ident()
            tid_gid = (tid, gid)

            # We trust the GIL here so we can do this comparison w/o locking.
            if tid_gid == self._owner:
                self._count += 1
                return True

            greenlet_lock = self._get_greenlet_lock()

            self._wait_queue.append(gid)
            # this is a safety in case an exception is raised somewhere
            # and we must make sure we're not in the queue
            # otherwise it'll get stuck forever.
            remove_from_queue_on_return = True
            try:
                while True:
                    if not greenlet_lock.acquire(blocking):
                        return False  # non-blocking and failed to acquire lock

                    if self._wait_queue[0] == gid:
                        # Hurray, we can have the lock.
                        self._owner = tid_gid
                        self._count = 1

                        # don't remove us from the queue
                        remove_from_queue_on_return = False
                        return True
                    else:
                        # we already hold the greenlet lock so obviously
                        # the owner is not in our thread.
                        greenlet_lock.release()
                        if blocking:
                            # 500 us -> initial delay of 1 ms
                            gevent.sleep(0.0005)
                        else:
                            return False
            finally:
                if remove_from_queue_on_return:
                    self._wait_queue.remove(gid)

        def release(self):
            tid_gid = (thread_get_ident(), greenlet_get_ident())
            if tid_gid != self._owner:
                raise RuntimeError("cannot release un-acquired lock")

            self._count -= 1
            if not self._count:
                self._owner = None
                gid = self._wait_queue.pop(0)
                assert gid == tid_gid[1]
                self._thread_local.greenlet_lock.release()

        __enter__ = acquire

        def __exit__(self, t, v, tb):
            self.release()

        def _get_greenlet_lock(self):
            if not hasattr(self._thread_local, 'greenlet_lock'):
                greenlet_lock = self._thread_local.greenlet_lock = BoundedSemaphore(1)
            else:
                greenlet_lock = self._thread_local.greenlet_lock
            return greenlet_lock

        def _is_owned(self):
            return self._owner == (thread_get_ident(), greenlet_get_ident())
else:
    from threading import (
        Lock as ThreadLock, RLock as ThreadRLock, currentThread)
    try:
        from thread import (
            get_ident as thread_get_ident, _local as thread_local)
    except ImportError:
        from _thread import (
            get_ident as thread_get_ident, _local as thread_local)

    def thread_get_name():
        return currentThread().getName()

    greenlet_get_ident = thread_get_ident

    greenlet_local = thread_local

    class GreenletRLock(object):
        def acquire(self):
            pass

        def release(self):
            pass

        def __enter__(self):
            pass

        def __exit__(self, t, v, tb):
            pass


def new_fine_grained_lock():
    global use_gevent
    if use_gevent:
        return GreenletRLock()
    else:
        return ThreadRLock()


has_contextvars = True
try:
    import contextvars
except ImportError:
    has_contextvars = False

if has_contextvars:
    from contextvars import ContextVar
    from itertools import count

    context_ident_counter = count()
    context_ident = ContextVar('context_ident')

    def context_get_ident():
        try:
            return context_ident.get()
        except LookupError:
            ident = 'context-%s' % next(context_ident_counter)
            context_ident.set(ident)
            return ident

    def is_context_enabled():
        try:
            context_ident.get()
            return True
        except LookupError:
            return False

else:
    class ContextVar(object):
        def __init__(self, name):
            self.name = name
            self.local = thread_local()

        def set(self, value):
            self.local = value

        def get(self, default=None):
            if self.local is None:
                return default

            return default

    def context_get_ident():
        return 1

    def is_context_enabled():
        return False
