Code import

This commit is contained in:
Jan Groß
2017-07-20 18:02:16 +02:00
parent 2ab406905d
commit 5c348ffe84
1807 changed files with 257494 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
import os
from eventlet import patcher
from eventlet.support import greenlets as greenlet, six
__all__ = ["use_hub", "get_hub", "get_default_hub", "trampoline"]
threading = patcher.original('threading')
_threadlocal = threading.local()
def get_default_hub():
"""Select the default hub implementation based on what multiplexing
libraries are installed. The order that the hubs are tried is:
* epoll
* kqueue
* poll
* select
It won't automatically select the pyevent hub, because it's not
python-thread-safe.
.. include:: ../doc/common.txt
.. note :: |internal|
"""
# pyevent hub disabled for now because it is not thread-safe
# try:
# import eventlet.hubs.pyevent
# return eventlet.hubs.pyevent
# except:
# pass
select = patcher.original('select')
try:
import eventlet.hubs.epolls
return eventlet.hubs.epolls
except ImportError:
try:
import eventlet.hubs.kqueue
return eventlet.hubs.kqueue
except ImportError:
if hasattr(select, 'poll'):
import eventlet.hubs.poll
return eventlet.hubs.poll
else:
import eventlet.hubs.selects
return eventlet.hubs.selects
def use_hub(mod=None):
"""Use the module *mod*, containing a class called Hub, as the
event hub. Usually not required; the default hub is usually fine.
Mod can be an actual module, a string, or None. If *mod* is a module,
it uses it directly. If *mod* is a string and contains either '.' or ':'
use_hub tries to import the hub using the 'package.subpackage.module:Class'
convention, otherwise use_hub looks for a matching setuptools entry point
in the 'eventlet.hubs' group to load or finally tries to import
`eventlet.hubs.mod` and use that as the hub module. If *mod* is None,
use_hub uses the default hub. Only call use_hub during application
initialization, because it resets the hub's state and any existing
timers or listeners will never be resumed.
"""
if mod is None:
mod = os.environ.get('EVENTLET_HUB', None)
if mod is None:
mod = get_default_hub()
if hasattr(_threadlocal, 'hub'):
del _threadlocal.hub
if isinstance(mod, six.string_types):
assert mod.strip(), "Need to specify a hub"
if '.' in mod or ':' in mod:
modulename, _, classname = mod.strip().partition(':')
mod = __import__(modulename, globals(), locals(), [classname])
if classname:
mod = getattr(mod, classname)
else:
found = False
# setuptools 5.4.1 test_import_patched_defaults fail
# https://github.com/eventlet/eventlet/issues/177
try:
# try and import pkg_resources ...
import pkg_resources
except ImportError:
# ... but do not depend on it
pkg_resources = None
if pkg_resources is not None:
for entry in pkg_resources.iter_entry_points(
group='eventlet.hubs', name=mod):
mod, found = entry.load(), True
break
if not found:
mod = __import__(
'eventlet.hubs.' + mod, globals(), locals(), ['Hub'])
if hasattr(mod, 'Hub'):
_threadlocal.Hub = mod.Hub
else:
_threadlocal.Hub = mod
def get_hub():
"""Get the current event hub singleton object.
.. note :: |internal|
"""
try:
hub = _threadlocal.hub
except AttributeError:
try:
_threadlocal.Hub
except AttributeError:
use_hub()
hub = _threadlocal.hub = _threadlocal.Hub()
return hub
# Lame middle file import because complex dependencies in import graph
from eventlet import timeout
def trampoline(fd, read=None, write=None, timeout=None,
timeout_exc=timeout.Timeout,
mark_as_closed=None):
"""Suspend the current coroutine until the given socket object or file
descriptor is ready to *read*, ready to *write*, or the specified
*timeout* elapses, depending on arguments specified.
To wait for *fd* to be ready to read, pass *read* ``=True``; ready to
write, pass *write* ``=True``. To specify a timeout, pass the *timeout*
argument in seconds.
If the specified *timeout* elapses before the socket is ready to read or
write, *timeout_exc* will be raised instead of ``trampoline()``
returning normally.
.. note :: |internal|
"""
t = None
hub = get_hub()
current = greenlet.getcurrent()
assert hub.greenlet is not current, 'do not call blocking functions from the mainloop'
assert not (
read and write), 'not allowed to trampoline for reading and writing'
try:
fileno = fd.fileno()
except AttributeError:
fileno = fd
if timeout is not None:
def _timeout(exc):
# This is only useful to insert debugging
current.throw(exc)
t = hub.schedule_call_global(timeout, _timeout, timeout_exc)
try:
if read:
listener = hub.add(hub.READ, fileno, current.switch, current.throw, mark_as_closed)
elif write:
listener = hub.add(hub.WRITE, fileno, current.switch, current.throw, mark_as_closed)
try:
return hub.switch()
finally:
hub.remove(listener)
finally:
if t is not None:
t.cancel()
def notify_close(fd):
"""
A particular file descriptor has been explicitly closed. Register for any
waiting listeners to be notified on the next run loop.
"""
hub = get_hub()
hub.notify_close(fd)
def notify_opened(fd):
"""
Some file descriptors may be closed 'silently' - that is, by the garbage
collector, by an external library, etc. When the OS returns a file descriptor
from an open call (or something similar), this may be the only indication we
have that the FD has been closed and then recycled.
We let the hub know that the old file descriptor is dead; any stuck listeners
will be disabled and notified in turn.
"""
hub = get_hub()
hub.mark_as_reopened(fd)
class IOClosed(IOError):
pass

