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

[docs] 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')