aboutsummaryrefslogtreecommitdiffstats
path: root/python/gevent/greenlet.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/gevent/greenlet.py')
-rw-r--r--python/gevent/greenlet.py944
1 files changed, 0 insertions, 944 deletions
diff --git a/python/gevent/greenlet.py b/python/gevent/greenlet.py
deleted file mode 100644
index 1d37034..0000000
--- a/python/gevent/greenlet.py
+++ /dev/null
@@ -1,944 +0,0 @@
-# Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details.
-# cython: auto_pickle=False,embedsignature=True,always_allow_keywords=False
-
-from __future__ import absolute_import, print_function, division
-
-from sys import _getframe as sys_getframe
-from sys import exc_info as sys_exc_info
-from weakref import ref as wref
-
-# XXX: How to get cython to let us rename this as RawGreenlet
-# like we prefer?
-from greenlet import greenlet
-from greenlet import GreenletExit
-
-from gevent._compat import reraise
-from gevent._compat import PYPY as _PYPY
-from gevent._tblib import dump_traceback
-from gevent._tblib import load_traceback
-
-from gevent.exceptions import InvalidSwitchError
-
-from gevent._hub_primitives import iwait_on_objects as iwait
-from gevent._hub_primitives import wait_on_objects as wait
-
-from gevent.timeout import Timeout
-
-from gevent._config import config as GEVENT_CONFIG
-from gevent._util import Lazy
-from gevent._util import readproperty
-from gevent._hub_local import get_hub_noargs as get_hub
-from gevent import _waiter
-
-
-__all__ = [
- 'Greenlet',
- 'joinall',
- 'killall',
-]
-
-
-# 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()['Waiter'] = _waiter.Waiter
-
-
-if _PYPY:
- import _continuation # pylint:disable=import-error
- _continulet = _continuation.continulet
-
-
-class SpawnedLink(object):
- """
- A wrapper around link that calls it in another greenlet.
-
- Can be called only from main loop.
- """
- __slots__ = ['callback']
-
- def __init__(self, callback):
- if not callable(callback):
- raise TypeError("Expected callable: %r" % (callback, ))
- self.callback = callback
-
- def __call__(self, source):
- g = greenlet(self.callback, get_hub())
- g.switch(source)
-
- def __hash__(self):
- return hash(self.callback)
-
- def __eq__(self, other):
- return self.callback == getattr(other, 'callback', other)
-
- def __str__(self):
- return str(self.callback)
-
- def __repr__(self):
- return repr(self.callback)
-
- def __getattr__(self, item):
- assert item != 'callback'
- return getattr(self.callback, item)
-
-
-class SuccessSpawnedLink(SpawnedLink):
- """A wrapper around link that calls it in another greenlet only if source succeed.
-
- Can be called only from main loop.
- """
- __slots__ = []
-
- def __call__(self, source):
- if source.successful():
- return SpawnedLink.__call__(self, source)
-
-
-class FailureSpawnedLink(SpawnedLink):
- """A wrapper around link that calls it in another greenlet only if source failed.
-
- Can be called only from main loop.
- """
- __slots__ = []
-
- def __call__(self, source):
- if not source.successful():
- return SpawnedLink.__call__(self, source)
-
-class _Frame(object):
-
- __slots__ = ('f_code', 'f_lineno', 'f_back')
-
- def __init__(self, f_code, f_lineno, f_back):
- self.f_code = f_code
- self.f_lineno = f_lineno
- self.f_back = f_back
-
- @property
- def f_globals(self):
- return None
-
-def _Frame_from_list(frames):
- previous = None
- for frame in reversed(frames):
- f = _Frame(frame[0], frame[1], previous)
- previous = f
- return previous
-
-def _extract_stack(limit):
- try:
- frame = sys_getframe()
- except ValueError:
- # In certain embedded cases that directly use the Python C api
- # to call Greenlet.spawn (e.g., uwsgi) this can raise
- # `ValueError: call stack is not deep enough`. This is because
- # the Cython stack frames for Greenlet.spawn ->
- # Greenlet.__init__ -> _extract_stack are all on the C level,
- # not the Python level.
- # See https://github.com/gevent/gevent/issues/1212
- frame = None
-
- frames = []
-
- while limit and frame is not None:
- limit -= 1
- frames.append((frame.f_code, frame.f_lineno))
- frame = frame.f_back
-
- return frames
-
-
-_greenlet__init__ = greenlet.__init__
-
-class Greenlet(greenlet):
- """
- A light-weight cooperatively-scheduled execution unit.
- """
- # pylint:disable=too-many-public-methods,too-many-instance-attributes
-
- spawning_stack_limit = 10
-
- # pylint:disable=keyword-arg-before-vararg,super-init-not-called
- def __init__(self, run=None, *args, **kwargs):
- """
- :param args: The arguments passed to the ``run`` function.
- :param kwargs: The keyword arguments passed to the ``run`` function.
- :keyword callable run: The callable object to run. If not given, this object's
- `_run` method will be invoked (typically defined by subclasses).
-
- .. versionchanged:: 1.1b1
- The ``run`` argument to the constructor is now verified to be a callable
- object. Previously, passing a non-callable object would fail after the greenlet
- was spawned.
-
- .. versionchanged:: 1.3b1
- The ``GEVENT_TRACK_GREENLET_TREE`` configuration value may be set to
- a false value to disable ``spawn_tree_locals``, ``spawning_greenlet``,
- and ``spawning_stack``. The first two will be None in that case, and the
- latter will be empty.
- """
- # The attributes are documented in the .rst file
-
- # greenlet.greenlet(run=None, parent=None)
- # Calling it with both positional arguments instead of a keyword
- # argument (parent=get_hub()) speeds up creation of this object ~30%:
- # python -m timeit -s 'import gevent' 'gevent.Greenlet()'
- # Python 3.5: 2.70usec with keywords vs 1.94usec with positional
- # Python 3.4: 2.32usec with keywords vs 1.74usec with positional
- # Python 3.3: 2.55usec with keywords vs 1.92usec with positional
- # Python 2.7: 1.73usec with keywords vs 1.40usec with positional
-
- # Timings taken Feb 21 2018 prior to integration of #755
- # python -m perf timeit -s 'import gevent' 'gevent.Greenlet()'
- # 3.6.4 : Mean +- std dev: 1.08 us +- 0.05 us
- # 2.7.14 : Mean +- std dev: 1.44 us +- 0.06 us
- # PyPy2 5.10.0: Mean +- std dev: 2.14 ns +- 0.08 ns
-
- # After the integration of spawning_stack, spawning_greenlet,
- # and spawn_tree_locals on that same date:
- # 3.6.4 : Mean +- std dev: 8.92 us +- 0.36 us -> 8.2x
- # 2.7.14 : Mean +- std dev: 14.8 us +- 0.5 us -> 10.2x
- # PyPy2 5.10.0: Mean +- std dev: 3.24 us +- 0.17 us -> 1.5x
-
- # Compiling with Cython gets us to these numbers:
- # 3.6.4 : Mean +- std dev: 3.63 us +- 0.14 us
- # 2.7.14 : Mean +- std dev: 3.37 us +- 0.20 us
- # PyPy2 5.10.0 : Mean +- std dev: 4.44 us +- 0.28 us
-
-
- _greenlet__init__(self, None, get_hub())
-
- if run is not None:
- self._run = run
-
- # If they didn't pass a callable at all, then they must
- # already have one. Note that subclassing to override the run() method
- # itself has never been documented or supported.
- if not callable(self._run):
- raise TypeError("The run argument or self._run must be callable")
-
- self.args = args
- self.kwargs = kwargs
- self.value = None
-
- #: An event, such as a timer or a callback that fires. It is established in
- #: start() and start_later() as those two objects, respectively.
- #: Once this becomes non-None, the Greenlet cannot be started again. Conversely,
- #: kill() and throw() check for non-None to determine if this object has ever been
- #: scheduled for starting. A placeholder _dummy_event is assigned by them to prevent
- #: the greenlet from being started in the future, if necessary.
- self._start_event = None
-
- self._notifier = None
- self._formatted_info = None
- self._links = []
- self._ident = None
-
- # Initial state: None.
- # Completed successfully: (None, None, None)
- # Failed with exception: (t, v, dump_traceback(tb)))
- self._exc_info = None
-
- if GEVENT_CONFIG.track_greenlet_tree:
- spawner = getcurrent() # pylint:disable=undefined-variable
- self.spawning_greenlet = wref(spawner)
- try:
- self.spawn_tree_locals = spawner.spawn_tree_locals
- except AttributeError:
- self.spawn_tree_locals = {}
- if spawner.parent is not None:
- # The main greenlet has no parent.
- # Its children get separate locals.
- spawner.spawn_tree_locals = self.spawn_tree_locals
-
- self._spawning_stack_frames = _extract_stack(self.spawning_stack_limit)
- self._spawning_stack_frames.extend(getattr(spawner, '_spawning_stack_frames', []))
- else:
- # None is the default for all of these in Cython, but we
- # need to declare them for pure-Python mode.
- self.spawning_greenlet = None
- self.spawn_tree_locals = None
- self._spawning_stack_frames = None
-
- @Lazy
- def spawning_stack(self):
- # Store this in the __dict__. We don't use it from the C
- # code. It's tempting to discard _spawning_stack_frames
- # after this, but child greenlets may still be created
- # that need it.
- return _Frame_from_list(self._spawning_stack_frames or [])
-
- def _get_minimal_ident(self):
- reg = self.parent.ident_registry
- return reg.get_ident(self)
-
- @property
- def minimal_ident(self):
- """
- A small, unique integer that identifies this object.
-
- This is similar to :attr:`threading.Thread.ident` (and `id`)
- in that as long as this object is alive, no other greenlet *in
- this hub* will have the same id, but it makes a stronger
- guarantee that the assigned values will be small and
- sequential. Sometime after this object has died, the value
- will be available for reuse.
-
- To get ids that are unique across all hubs, combine this with
- the hub's ``minimal_ident``.
-
- .. versionadded:: 1.3a2
- """
- if self._ident is None:
- self._ident = self._get_minimal_ident()
- return self._ident
-
- @readproperty
- def name(self):
- """
- The greenlet name. By default, a unique name is constructed using
- the :attr:`minimal_ident`. You can assign a string to this
- value to change it. It is shown in the `repr` of this object.
-
- .. versionadded:: 1.3a2
- """
- return 'Greenlet-%d' % (self.minimal_ident)
-
- def _raise_exception(self):
- reraise(*self.exc_info)
-
- @property
- def loop(self):
- # needed by killall
- return self.parent.loop
-
- def __nonzero__(self):
- return self._start_event is not None and self._exc_info is None
- try:
- __bool__ = __nonzero__ # Python 3
- except NameError: # pragma: no cover
- # When we're compiled with Cython, the __nonzero__ function
- # goes directly into the slot and can't be accessed by name.
- pass
-
- ### Lifecycle
-
- if _PYPY:
- # oops - pypy's .dead relies on __nonzero__ which we overriden above
- @property
- def dead(self):
- "Boolean indicating that the greenlet is dead and will not run again."
- if self._greenlet__main:
- return False
- if self.__start_cancelled_by_kill() or self.__started_but_aborted():
- return True
-
- return self._greenlet__started and not _continulet.is_pending(self)
- else:
- @property
- def dead(self):
- "Boolean indicating that the greenlet is dead and will not run again."
- return self.__start_cancelled_by_kill() or self.__started_but_aborted() or greenlet.dead.__get__(self)
-
- def __never_started_or_killed(self):
- return self._start_event is None
-
- def __start_pending(self):
- return (self._start_event is not None
- and (self._start_event.pending or getattr(self._start_event, 'active', False)))
-
- def __start_cancelled_by_kill(self):
- return self._start_event is _cancelled_start_event
-
- def __start_completed(self):
- return self._start_event is _start_completed_event
-
- def __started_but_aborted(self):
- return (not self.__never_started_or_killed() # we have been started or killed
- and not self.__start_cancelled_by_kill() # we weren't killed, so we must have been started
- and not self.__start_completed() # the start never completed
- and not self.__start_pending()) # and we're not pending, so we must have been aborted
-
- def __cancel_start(self):
- if self._start_event is None:
- # prevent self from ever being started in the future
- self._start_event = _cancelled_start_event
- # cancel any pending start event
- # NOTE: If this was a real pending start event, this will leave a
- # "dangling" callback/timer object in the hub.loop.callbacks list;
- # depending on where we are in the event loop, it may even be in a local
- # variable copy of that list (in _run_callbacks). This isn't a problem,
- # except for the leak-tests.
- self._start_event.stop()
- self._start_event.close()
-
- def __handle_death_before_start(self, args):
- # args is (t, v, tb) or simply t or v
- if self._exc_info is None and self.dead:
- # the greenlet was never switched to before and it will never be, _report_error was not called
- # the result was not set and the links weren't notified. let's do it here.
- # checking that self.dead is true is essential, because throw() does not necessarily kill the greenlet
- # (if the exception raised by throw() is caught somewhere inside the greenlet).
- if len(args) == 1:
- arg = args[0]
- #if isinstance(arg, type):
- if type(arg) is type(Exception):
- args = (arg, arg(), None)
- else:
- args = (type(arg), arg, None)
- elif not args:
- args = (GreenletExit, GreenletExit(), None)
- self._report_error(args)
-
- @property
- def started(self):
- # DEPRECATED
- return bool(self)
-
- def ready(self):
- """
- Return a true value if and only if the greenlet has finished
- execution.
-
- .. versionchanged:: 1.1
- This function is only guaranteed to return true or false *values*, not
- necessarily the literal constants ``True`` or ``False``.
- """
- return self.dead or self._exc_info is not None
-
- def successful(self):
- """
- Return a true value if and only if the greenlet has finished execution
- successfully, that is, without raising an error.
-
- .. tip:: A greenlet that has been killed with the default
- :class:`GreenletExit` exception is considered successful.
- That is, ``GreenletExit`` is not considered an error.
-
- .. note:: This function is only guaranteed to return true or false *values*,
- not necessarily the literal constants ``True`` or ``False``.
- """
- return self._exc_info is not None and self._exc_info[1] is None
-
- def __repr__(self):
- classname = self.__class__.__name__
- result = '<%s "%s" at %s' % (classname, self.name, hex(id(self)))
- formatted = self._formatinfo()
- if formatted:
- result += ': ' + formatted
- return result + '>'
-
-
- def _formatinfo(self):
- info = self._formatted_info
- if info is not None:
- return info
-
- # Are we running an arbitrary function provided to the constructor,
- # or did a subclass override _run?
- func = self._run
- im_self = getattr(func, '__self__', None)
- if im_self is self:
- funcname = '_run'
- elif im_self is not None:
- funcname = repr(func)
- else:
- funcname = getattr(func, '__name__', '') or repr(func)
-
- result = funcname
- args = []
- if self.args:
- args = [repr(x)[:50] for x in self.args]
- if self.kwargs:
- args.extend(['%s=%s' % (key, repr(value)[:50]) for (key, value) in self.kwargs.items()])
- if args:
- result += '(' + ', '.join(args) + ')'
- # it is important to save the result here, because once the greenlet exits '_run' attribute will be removed
- self._formatted_info = result
- return result
-
- @property
- def exception(self):
- """
- Holds the exception instance raised by the function if the
- greenlet has finished with an error. Otherwise ``None``.
- """
- return self._exc_info[1] if self._exc_info is not None else None
-
- @property
- def exc_info(self):
- """
- Holds the exc_info three-tuple raised by the function if the
- greenlet finished with an error. Otherwise a false value.
-
- .. note:: This is a provisional API and may change.
-
- .. versionadded:: 1.1
- """
- ei = self._exc_info
- if ei is not None and ei[0] is not None:
- return (ei[0], ei[1], load_traceback(ei[2]))
-
- def throw(self, *args):
- """Immediately switch into the greenlet and raise an exception in it.
-
- Should only be called from the HUB, otherwise the current greenlet is left unscheduled forever.
- To raise an exception in a safe manner from any greenlet, use :meth:`kill`.
-
- If a greenlet was started but never switched to yet, then also
- a) cancel the event that will start it
- b) fire the notifications as if an exception was raised in a greenlet
- """
- self.__cancel_start()
-
- try:
- if not self.dead:
- # Prevent switching into a greenlet *at all* if we had never
- # started it. Usually this is the same thing that happens by throwing,
- # but if this is done from the hub with nothing else running, prevents a
- # LoopExit.
- greenlet.throw(self, *args)
- finally:
- self.__handle_death_before_start(args)
-
- def start(self):
- """Schedule the greenlet to run in this loop iteration"""
- if self._start_event is None:
- self._start_event = self.parent.loop.run_callback(self.switch)
-
- def start_later(self, seconds):
- """
- start_later(seconds) -> None
-
- Schedule the greenlet to run in the future loop iteration
- *seconds* later
- """
- if self._start_event is None:
- self._start_event = self.parent.loop.timer(seconds)
- self._start_event.start(self.switch)
-
- @classmethod
- def spawn(cls, *args, **kwargs):
- """
- spawn(function, *args, **kwargs) -> Greenlet
-
- Create a new :class:`Greenlet` object and schedule it to run ``function(*args, **kwargs)``.
- This can be used as ``gevent.spawn`` or ``Greenlet.spawn``.
-
- The arguments are passed to :meth:`Greenlet.__init__`.
-
- .. versionchanged:: 1.1b1
- If a *function* is given that is not callable, immediately raise a :exc:`TypeError`
- instead of spawning a greenlet that will raise an uncaught TypeError.
- """
- g = cls(*args, **kwargs)
- g.start()
- return g
-
- @classmethod
- def spawn_later(cls, seconds, *args, **kwargs):
- """
- spawn_later(seconds, function, *args, **kwargs) -> Greenlet
-
- Create and return a new `Greenlet` object scheduled to run ``function(*args, **kwargs)``
- in a future loop iteration *seconds* later. This can be used as ``Greenlet.spawn_later``
- or ``gevent.spawn_later``.
-
- The arguments are passed to :meth:`Greenlet.__init__`.
-
- .. versionchanged:: 1.1b1
- If an argument that's meant to be a function (the first argument in *args*, or the ``run`` keyword )
- is given to this classmethod (and not a classmethod of a subclass),
- it is verified to be callable. Previously, the spawned greenlet would have failed
- when it started running.
- """
- if cls is Greenlet and not args and 'run' not in kwargs:
- raise TypeError("")
- g = cls(*args, **kwargs)
- g.start_later(seconds)
- return g
-
- def kill(self, exception=GreenletExit, block=True, timeout=None):
- """
- Raise the ``exception`` in the greenlet.
-
- If ``block`` is ``True`` (the default), wait until the greenlet dies or the optional timeout expires.
- If block is ``False``, the current greenlet is not unscheduled.
-
- The function always returns ``None`` and never raises an error.
-
- .. note::
-
- Depending on what this greenlet is executing and the state
- of the event loop, the exception may or may not be raised
- immediately when this greenlet resumes execution. It may
- be raised on a subsequent green call, or, if this greenlet
- exits before making such a call, it may not be raised at
- all. As of 1.1, an example where the exception is raised
- later is if this greenlet had called :func:`sleep(0)
- <gevent.sleep>`; an example where the exception is raised
- immediately is if this greenlet had called
- :func:`sleep(0.1) <gevent.sleep>`.
-
- .. caution::
-
- Use care when killing greenlets. If the code executing is not
- exception safe (e.g., makes proper use of ``finally``) then an
- unexpected exception could result in corrupted state.
-
- See also :func:`gevent.kill`.
-
- :keyword type exception: The type of exception to raise in the greenlet. The default
- is :class:`GreenletExit`, which indicates a :meth:`successful` completion
- of the greenlet.
-
- .. versionchanged:: 0.13.0
- *block* is now ``True`` by default.
- .. versionchanged:: 1.1a2
- If this greenlet had never been switched to, killing it will prevent it from ever being switched to.
- """
- self.__cancel_start()
-
- if self.dead:
- self.__handle_death_before_start((exception,))
- else:
- waiter = Waiter() if block else None # pylint:disable=undefined-variable
- self.parent.loop.run_callback(_kill, self, exception, waiter)
- if block:
- waiter.get()
- self.join(timeout)
- # it should be OK to use kill() in finally or kill a greenlet from more than one place;
- # thus it should not raise when the greenlet is already killed (= not started)
-
- def get(self, block=True, timeout=None):
- """
- get(block=True, timeout=None) -> object
-
- Return the result the greenlet has returned or re-raise the
- exception it has raised.
-
- If block is ``False``, raise :class:`gevent.Timeout` if the
- greenlet is still alive. If block is ``True``, unschedule the
- current greenlet until the result is available or the timeout
- expires. In the latter case, :class:`gevent.Timeout` is
- raised.
- """
- if self.ready():
- if self.successful():
- return self.value
- self._raise_exception()
- if not block:
- raise Timeout()
-
- switch = getcurrent().switch # pylint:disable=undefined-variable
- self.rawlink(switch)
- try:
- t = Timeout._start_new_or_dummy(timeout)
- try:
- result = self.parent.switch()
- if result is not self:
- raise InvalidSwitchError('Invalid switch into Greenlet.get(): %r' % (result, ))
- finally:
- t.cancel()
- except:
- # unlinking in 'except' instead of finally is an optimization:
- # if switch occurred normally then link was already removed in _notify_links
- # and there's no need to touch the links set.
- # Note, however, that if "Invalid switch" assert was removed and invalid switch
- # did happen, the link would remain, causing another invalid switch later in this greenlet.
- self.unlink(switch)
- raise
-
- if self.ready():
- if self.successful():
- return self.value
- self._raise_exception()
-
- def join(self, timeout=None):
- """
- join(timeout=None) -> None
-
- Wait until the greenlet finishes or *timeout* expires. Return
- ``None`` regardless.
- """
- if self.ready():
- return
-
- switch = getcurrent().switch # pylint:disable=undefined-variable
- self.rawlink(switch)
- try:
- t = Timeout._start_new_or_dummy(timeout)
- try:
- result = self.parent.switch()
- if result is not self:
- raise InvalidSwitchError('Invalid switch into Greenlet.join(): %r' % (result, ))
- finally:
- t.cancel()
- except Timeout as ex:
- self.unlink(switch)
- if ex is not t:
- raise
- except:
- self.unlink(switch)
- raise
-
- def _report_result(self, result):
- self._exc_info = (None, None, None)
- self.value = result
- if self._links and not self._notifier:
- self._notifier = self.parent.loop.run_callback(self._notify_links)
-
- def _report_error(self, exc_info):
- if isinstance(exc_info[1], GreenletExit):
- self._report_result(exc_info[1])
- return
-
- self._exc_info = exc_info[0], exc_info[1], dump_traceback(exc_info[2])
-
- if self._links and not self._notifier:
- self._notifier = self.parent.loop.run_callback(self._notify_links)
-
- try:
- self.parent.handle_error(self, *exc_info)
- finally:
- del exc_info
-
- def run(self):
- try:
- self.__cancel_start()
- self._start_event = _start_completed_event
-
- try:
- result = self._run(*self.args, **self.kwargs)
- except: # pylint:disable=bare-except
- self._report_error(sys_exc_info())
- return
- self._report_result(result)
- finally:
- self.__dict__.pop('_run', None)
- self.args = ()
- self.kwargs.clear()
-
- def _run(self):
- """
- Subclasses may override this method to take any number of
- arguments and keyword arguments.
-
- .. versionadded:: 1.1a3
- Previously, if no callable object was
- passed to the constructor, the spawned greenlet would later
- fail with an AttributeError.
- """
- # We usually override this in __init__
- # pylint: disable=method-hidden
- return
-
- def has_links(self):
- return len(self._links)
-
- def rawlink(self, callback):
- """
- Register a callable to be executed when the greenlet finishes
- execution.
-
- The *callback* will be called with this instance as an
- argument.
-
- .. caution:: The callable will be called in the HUB greenlet.
- """
- if not callable(callback):
- raise TypeError('Expected callable: %r' % (callback, ))
- self._links.append(callback) # pylint:disable=no-member
- if self.ready() and self._links and not self._notifier:
- self._notifier = self.parent.loop.run_callback(self._notify_links)
-
- def link(self, callback, SpawnedLink=SpawnedLink):
- """
- Link greenlet's completion to a callable.
-
- The *callback* will be called with this instance as an
- argument once this greenlet is dead. A callable is called in
- its own :class:`greenlet.greenlet` (*not* a
- :class:`Greenlet`).
- """
- # XXX: Is the redefinition of SpawnedLink supposed to just be an
- # optimization, or do people use it? It's not documented
- # pylint:disable=redefined-outer-name
- self.rawlink(SpawnedLink(callback))
-
- def unlink(self, callback):
- """Remove the callback set by :meth:`link` or :meth:`rawlink`"""
- try:
- self._links.remove(callback) # pylint:disable=no-member
- except ValueError:
- pass
-
- def unlink_all(self):
- """
- Remove all the callbacks.
-
- .. versionadded:: 1.3a2
- """
- del self._links[:]
-
- def link_value(self, callback, SpawnedLink=SuccessSpawnedLink):
- """
- Like :meth:`link` but *callback* is only notified when the greenlet
- has completed successfully.
- """
- # pylint:disable=redefined-outer-name
- self.link(callback, SpawnedLink=SpawnedLink)
-
- def link_exception(self, callback, SpawnedLink=FailureSpawnedLink):
- """
- Like :meth:`link` but *callback* is only notified when the
- greenlet dies because of an unhandled exception.
- """
- # pylint:disable=redefined-outer-name
- self.link(callback, SpawnedLink=SpawnedLink)
-
- def _notify_links(self):
- while self._links:
- # Early links are allowed to remove later links
- # before we get to them, and they're also allowed to
- # add new links, so we have to be careful about iterating.
-
- # We don't expect this list to be very large, so the time spent
- # manipulating it should be small. a deque is probably not justified.
- # Cython has optimizations to transform this into a memmove anyway.
- link = self._links.pop(0)
- try:
- link(self)
- except: # pylint:disable=bare-except
- self.parent.handle_error((link, self), *sys_exc_info())
-
-
-class _dummy_event(object):
- __slots__ = ('pending', 'active')
-
- def __init__(self):
- self.pending = self.active = False
-
- def stop(self):
- pass
-
- def start(self, cb): # pylint:disable=unused-argument
- raise AssertionError("Cannot start the dummy event")
-
- def close(self):
- pass
-
-_cancelled_start_event = _dummy_event()
-_start_completed_event = _dummy_event()
-
-
-def _kill(glet, exception, waiter):
- try:
- glet.throw(exception)
- except: # pylint:disable=bare-except
- # XXX do we need this here?
- glet.parent.handle_error(glet, *sys_exc_info())
- if waiter is not None:
- waiter.switch(None)
-
-
-def joinall(greenlets, timeout=None, raise_error=False, count=None):
- """
- Wait for the ``greenlets`` to finish.
-
- :param greenlets: A sequence (supporting :func:`len`) of greenlets to wait for.
- :keyword float timeout: If given, the maximum number of seconds to wait.
- :return: A sequence of the greenlets that finished before the timeout (if any)
- expired.
- """
- if not raise_error:
- return wait(greenlets, timeout=timeout, count=count)
-
- done = []
- for obj in iwait(greenlets, timeout=timeout, count=count):
- if getattr(obj, 'exception', None) is not None:
- if hasattr(obj, '_raise_exception'):
- obj._raise_exception()
- else:
- raise obj.exception
- done.append(obj)
- return done
-
-
-def _killall3(greenlets, exception, waiter):
- diehards = []
- for g in greenlets:
- if not g.dead:
- try:
- g.throw(exception)
- except: # pylint:disable=bare-except
- g.parent.handle_error(g, *sys_exc_info())
- if not g.dead:
- diehards.append(g)
- waiter.switch(diehards)
-
-
-def _killall(greenlets, exception):
- for g in greenlets:
- if not g.dead:
- try:
- g.throw(exception)
- except: # pylint:disable=bare-except
- g.parent.handle_error(g, *sys_exc_info())
-
-
-def killall(greenlets, exception=GreenletExit, block=True, timeout=None):
- """
- Forceably terminate all the ``greenlets`` by causing them to raise ``exception``.
-
- .. caution:: Use care when killing greenlets. If they are not prepared for exceptions,
- this could result in corrupted state.
-
- :param greenlets: A **bounded** iterable of the non-None greenlets to terminate.
- *All* the items in this iterable must be greenlets that belong to the same thread.
- :keyword exception: The exception to raise in the greenlets. By default this is
- :class:`GreenletExit`.
- :keyword bool block: If True (the default) then this function only returns when all the
- greenlets are dead; the current greenlet is unscheduled during that process.
- If greenlets ignore the initial exception raised in them,
- then they will be joined (with :func:`gevent.joinall`) and allowed to die naturally.
- If False, this function returns immediately and greenlets will raise
- the exception asynchronously.
- :keyword float timeout: A time in seconds to wait for greenlets to die. If given, it is
- only honored when ``block`` is True.
- :raise Timeout: If blocking and a timeout is given that elapses before
- all the greenlets are dead.
-
- .. versionchanged:: 1.1a2
- *greenlets* can be any iterable of greenlets, like an iterator or a set.
- Previously it had to be a list or tuple.
- """
- # support non-indexable containers like iterators or set objects
- greenlets = list(greenlets)
- if not greenlets:
- return
- loop = greenlets[0].loop
- if block:
- waiter = Waiter() # pylint:disable=undefined-variable
- loop.run_callback(_killall3, greenlets, exception, waiter)
- t = Timeout._start_new_or_dummy(timeout)
- try:
- alive = waiter.get()
- if alive:
- joinall(alive, raise_error=False)
- finally:
- t.cancel()
- else:
- loop.run_callback(_killall, greenlets, exception)
-
-def _init():
- greenlet_init() # pylint:disable=undefined-variable
-
-_init()
-
-from gevent._util import import_c_accel
-import_c_accel(globals(), 'gevent._greenlet')