Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
Toggle table of contents sidebar

# Source code for gevent._hub_local

    # -*- coding: utf-8 -*-
    # copyright 2018 gevent. See LICENSE
    """
    Maintains the thread local hub.

    """
    from __future__ import absolute_import
    from __future__ import division
    from __future__ import print_function

    import _thread

    __all__ = [
        'get_hub',
        'get_hub_noargs',
        'get_hub_if_exists',
    ]

    # These must be the "real" native thread versions,
    # not monkey-patched.
    # We are imported early enough (by gevent/__init__) that
    # we can rely on not being monkey-patched in any way yet.
    assert 'gevent' not in str(_thread._local)
    class _Threadlocal(_thread._local):

        def __init__(self):
            # Use a class with an initializer so that we can test
            # for 'is None' instead of catching AttributeError, making
            # the code cleaner and possibly solving some corner cases
            # (like #687).
            #
            # However, under some weird circumstances, it _seems_ like the
            # __init__ method doesn't get called properly ("seems" is the
            # keyword). We've seen at least one instance
            # (https://github.com/gevent/gevent/issues/1961) of
            # ``AttributeError: '_Threadlocal' object has no attribute # 'hub'``
            # which should be impossible unless:
            #
            # - Someone manually deletes the attribute
            # - The _threadlocal object itself is in the process of being
            #   deleted. The C ``tp_clear`` slot for it deletes the ``__dict__``
            #   of each instance in each thread (and/or the ``tp_clear`` of ``dict`` itself
            #   clears the instance). Now, how we could be getting
            #   cleared while still being used is unclear, but clearing is part of
            #   circular garbage collection, and in the bug report it looks like we're inside a
            #   weakref finalizer or ``__del__`` method, which could suggest that
            #   garbage collection is happening.
            #
            # See https://github.com/gevent/gevent/issues/1961
            # and ``get_hub_if_exists()``
            super(_Threadlocal, self).__init__()
            self.Hub = None
            self.loop = None
            self.hub = None

    _threadlocal = _Threadlocal()

    Hub = None # Set when gevent.hub is imported

    def get_hub_class():
        """Return the type of hub to use for the current thread.

        If there's no type of hub for the current thread yet, 'gevent.hub.Hub' is used.
        """
        hubtype = _threadlocal.Hub
        if hubtype is None:
            hubtype = _threadlocal.Hub = Hub
        return hubtype

    def set_default_hub_class(hubtype):
        global Hub
        Hub = hubtype

    def get_hub():
        """
        Return the hub for the current thread.

        If a hub does not exist in the current thread, a new one is
        created of the type returned by :func:`get_hub_class`.

        .. deprecated:: 1.3b1
           The ``*args`` and ``**kwargs`` arguments are deprecated. They were
           only used when the hub was created, and so were non-deterministic---to be
           sure they were used, *all* callers had to pass them, or they were order-dependent.
           Use ``set_hub`` instead.

        .. versionchanged:: 1.5a3
           The *args* and *kwargs* arguments are now completely ignored.

        .. versionchanged:: 23.7.0
           The long-deprecated ``args`` and ``kwargs`` parameters are no
           longer accepted.
        """
        # See get_hub_if_exists
        try:
            hub = _threadlocal.hub
        except AttributeError:
            hub = None
        if hub is None:
            hubtype = get_hub_class()
            hub = _threadlocal.hub = hubtype()
        return hub

    # For Cython purposes, we need to duplicate get_hub into this function so it
    # can be directly called.
    def get_hub_noargs():
        # See get_hub_if_exists
        try:
            hub = _threadlocal.hub
        except AttributeError:
            hub = None
        if hub is None:
            hubtype = get_hub_class()
            hub = _threadlocal.hub = hubtype()
        return hub

    def get_hub_if_exists():
        """
        Return the hub for the current thread.

        Return ``None`` if no hub has been created yet.
        """
        # Attempt a band-aid for the poorly-understood behaviour
        # seen in https://github.com/gevent/gevent/issues/1961
        # where the ``hub`` attribute has gone missing.
        try:
            return _threadlocal.hub
        except AttributeError:
            # XXX: I'd really like to report this, but I'm not sure how
            # that can be done safely (because I don't know how we get
            # here in the first place). We may be in a place where imports
            # are unsafe, or the interpreter is shutting down, or the
            # thread is exiting, or...
            return None

    def set_hub(hub):
        _threadlocal.hub = hub

    def get_loop():
        return _threadlocal.loop

    def set_loop(loop):
        _threadlocal.loop = loop

    from gevent._util import import_c_accel
    import_c_accel(globals(), 'gevent.__hub_local')