View File

@@ -0,0 +1,61 @@
import errno
from eventlet.support import get_errno
from eventlet import patcher
select = patcher.original("select")
if hasattr(select, 'epoll'):
epoll = select.epoll
else:
try:
# http://pypi.python.org/pypi/select26/
from select26 import epoll
except ImportError:
try:
import epoll as _epoll_mod
except ImportError:
raise ImportError(
"No epoll implementation found in select module or PYTHONPATH")
else:
if hasattr(_epoll_mod, 'poll'):
epoll = _epoll_mod.poll
else:
raise ImportError(
"You have an old, buggy epoll module in PYTHONPATH."
" Install http://pypi.python.org/pypi/python-epoll/"
" NOT http://pypi.python.org/pypi/pyepoll/. "
" easy_install pyepoll installs the wrong version.")
from eventlet.hubs.hub import BaseHub
from eventlet.hubs import poll
from eventlet.hubs.poll import READ, WRITE
# NOTE: we rely on the fact that the epoll flag constants
# are identical in value to the poll constants
class Hub(poll.Hub):
def __init__(self, clock=None):
BaseHub.__init__(self, clock)
self.poll = epoll()
try:
# modify is required by select.epoll
self.modify = self.poll.modify
except AttributeError:
self.modify = self.poll.register
def add(self, evtype, fileno, cb, tb, mac):
oldlisteners = bool(self.listeners[READ].get(fileno) or
self.listeners[WRITE].get(fileno))
listener = BaseHub.add(self, evtype, fileno, cb, tb, mac)
try:
if not oldlisteners:
# Means we've added a new listener
self.register(fileno, new=True)
else:
self.register(fileno, new=False)
except IOError as ex: # ignore EEXIST, #80
if get_errno(ex) != errno.EEXIST:
raise
return listener
def do_poll(self, seconds):
return self.poll.poll(seconds)

View File

