You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
4.5 KiB
Python
133 lines
4.5 KiB
Python
5 years ago
|
# -*- coding: utf-8 -*-
|
||
|
# copyright (c) 2018 gevent. See LICENSE.
|
||
|
# cython: auto_pickle=False,embedsignature=True,always_allow_keywords=False
|
||
|
"""
|
||
|
A collection of primitives used by the hub, and suitable for
|
||
|
compilation with Cython because of their frequency of use.
|
||
|
|
||
|
"""
|
||
|
from __future__ import absolute_import
|
||
|
from __future__ import division
|
||
|
from __future__ import print_function
|
||
|
|
||
|
from weakref import ref as wref
|
||
|
from gc import get_objects
|
||
|
|
||
|
from greenlet import greenlet
|
||
|
|
||
|
from gevent.exceptions import BlockingSwitchOutError
|
||
|
|
||
|
|
||
|
# In Cython, we define these as 'cdef inline' functions. The
|
||
|
# compilation unit cannot have a direct assignment to them (import
|
||
|
# is assignment) without generating a 'lvalue is not valid target'
|
||
|
# error.
|
||
|
locals()['getcurrent'] = __import__('greenlet').getcurrent
|
||
|
locals()['greenlet_init'] = lambda: None
|
||
|
locals()['_greenlet_switch'] = greenlet.switch
|
||
|
|
||
|
|
||
|
__all__ = [
|
||
|
'TrackedRawGreenlet',
|
||
|
'SwitchOutGreenletWithLoop',
|
||
|
]
|
||
|
|
||
|
class TrackedRawGreenlet(greenlet):
|
||
|
|
||
|
def __init__(self, function, parent):
|
||
|
greenlet.__init__(self, function, parent)
|
||
|
# See greenlet.py's Greenlet class. We capture the cheap
|
||
|
# parts to maintain the tree structure, but we do not capture
|
||
|
# the stack because that's too expensive for 'spawn_raw'.
|
||
|
|
||
|
current = getcurrent() # pylint:disable=undefined-variable
|
||
|
self.spawning_greenlet = wref(current)
|
||
|
# See Greenlet for how trees are maintained.
|
||
|
try:
|
||
|
self.spawn_tree_locals = current.spawn_tree_locals
|
||
|
except AttributeError:
|
||
|
self.spawn_tree_locals = {}
|
||
|
if current.parent:
|
||
|
current.spawn_tree_locals = self.spawn_tree_locals
|
||
|
|
||
|
|
||
|
class SwitchOutGreenletWithLoop(TrackedRawGreenlet):
|
||
|
# Subclasses must define:
|
||
|
# - self.loop
|
||
|
|
||
|
# This class defines loop in its .pxd for Cython. This lets us avoid
|
||
|
# circular dependencies with the hub.
|
||
|
|
||
|
def switch(self):
|
||
|
switch_out = getattr(getcurrent(), 'switch_out', None) # pylint:disable=undefined-variable
|
||
|
if switch_out is not None:
|
||
|
switch_out()
|
||
|
return _greenlet_switch(self) # pylint:disable=undefined-variable
|
||
|
|
||
|
def switch_out(self):
|
||
|
raise BlockingSwitchOutError('Impossible to call blocking function in the event loop callback')
|
||
|
|
||
|
|
||
|
def get_reachable_greenlets():
|
||
|
# We compile this loop with Cython so that it's faster, and so that
|
||
|
# the GIL isn't dropped at unpredictable times during the loop.
|
||
|
# Dropping the GIL could lead to accessing partly constructed objects
|
||
|
# in undefined states (particularly, tuples). This helps close a hole
|
||
|
# where a `SystemError: Objects/tupleobject.c bad argument to internal function`
|
||
|
# could get raised. (Note that this probably doesn't completely close the hole,
|
||
|
# if other threads have dropped the GIL, but hopefully the speed makes that
|
||
|
# more rare.) See https://github.com/gevent/gevent/issues/1302
|
||
|
return [
|
||
|
x for x in get_objects()
|
||
|
if isinstance(x, greenlet) and not getattr(x, 'greenlet_tree_is_ignored', False)
|
||
|
]
|
||
|
|
||
|
# Cache the global memoryview so cython can optimize.
|
||
|
_memoryview = memoryview
|
||
|
try:
|
||
|
if isinstance(__builtins__, dict):
|
||
|
# Pure-python mode on CPython
|
||
|
_buffer = __builtins__['buffer']
|
||
|
else:
|
||
|
# Cythonized mode, or PyPy
|
||
|
_buffer = __builtins__.buffer
|
||
|
except (AttributeError, KeyError):
|
||
|
# Python 3.
|
||
|
_buffer = memoryview
|
||
|
|
||
|
def get_memory(data):
|
||
|
# On Python 2, memoryview(memoryview()) can leak in some cases,
|
||
|
# notably when an io.BufferedWriter object produced the memoryview.
|
||
|
# So we need to check to see if we already have one before we convert.
|
||
|
# We do this in Cython to mitigate the performance cost (which turns out to be a
|
||
|
# net win.)
|
||
|
|
||
|
# We don't specifically test for this leak.
|
||
|
|
||
|
# https://github.com/gevent/gevent/issues/1318
|
||
|
try:
|
||
|
mv = _memoryview(data) if not isinstance(data, _memoryview) else data
|
||
|
if mv.shape:
|
||
|
return mv
|
||
|
# No shape, probably working with a ctypes object,
|
||
|
# or something else exotic that supports the buffer interface
|
||
|
return mv.tobytes()
|
||
|
except TypeError:
|
||
|
# fixes "python2.7 array.array doesn't support memoryview used in
|
||
|
# gevent.socket.send" issue
|
||
|
# (http://code.google.com/p/gevent/issues/detail?id=94)
|
||
|
if _buffer is _memoryview:
|
||
|
# Py3
|
||
|
raise
|
||
|
return _buffer(data)
|
||
|
|
||
|
|
||
|
|
||
|
def _init():
|
||
|
greenlet_init() # pylint:disable=undefined-variable
|
||
|
|
||
|
_init()
|
||
|
|
||
|
from gevent._util import import_c_accel
|
||
|
import_c_accel(globals(), 'gevent.__greenlet_primitives')
|