Windows: Use 32-bit distribution of python
This commit is contained in:
204
python/gevent/_waiter.py
Normal file
204
python/gevent/_waiter.py
Normal file
@@ -0,0 +1,204 @@
|
||||
# -*- 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')
|
||||
Reference in New Issue
Block a user