@@ -0,0 +1,483 @@
import errno
import heapq
import math
import signal
import sys
import traceback
arm_alarm = None
if hasattr(signal, 'setitimer'):
def alarm_itimer(seconds):
signal.setitimer(signal.ITIMER_REAL, seconds)
arm_alarm = alarm_itimer
else:
try:
import itimer
arm_alarm = itimer.alarm
except ImportError:
def alarm_signal(seconds):
signal.alarm(math.ceil(seconds))
arm_alarm = alarm_signal
from eventlet.hubs import timer, IOClosed
from eventlet.support import greenlets as greenlet, clear_sys_exc_info, monotonic, six
g_prevent_multiple_readers = True
READ = "read"
WRITE = "write"
def closed_callback(fileno):
""" Used to de-fang a callback that may be triggered by a loop in BaseHub.wait
"""
# No-op.
pass
class FdListener(object):
def __init__(self, evtype, fileno, cb, tb, mark_as_closed):
""" The following are required:
cb - the standard callback, which will switch into the
listening greenlet to indicate that the event waited upon
is ready
tb - a 'throwback'. This is typically greenlet.throw, used
to raise a signal into the target greenlet indicating that
an event was obsoleted by its underlying filehandle being
repurposed.
mark_as_closed - if any listener is obsoleted, this is called
(in the context of some other client greenlet) to alert
underlying filehandle-wrapping objects that they've been
closed.
"""
assert (evtype is READ or evtype is WRITE)
self.evtype = evtype
self.fileno = fileno
self.cb = cb
self.tb = tb
self.mark_as_closed = mark_as_closed
self.spent = False
self.greenlet = greenlet.getcurrent()
def __repr__(self):
return "%s(%r, %r, %r, %r)" % (type(self).__name__, self.evtype, self.fileno,
self.cb, self.tb)
__str__ = __repr__
def defang(self):
self.cb = closed_callback
if self.mark_as_closed is not None:
self.mark_as_closed()
self.spent = True
noop = FdListener(READ, 0, lambda x: None, lambda x: None, None)
# in debug mode, track the call site that created the listener
class DebugListener(FdListener):
def __init__(self, evtype, fileno, cb, tb, mark_as_closed):
self.where_called = traceback.format_stack()
self.greenlet = greenlet.getcurrent()
super(DebugListener, self).__init__(evtype, fileno, cb, tb, mark_as_closed)
def __repr__(self):
return "DebugListener(%r, %r, %r, %r, %r, %r)\n%sEndDebugFdListener" % (
self.evtype,
self.fileno,
self.cb,
self.tb,
self.mark_as_closed,
self.greenlet,
''.join(self.where_called))
__str__ = __repr__
def alarm_handler(signum, frame):
import inspect
raise RuntimeError("Blocking detector ALARMED at" + str(inspect.getframeinfo(frame)))
class BaseHub(object):
""" Base hub class for easing the implementation of subclasses that are
specific to a particular underlying event architecture. """
SYSTEM_EXCEPTIONS = (KeyboardInterrupt, SystemExit)
READ = READ
WRITE = WRITE
def __init__(self, clock=None):
self.listeners = {READ: {}, WRITE: {}}
self.secondaries = {READ: {}, WRITE: {}}
self.closed = []
if clock is None:
clock = monotonic.monotonic
self.clock = clock
self.greenlet = greenlet.greenlet(self.run)
self.stopping = False
self.running = False
self.timers = []
self.next_timers = []
self.lclass = FdListener
self.timers_canceled = 0
self.debug_exceptions = True
self.debug_blocking = False
self.debug_blocking_resolution = 1
def block_detect_pre(self):
# shortest alarm we can possibly raise is one second
tmp = signal.signal(signal.SIGALRM, alarm_handler)
if tmp != alarm_handler:
self._old_signal_handler = tmp
arm_alarm(self.debug_blocking_resolution)
def block_detect_post(self):
if (hasattr(self, "_old_signal_handler") and
self._old_signal_handler):
signal.signal(signal.SIGALRM, self._old_signal_handler)
signal.alarm(0)
def add(self, evtype, fileno, cb, tb, mark_as_closed):
""" Signals an intent to or write a particular file descriptor.
The *evtype* argument is either the constant READ or WRITE.
The *fileno* argument is the file number of the file of interest.
The *cb* argument is the callback which will be called when the file
is ready for reading/writing.
The *tb* argument is the throwback used to signal (into the greenlet)
that the file was closed.
The *mark_as_closed* is used in the context of the event hub to
prepare a Python object as being closed, pre-empting further
close operations from accidentally shutting down the wrong OS thread.
"""
listener = self.lclass(evtype, fileno, cb, tb, mark_as_closed)
bucket = self.listeners[evtype]
if fileno in bucket:
if g_prevent_multiple_readers:
raise RuntimeError(
"Second simultaneous %s on fileno %s "
"detected. Unless you really know what you're doing, "
"make sure that only one greenthread can %s any "
"particular socket. Consider using a pools.Pool. "
"If you do know what you're doing and want to disable "
"this error, call "
"eventlet.debug.hub_prevent_multiple_readers(False) - MY THREAD=%s; "
"THAT THREAD=%s" % (
evtype, fileno, evtype, cb, bucket[fileno]))
# store off the second listener in another structure
self.secondaries[evtype].setdefault(fileno, []).append(listener)
else:
bucket[fileno] = listener
return listener
def _obsolete(self, fileno):
""" We've received an indication that 'fileno' has been obsoleted.
Any current listeners must be defanged, and notifications to
their greenlets queued up to send.
"""
found = False
for evtype, bucket in six.iteritems(self.secondaries):
if fileno in bucket:
for listener in bucket[fileno]:
found = True
self.closed.append(listener)
listener.defang()
del bucket[fileno]
# For the primary listeners, we actually need to call remove,
# which may modify the underlying OS polling objects.
for evtype, bucket in six.iteritems(self.listeners):
if fileno in bucket:
listener = bucket[fileno]
found = True
self.closed.append(listener)
self.remove(listener)
listener.defang()
return found
def notify_close(self, fileno):
""" We might want to do something when a fileno is closed.
However, currently it suffices to obsolete listeners only
when we detect an old fileno being recycled, on open.
"""
pass
def remove(self, listener):
if listener.spent:
# trampoline may trigger this in its finally section.
return
fileno = listener.fileno
evtype = listener.evtype
self.listeners[evtype].pop(fileno, None)
# migrate a secondary listener to be the primary listener
if fileno in self.secondaries[evtype]:
sec = self.secondaries[evtype].get(fileno, None)
if not sec:
return
self.listeners[evtype][fileno] = sec.pop(0)
if not sec:
del self.secondaries[evtype][fileno]
def mark_as_reopened(self, fileno):
""" If a file descriptor is returned by the OS as the result of some
open call (or equivalent), that signals that it might be being
recycled.
Catch the case where the fd was previously in use.
"""
self._obsolete(fileno)
def remove_descriptor(self, fileno):
""" Completely remove all listeners for this fileno. For internal use
only."""
listeners = []
listeners.append(self.listeners[READ].pop(fileno, noop))
listeners.append(self.listeners[WRITE].pop(fileno, noop))
listeners.extend(self.secondaries[READ].pop(fileno, ()))
listeners.extend(self.secondaries[WRITE].pop(fileno, ()))
for listener in listeners:
try:
listener.cb(fileno)
except Exception:
self.squelch_generic_exception(sys.exc_info())
def close_one(self):
""" Triggered from the main run loop. If a listener's underlying FD was
closed somehow, throw an exception back to the trampoline, which should
be able to manage it appropriately.
"""
listener = self.closed.pop()
if not listener.greenlet.dead:
# There's no point signalling a greenlet that's already dead.
listener.tb(IOClosed(errno.ENOTCONN, "Operation on closed file"))
def ensure_greenlet(self):
if self.greenlet.dead:
# create new greenlet sharing same parent as original
new = greenlet.greenlet(self.run, self.greenlet.parent)
# need to assign as parent of old greenlet
# for those greenlets that are currently
# children of the dead hub and may subsequently
# exit without further switching to hub.
self.greenlet.parent = new
self.greenlet = new
def switch(self):
cur = greenlet.getcurrent()
assert cur is not self.greenlet, 'Cannot switch to MAINLOOP from MAINLOOP'
switch_out = getattr(cur, 'switch_out', None)
if switch_out is not None:
try:
switch_out()
except:
self.squelch_generic_exception(sys.exc_info())
self.ensure_greenlet()
try:
if self.greenlet.parent is not cur:
cur.parent = self.greenlet
except ValueError:
pass # gets raised if there is a greenlet parent cycle
clear_sys_exc_info()
return self.greenlet.switch()
def squelch_exception(self, fileno, exc_info):
traceback.print_exception(*exc_info)
sys.stderr.write("Removing descriptor: %r\n" % (fileno,))
sys.stderr.flush()
try:
self.remove_descriptor(fileno)
except Exception as e:
sys.stderr.write("Exception while removing descriptor! %r\n" % (e,))
sys.stderr.flush()
def wait(self, seconds=None):
raise NotImplementedError("Implement this in a subclass")
def default_sleep(self):
return 60.0
def sleep_until(self):
t = self.timers
if not t:
return None
return t[0][0]
def run(self, *a, **kw):
"""Run the runloop until abort is called.
"""
# accept and discard variable arguments because they will be
# supplied if other greenlets have run and exited before the
# hub's greenlet gets a chance to run
if self.running:
raise RuntimeError("Already running!")
try:
self.running = True
self.stopping = False
while not self.stopping:
while self.closed:
# We ditch all of these first.
self.close_one()
self.prepare_timers()
if self.debug_blocking:
self.block_detect_pre()
self.fire_timers(self.clock())
if self.debug_blocking:
self.block_detect_post()
self.prepare_timers()
wakeup_when = self.sleep_until()
if wakeup_when is None:
sleep_time = self.default_sleep()
else:
sleep_time = wakeup_when - self.clock()
if sleep_time > 0:
self.wait(sleep_time)
else:
self.wait(0)
else:
self.timers_canceled = 0
del self.timers[:]
del self.next_timers[:]
finally:
self.running = False
self.stopping = False
def abort(self, wait=False):
"""Stop the runloop. If run is executing, it will exit after
completing the next runloop iteration.
Set *wait* to True to cause abort to switch to the hub immediately and
wait until it's finished processing. Waiting for the hub will only
work from the main greenthread; all other greenthreads will become
unreachable.
"""
if self.running:
self.stopping = True
if wait:
assert self.greenlet is not greenlet.getcurrent(
), "Can't abort with wait from inside the hub's greenlet."
# schedule an immediate timer just so the hub doesn't sleep
self.schedule_call_global(0, lambda: None)
# switch to it; when done the hub will switch back to its parent,
# the main greenlet
self.switch()
def squelch_generic_exception(self, exc_info):
if self.debug_exceptions:
traceback.print_exception(*exc_info)
sys.stderr.flush()
clear_sys_exc_info()
def squelch_timer_exception(self, timer, exc_info):
if self.debug_exceptions:
traceback.print_exception(*exc_info)
sys.stderr.flush()
clear_sys_exc_info()
def add_timer(self, timer):
scheduled_time = self.clock() + timer.seconds
self.next_timers.append((scheduled_time, timer))
return scheduled_time
def timer_canceled(self, timer):
self.timers_canceled += 1
len_timers = len(self.timers) + len(self.next_timers)
if len_timers > 1000 and len_timers / 2 <= self.timers_canceled:
self.timers_canceled = 0
self.timers = [t for t in self.timers if not t[1].called]
self.next_timers = [t for t in self.next_timers if not t[1].called]
heapq.heapify(self.timers)
def prepare_timers(self):
heappush = heapq.heappush
t = self.timers
for item in self.next_timers:
if item[1].called:
self.timers_canceled -= 1
else:
heappush(t, item)
del self.next_timers[:]
def schedule_call_local(self, seconds, cb, *args, **kw):
"""Schedule a callable to be called after 'seconds' seconds have
elapsed. Cancel the timer if greenlet has exited.
seconds: The number of seconds to wait.
cb: The callable to call after the given time.
*args: Arguments to pass to the callable when called.
**kw: Keyword arguments to pass to the callable when called.
"""
t = timer.LocalTimer(seconds, cb, *args, **kw)
self.add_timer(t)
return t
def schedule_call_global(self, seconds, cb, *args, **kw):
"""Schedule a callable to be called after 'seconds' seconds have
elapsed. The timer will NOT be canceled if the current greenlet has
exited before the timer fires.
seconds: The number of seconds to wait.
cb: The callable to call after the given time.
*args: Arguments to pass to the callable when called.
**kw: Keyword arguments to pass to the callable when called.
"""
t = timer.Timer(seconds, cb, *args, **kw)
self.add_timer(t)
return t
def fire_timers(self, when):
t = self.timers
heappop = heapq.heappop
while t:
next = t[0]
exp = next[0]
timer = next[1]
if when < exp:
break
heappop(t)
try:
if timer.called:
self.timers_canceled -= 1
else:
timer()
except self.SYSTEM_EXCEPTIONS:
raise
except:
self.squelch_timer_exception(timer, sys.exc_info())
clear_sys_exc_info()
# for debugging:
def get_readers(self):
return self.listeners[READ].values()
def get_writers(self):
return self.listeners[WRITE].values()
def get_timers_count(hub):
return len(hub.timers) + len(hub.next_timers)
def set_debug_listeners(self, value):
if value:
self.lclass = DebugListener
else:
self.lclass = FdListener
def set_timer_exceptions(self, value):
self.debug_exceptions = value

