151 lines
4.7 KiB
Python
151 lines
4.7 KiB
Python
# -*- 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')
|