aboutsummaryrefslogtreecommitdiffstats
path: root/python/gevent/_waiter.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/gevent/_waiter.py')
-rw-r--r--python/gevent/_waiter.py204
1 files changed, 0 insertions, 204 deletions
diff --git a/python/gevent/_waiter.py b/python/gevent/_waiter.py
deleted file mode 100644
index b9ba3e8..0000000
--- a/python/gevent/_waiter.py
+++ /dev/null
@@ -1,204 +0,0 @@
-# -*- coding: utf-8 -*-
-# copyright 2018 gevent
-# cython: auto_pickle=False,embedsignature=True,always_allow_keywords=False
-"""
-Low-level waiting primitives.
-
-"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import sys
-
-from gevent._hub_local import get_hub_noargs as get_hub
-from gevent.exceptions import ConcurrentObjectUseError
-
-__all__ = [
- 'Waiter',
-]
-
-_NONE = object()
-
-locals()['getcurrent'] = __import__('greenlet').getcurrent
-locals()['greenlet_init'] = lambda: None
-
-
-class Waiter(object):
- """
- A low level communication utility for greenlets.
-
- Waiter is a wrapper around greenlet's ``switch()`` and ``throw()`` calls that makes them somewhat safer:
-
- * switching will occur only if the waiting greenlet is executing :meth:`get` method currently;
- * any error raised in the greenlet is handled inside :meth:`switch` and :meth:`throw`
- * if :meth:`switch`/:meth:`throw` is called before the receiver calls :meth:`get`, then :class:`Waiter`
- will store the value/exception. The following :meth:`get` will return the value/raise the exception.
-
- The :meth:`switch` and :meth:`throw` methods must only be called from the :class:`Hub` greenlet.
- The :meth:`get` method must be called from a greenlet other than :class:`Hub`.
-
- >>> result = Waiter()
- >>> timer = get_hub().loop.timer(0.1)
- >>> timer.start(result.switch, 'hello from Waiter')
- >>> result.get() # blocks for 0.1 seconds
- 'hello from Waiter'
- >>> timer.close()
-
- If switch is called before the greenlet gets a chance to call :meth:`get` then
- :class:`Waiter` stores the value.
-
- >>> result = Waiter()
- >>> timer = get_hub().loop.timer(0.1)
- >>> timer.start(result.switch, 'hi from Waiter')
- >>> sleep(0.2)
- >>> result.get() # returns immediately without blocking
- 'hi from Waiter'
- >>> timer.close()
-
- .. warning::
-
- This a limited and dangerous way to communicate between
- greenlets. It can easily leave a greenlet unscheduled forever
- if used incorrectly. Consider using safer classes such as
- :class:`gevent.event.Event`, :class:`gevent.event.AsyncResult`,
- or :class:`gevent.queue.Queue`.
- """
-
- __slots__ = ['hub', 'greenlet', 'value', '_exception']
-
- def __init__(self, hub=None):
- self.hub = get_hub() if hub is None else hub
- self.greenlet = None
- self.value = None
- self._exception = _NONE
-
- def clear(self):
- self.greenlet = None
- self.value = None
- self._exception = _NONE
-
- def __str__(self):
- if self._exception is _NONE:
- return '<%s greenlet=%s>' % (type(self).__name__, self.greenlet)
- if self._exception is None:
- return '<%s greenlet=%s value=%r>' % (type(self).__name__, self.greenlet, self.value)
- return '<%s greenlet=%s exc_info=%r>' % (type(self).__name__, self.greenlet, self.exc_info)
-
- def ready(self):
- """Return true if and only if it holds a value or an exception"""
- return self._exception is not _NONE
-
- def successful(self):
- """Return true if and only if it is ready and holds a value"""
- return self._exception is None
-
- @property
- def exc_info(self):
- "Holds the exception info passed to :meth:`throw` if :meth:`throw` was called. Otherwise ``None``."
- if self._exception is not _NONE:
- return self._exception
-
- def switch(self, value):
- """
- Switch to the greenlet if one's available. Otherwise store the
- *value*.
-
- .. versionchanged:: 1.3b1
- The *value* is no longer optional.
- """
- greenlet = self.greenlet
- if greenlet is None:
- self.value = value
- self._exception = None
- else:
- if getcurrent() is not self.hub: # pylint:disable=undefined-variable
- raise AssertionError("Can only use Waiter.switch method from the Hub greenlet")
- switch = greenlet.switch
- try:
- switch(value)
- except: # pylint:disable=bare-except
- self.hub.handle_error(switch, *sys.exc_info())
-
- def switch_args(self, *args):
- return self.switch(args)
-
- def throw(self, *throw_args):
- """Switch to the greenlet with the exception. If there's no greenlet, store the exception."""
- greenlet = self.greenlet
- if greenlet is None:
- self._exception = throw_args
- else:
- if getcurrent() is not self.hub: # pylint:disable=undefined-variable
- raise AssertionError("Can only use Waiter.switch method from the Hub greenlet")
- throw = greenlet.throw
- try:
- throw(*throw_args)
- except: # pylint:disable=bare-except
- self.hub.handle_error(throw, *sys.exc_info())
-
- def get(self):
- """If a value/an exception is stored, return/raise it. Otherwise until switch() or throw() is called."""
- if self._exception is not _NONE:
- if self._exception is None:
- return self.value
- getcurrent().throw(*self._exception) # pylint:disable=undefined-variable
- else:
- if self.greenlet is not None:
- raise ConcurrentObjectUseError('This Waiter is already used by %r' % (self.greenlet, ))
- self.greenlet = getcurrent() # pylint:disable=undefined-variable
- try:
- return self.hub.switch()
- finally:
- self.greenlet = None
-
- def __call__(self, source):
- if source.exception is None:
- self.switch(source.value)
- else:
- self.throw(source.exception)
-
- # can also have a debugging version, that wraps the value in a tuple (self, value) in switch()
- # and unwraps it in wait() thus checking that switch() was indeed called
-
-
-
-class MultipleWaiter(Waiter):
- """
- An internal extension of Waiter that can be used if multiple objects
- must be waited on, and there is a chance that in between waits greenlets
- might be switched out. All greenlets that switch to this waiter
- will have their value returned.
-
- This does not handle exceptions or throw methods.
- """
- __slots__ = ['_values']
-
- def __init__(self, hub=None):
- Waiter.__init__(self, hub)
- # we typically expect a relatively small number of these to be outstanding.
- # since we pop from the left, a deque might be slightly
- # more efficient, but since we're in the hub we avoid imports if
- # we can help it to better support monkey-patching, and delaying the import
- # here can be impractical (see https://github.com/gevent/gevent/issues/652)
- self._values = list()
-
- def switch(self, value):
- self._values.append(value)
- Waiter.switch(self, True)
-
- def get(self):
- if not self._values:
- Waiter.get(self)
- Waiter.clear(self)
-
- return self._values.pop(0)
-
-def _init():
- greenlet_init() # pylint:disable=undefined-variable
-
-_init()
-
-
-from gevent._util import import_c_accel
-import_c_accel(globals(), 'gevent.__waiter')