Binary file not shown.

View File

@@ -0,0 +1,114 @@
import os
import sys
from eventlet import patcher, support
from eventlet.support import six
select = patcher.original('select')
time = patcher.original('time')
from eventlet.hubs.hub import BaseHub, READ, WRITE, noop
if getattr(select, 'kqueue', None) is None:
raise ImportError('No kqueue implementation found in select module')
FILTERS = {READ: select.KQ_FILTER_READ,
WRITE: select.KQ_FILTER_WRITE}
class Hub(BaseHub):
MAX_EVENTS = 100
def __init__(self, clock=None):
super(Hub, self).__init__(clock)
self._events = {}
self._init_kqueue()
def _init_kqueue(self):
self.kqueue = select.kqueue()
self._pid = os.getpid()
def _reinit_kqueue(self):
self.kqueue.close()
self._init_kqueue()
kqueue = self.kqueue
events = [e for i in six.itervalues(self._events)
for e in six.itervalues(i)]
kqueue.control(events, 0, 0)
def _control(self, events, max_events, timeout):
try:
return self.kqueue.control(events, max_events, timeout)
except (OSError, IOError):
# have we forked?
if os.getpid() != self._pid:
self._reinit_kqueue()
return self.kqueue.control(events, max_events, timeout)
raise
def add(self, evtype, fileno, cb, tb, mac):
listener = super(Hub, self).add(evtype, fileno, cb, tb, mac)
events = self._events.setdefault(fileno, {})
if evtype not in events:
try:
event = select.kevent(fileno, FILTERS.get(evtype), select.KQ_EV_ADD)
self._control([event], 0, 0)
events[evtype] = event
except ValueError:
super(Hub, self).remove(listener)
raise
return listener
def _delete_events(self, events):
del_events = [
select.kevent(e.ident, e.filter, select.KQ_EV_DELETE)
for e in events
]
self._control(del_events, 0, 0)
def remove(self, listener):
super(Hub, self).remove(listener)
evtype = listener.evtype
fileno = listener.fileno
if not self.listeners[evtype].get(fileno):
event = self._events[fileno].pop(evtype, None)
if event is None:
return
try:
self._delete_events((event,))
except OSError:
pass
def remove_descriptor(self, fileno):
super(Hub, self).remove_descriptor(fileno)
try:
events = self._events.pop(fileno).values()
self._delete_events(events)
except KeyError:
pass
except OSError:
pass
def wait(self, seconds=None):
readers = self.listeners[READ]
writers = self.listeners[WRITE]
if not readers and not writers:
if seconds:
time.sleep(seconds)
return
result = self._control([], self.MAX_EVENTS, seconds)
SYSTEM_EXCEPTIONS = self.SYSTEM_EXCEPTIONS
for event in result:
fileno = event.ident
evfilt = event.filter
try:
if evfilt == FILTERS[READ]:
readers.get(fileno, noop).cb(fileno)
if evfilt == FILTERS[WRITE]:
writers.get(fileno, noop).cb(fileno)
except SYSTEM_EXCEPTIONS:
raise
except:
self.squelch_exception(fileno, sys.exc_info())
support.clear_sys_exc_info()

View File

@@ -0,0 +1,122 @@
import errno
import sys
from eventlet import patcher
select = patcher.original('select')
time = patcher.original('time')
from eventlet.hubs.hub import BaseHub, READ, WRITE, noop
from eventlet.support import get_errno, clear_sys_exc_info
EXC_MASK = select.POLLERR | select.POLLHUP
READ_MASK = select.POLLIN | select.POLLPRI
WRITE_MASK = select.POLLOUT
class Hub(BaseHub):
def __init__(self, clock=None):
super(Hub, self).__init__(clock)
self.poll = select.poll()
# poll.modify is new to 2.6
try:
self.modify = self.poll.modify
except AttributeError:
self.modify = self.poll.register
def add(self, evtype, fileno, cb, tb, mac):
listener = super(Hub, self).add(evtype, fileno, cb, tb, mac)
self.register(fileno, new=True)
return listener
def remove(self, listener):
super(Hub, self).remove(listener)
self.register(listener.fileno)
def register(self, fileno, new=False):
mask = 0
if self.listeners[READ].get(fileno):
mask |= READ_MASK | EXC_MASK
if self.listeners[WRITE].get(fileno):
mask |= WRITE_MASK | EXC_MASK
try:
if mask:
if new:
self.poll.register(fileno, mask)
else:
try:
self.modify(fileno, mask)
except (IOError, OSError):
self.poll.register(fileno, mask)
else:
try:
self.poll.unregister(fileno)
except (KeyError, IOError, OSError):
# raised if we try to remove a fileno that was
# already removed/invalid
pass
except ValueError:
# fileno is bad, issue 74
self.remove_descriptor(fileno)
raise
def remove_descriptor(self, fileno):
super(Hub, self).remove_descriptor(fileno)
try:
self.poll.unregister(fileno)
except (KeyError, ValueError, IOError, OSError):
# raised if we try to remove a fileno that was
# already removed/invalid
pass
def do_poll(self, seconds):
# poll.poll expects integral milliseconds
return self.poll.poll(int(seconds * 1000.0))
def wait(self, seconds=None):
readers = self.listeners[READ]
writers = self.listeners[WRITE]
if not readers and not writers:
if seconds:
time.sleep(seconds)
return
try:
presult = self.do_poll(seconds)
except (IOError, select.error) as e:
if get_errno(e) == errno.EINTR:
return
raise
SYSTEM_EXCEPTIONS = self.SYSTEM_EXCEPTIONS
if self.debug_blocking:
self.block_detect_pre()
# Accumulate the listeners to call back to prior to
# triggering any of them. This is to keep the set
# of callbacks in sync with the events we've just
# polled for. It prevents one handler from invalidating
# another.
callbacks = set()
for fileno, event in presult:
if event & READ_MASK:
callbacks.add((readers.get(fileno, noop), fileno))
if event & WRITE_MASK:
callbacks.add((writers.get(fileno, noop), fileno))
if event & select.POLLNVAL:
self.remove_descriptor(fileno)
continue
if event & EXC_MASK:
callbacks.add((readers.get(fileno, noop), fileno))
callbacks.add((writers.get(fileno, noop), fileno))
for listener, fileno in callbacks:
try:
listener.cb(fileno)
except SYSTEM_EXCEPTIONS:
raise
except:
self.squelch_exception(fileno, sys.exc_info())
clear_sys_exc_info()
if self.debug_blocking:
self.block_detect_post()

View File

@@ -0,0 +1,178 @@
import sys
import traceback
import event
import types
from eventlet.support import greenlets as greenlet, six
from eventlet.hubs.hub import BaseHub, READ, WRITE
class event_wrapper(object):
def __init__(self, impl=None, seconds=None):
self.impl = impl
self.seconds = seconds
def __repr__(self):
if self.impl is not None:
return repr(self.impl)
else:
return object.__repr__(self)
def __str__(self):
if self.impl is not None:
return str(self.impl)
else:
return object.__str__(self)
def cancel(self):
if self.impl is not None:
self.impl.delete()
self.impl = None
@property
def pending(self):
return bool(self.impl and self.impl.pending())
class Hub(BaseHub):
SYSTEM_EXCEPTIONS = (KeyboardInterrupt, SystemExit)
def __init__(self):
super(Hub, self).__init__()
event.init()
self.signal_exc_info = None
self.signal(
2,
lambda signalnum, frame: self.greenlet.parent.throw(KeyboardInterrupt))
self.events_to_add = []
def dispatch(self):
loop = event.loop
while True:
for e in self.events_to_add:
if e is not None and e.impl is not None and e.seconds is not None:
e.impl.add(e.seconds)
e.seconds = None
self.events_to_add = []
result = loop()
if getattr(event, '__event_exc', None) is not None:
# only have to do this because of bug in event.loop
t = getattr(event, '__event_exc')
setattr(event, '__event_exc', None)
assert getattr(event, '__event_exc') is None
six.reraise(t[0], t[1], t[2])
if result != 0:
return result
def run(self):
while True:
try:
self.dispatch()
except greenlet.GreenletExit:
break
except self.SYSTEM_EXCEPTIONS:
raise
except:
if self.signal_exc_info is not None:
self.schedule_call_global(
0, greenlet.getcurrent().parent.throw, *self.signal_exc_info)
self.signal_exc_info = None
else:
self.squelch_timer_exception(None, sys.exc_info())
def abort(self, wait=True):
self.schedule_call_global(0, self.greenlet.throw, greenlet.GreenletExit)
if wait:
assert self.greenlet is not greenlet.getcurrent(
), "Can't abort with wait from inside the hub's greenlet."
self.switch()
def _getrunning(self):
return bool(self.greenlet)
def _setrunning(self, value):
pass # exists for compatibility with BaseHub
running = property(_getrunning, _setrunning)
def add(self, evtype, fileno, real_cb, real_tb, mac):
# this is stupid: pyevent won't call a callback unless it's a function,
# so we have to force it to be one here
if isinstance(real_cb, types.BuiltinMethodType):
def cb(_d):
real_cb(_d)
else:
cb = real_cb
if evtype is READ:
evt = event.read(fileno, cb, fileno)
elif evtype is WRITE:
evt = event.write(fileno, cb, fileno)
return super(Hub, self).add(evtype, fileno, evt, real_tb, mac)
def signal(self, signalnum, handler):
def wrapper():
try:
handler(signalnum, None)
except:
self.signal_exc_info = sys.exc_info()
event.abort()
return event_wrapper(event.signal(signalnum, wrapper))
def remove(self, listener):
super(Hub, self).remove(listener)
listener.cb.delete()
def remove_descriptor(self, fileno):
for lcontainer in six.itervalues(self.listeners):
listener = lcontainer.pop(fileno, None)
if listener:
try:
listener.cb.delete()
except self.SYSTEM_EXCEPTIONS:
raise
except:
traceback.print_exc()
def schedule_call_local(self, seconds, cb, *args, **kwargs):
current = greenlet.getcurrent()
if current is self.greenlet:
return self.schedule_call_global(seconds, cb, *args, **kwargs)
event_impl = event.event(_scheduled_call_local, (cb, args, kwargs, current))
wrapper = event_wrapper(event_impl, seconds=seconds)
self.events_to_add.append(wrapper)
return wrapper
schedule_call = schedule_call_local
def schedule_call_global(self, seconds, cb, *args, **kwargs):
event_impl = event.event(_scheduled_call, (cb, args, kwargs))
wrapper = event_wrapper(event_impl, seconds=seconds)
self.events_to_add.append(wrapper)
return wrapper
def _version_info(self):
baseversion = event.__version__
return baseversion
def _scheduled_call(event_impl, handle, evtype, arg):
cb, args, kwargs = arg
try:
cb(*args, **kwargs)
finally:
event_impl.delete()
def _scheduled_call_local(event_impl, handle, evtype, arg):
cb, args, kwargs, caller_greenlet = arg
try:
if not caller_greenlet.dead:
cb(*args, **kwargs)
finally:
event_impl.delete()

View File

@@ -0,0 +1,60 @@
import errno
import sys
from eventlet import patcher
from eventlet.support import get_errno, clear_sys_exc_info
select = patcher.original('select')
time = patcher.original('time')
from eventlet.hubs.hub import BaseHub, READ, WRITE, noop
try:
BAD_SOCK = set((errno.EBADF, errno.WSAENOTSOCK))
except AttributeError:
BAD_SOCK = set((errno.EBADF,))
class Hub(BaseHub):
def _remove_bad_fds(self):
""" Iterate through fds, removing the ones that are bad per the
operating system.
"""
all_fds = list(self.listeners[READ]) + list(self.listeners[WRITE])
for fd in all_fds:
try:
select.select([fd], [], [], 0)
except select.error as e:
if get_errno(e) in BAD_SOCK:
self.remove_descriptor(fd)
def wait(self, seconds=None):
readers = self.listeners[READ]
writers = self.listeners[WRITE]
if not readers and not writers:
if seconds:
time.sleep(seconds)
return
all_fds = list(readers) + list(writers)
try:
r, w, er = select.select(readers.keys(), writers.keys(), all_fds, seconds)
except select.error as e:
if get_errno(e) == errno.EINTR:
return
elif get_errno(e) in BAD_SOCK:
self._remove_bad_fds()
return
else:
raise
for fileno in er:
readers.get(fileno, noop).cb(fileno)
writers.get(fileno, noop).cb(fileno)
for listeners, events in ((readers, r), (writers, w)):
for fileno in events:
try:
listeners.get(fileno, noop).cb(fileno)
except self.SYSTEM_EXCEPTIONS:
raise
except:
self.squelch_exception(fileno, sys.exc_info())
clear_sys_exc_info()

View File

@@ -0,0 +1,105 @@
import traceback
from eventlet.support import greenlets as greenlet, six
from eventlet.hubs import get_hub
""" If true, captures a stack trace for each timer when constructed. This is
useful for debugging leaking timers, to find out where the timer was set up. """
_g_debug = False
class Timer(object):
def __init__(self, seconds, cb, *args, **kw):
"""Create a timer.
seconds: The minimum number of seconds to wait before calling
cb: The callback to call when the timer has expired
*args: The arguments to pass to cb
**kw: The keyword arguments to pass to cb
This timer will not be run unless it is scheduled in a runloop by
calling timer.schedule() or runloop.add_timer(timer).
"""
self.seconds = seconds
self.tpl = cb, args, kw
self.called = False
if _g_debug:
self.traceback = six.StringIO()
traceback.print_stack(file=self.traceback)
@property
def pending(self):
return not self.called
def __repr__(self):
secs = getattr(self, 'seconds', None)
cb, args, kw = getattr(self, 'tpl', (None, None, None))
retval = "Timer(%s, %s, *%s, **%s)" % (
secs, cb, args, kw)
if _g_debug and hasattr(self, 'traceback'):
retval += '\n' + self.traceback.getvalue()
return retval
def copy(self):
cb, args, kw = self.tpl
return self.__class__(self.seconds, cb, *args, **kw)
def schedule(self):
"""Schedule this timer to run in the current runloop.
"""
self.called = False
self.scheduled_time = get_hub().add_timer(self)
return self
def __call__(self, *args):
if not self.called:
self.called = True
cb, args, kw = self.tpl
try:
cb(*args, **kw)
finally:
try:
del self.tpl
except AttributeError:
pass
def cancel(self):
"""Prevent this timer from being called. If the timer has already
been called or canceled, has no effect.
"""
if not self.called:
self.called = True
get_hub().timer_canceled(self)
try:
del self.tpl
except AttributeError:
pass
# No default ordering in 3.x. heapq uses <
# FIXME should full set be added?
def __lt__(self, other):
return id(self) < id(other)
class LocalTimer(Timer):
def __init__(self, *args, **kwargs):
self.greenlet = greenlet.getcurrent()
Timer.__init__(self, *args, **kwargs)
@property
def pending(self):
if self.greenlet is None or self.greenlet.dead:
return False
return not self.called
def __call__(self, *args):
if not self.called:
self.called = True
if self.greenlet is not None and self.greenlet.dead:
return
cb, args, kw = self.tpl
cb(*args, **kw)
def cancel(self):
self.greenlet = None
Timer.cancel(self)