Пример #1
0
Файл: hub.py Проект: inercia/evy
    def _stopped(self):
        """
        Invoked when the loop has been stopped
        """
        self.running = False
        self.stopping = False

        ## remove all the timers and pollers
        for timer in self.timers:                   timer.destroy()
        for poller in self.pollers.values():        poller.destroy()
        for callback in self.callbacks:             callback.destroy()
        self.timers = set()
        self.pollers = {}
        self.callbacks = set()
Пример #2
0
Файл: hub.py Проект: inercia/evy
    def _timer_canceled (self, timer):
        """
        A timer has been canceled

        :param timer: the timer that has been canceled
        :return: nothing
        """
        try:
            timer.destroy()
        except (AttributeError, TypeError):
            pass

        try:
            self.timers.remove(timer)
        except KeyError:
            pass
Пример #3
0
    def _timer_canceled(self, timer):
        """
        A timer has been canceled

        :param timer: the timer that has been canceled
        :return: nothing
        """
        try:
            timer.destroy()
        except (AttributeError, TypeError):
            pass

        try:
            self.timers.remove(timer)
        except KeyError:
            pass
Пример #4
0
    def _stopped(self):
        """
        Invoked when the loop has been stopped
        """
        self.running = False
        self.stopping = False

        ## remove all the timers and pollers
        for timer in self.timers:
            timer.destroy()
        for poller in self.pollers.values():
            poller.destroy()
        for callback in self.callbacks:
            callback.destroy()
        self.timers = set()
        self.pollers = {}
        self.callbacks = set()
Пример #5
0
class Hub(object):
    """
    Base hub class for easing the implementation of subclasses that are
    specific to a particular underlying event architecture.
    """

    SYSTEM_EXCEPTIONS = (KeyboardInterrupt, SystemExit)
    SYSTEM_EXCEPTIONS_SIGNUMS = (signal.SIGINT, signal.SIGTERM)
    READ = READ
    WRITE = WRITE

    error_handler = None

    def __init__(self, clock=time.time, ptr=None, default=True):
        """

        :param clock:
        :type clock:
        :param default: default loop
        :type default: boolean
        :param ptr: a pointer a an (optional) libuv loop
        :type ptr: a "uv_loop_t*"
        """
        self.clock = clock
        self.greenlet = greenlet.greenlet(self.run)

        self.stopping = False
        self.running = False

        self.timers = set()
        self.pollers = {}
        self.callbacks = set()

        self.debug_exceptions = True
        self.debug_blocking = False
        self.debug_blocking_resolution = 1

        self.interrupted = False

        if ptr:
            self.uv_loop = ptr
        else:
            if _default_loop_destroyed:
                default = False

            self.uv_sighandlers = set()

            self.uv_loop = pyuv.Loop.default_loop()
            if default:
                #self.uv_signal_checker = pyuv.SignalChecker(self.uv_loop)

                for signum in self.SYSTEM_EXCEPTIONS_SIGNUMS:
                    handler = pyuv.Signal(self.uv_loop)
                    handler.start(self.signal_received, int(signum))
                    self.uv_sighandlers.add(handler)
            #else:
            #    self.uv_signal_checker = None

        if not self.uv_loop:
            raise SystemError("default_loop() failed")

    def block_detect_pre(self):
        # shortest alarm we can possibly raise is one second
        self.block_detect_handle = pyuv.Signal(self.uv_loop)
        self.block_detect_handle.start(alarm_handler, signal.SIGALRM)

    def block_detect_post(self):
        if hasattr(self, "block_detect_handle"):
            self.block_detect_handle.stop()
            del self.block_detect_handle

    def add(self, evtype, fileno, cb, persistent=False):
        """
        Signals an intent to or write a particular file descriptor.

        :param evtype: either the constant READ or WRITE.
        :param fileno: the file number of the file of interest.
        :param cb: callback which will be called when the file is ready for reading/writing.
        """
        if fileno in self.pollers:
            p = self.pollers[fileno]

            current_events = 0

            ## check we do not have another callback on the same descriptor and event
            if p.notify_readable and evtype is READ:
                raise RuntimeError(
                    'there is already %s reading from descriptor %d' %
                    (str(p), fileno))

            if p.notify_writable and evtype is WRITE:
                raise RuntimeError(
                    'there is already %s writing to descriptor %d' %
                    (str(p), fileno))

            p.start(self, current_events | evtype, cb, fileno)
        else:
            p = poller.Poller(fileno, persistent=persistent)
            p.start(self, evtype, cb, fileno)

            ## register the poller
            self.pollers[fileno] = p

        return p

    def remove(self, p):
        """
        Remove a listener
        :param listener: the listener to remove
        """
        self._poller_canceled(p)

    def remove_descriptor(self, fileno, skip_callbacks=False):
        """
        Completely remove all watchers for this *fileno*. For internal use only.
        """
        try:
            p = self.pollers[fileno]
        except KeyError:
            return

        try:
            if not skip_callbacks:
                # invoke the callbacks in the poller and destroy it
                p(READ)
                p(WRITE)
        except self.SYSTEM_EXCEPTIONS:
            self.interrupted = True
        except:
            self.squelch_io_exception(fileno, sys.exc_info())
        finally:
            self._poller_canceled(p)

    def set_timer_exceptions(self, value):
        """
        Debug exceptions

        :param value: True if we want to debug exceptions
        :type value: boolean
        """
        self.debug_exceptions = value

    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):
        """
        Switches to a different greenlet
        :return:
        :rtype:
        """
        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 cede(self):
        """
        Switch temporarily to any other greenlet, and then return to the current greenlet
        :return: the greenlet that takes control
        """
        current = greenlet.getcurrent()
        self.run_callback(current.switch)
        return self.switch()

    def wait(self, seconds=None):
        """
        This timeout will cause us to return from the dispatch() call when we want to

        :param seconds: the amount of seconds to wait
        :type seconds: integer
        """

        if not seconds:
            seconds = self.default_sleep()

        def empty_callback(handle):
            pass

        ## create a timer for avoiding exiting the loop for *seconds*
        timer = pyuv.Timer(empty_callback, seconds, 0)
        timer.start(None)

        try:
            status = self.loop(once=True)
        except self.SYSTEM_EXCEPTIONS:
            self.interrupted = True
        except:
            self.squelch_io_exception(-1, sys.exc_info())

        # we are explicitly ignoring the status because in our experience it's
        # harmless and there's nothing meaningful we could do with it anyway

        timer.stop()

        # raise any signals that deserve raising
        if self.interrupted:
            self.interrupted = False
            raise KeyboardInterrupt()

        if self.debug_blocking:
            self.block_detect_post()

    def default_sleep(self):
        return 10.0

    def sleep_until(self):
        t = self.timers
        if not t:
            return None
        return t[0][0]

    def run(self, *a, **kw):
        """
        Run the loop 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:

                if self.debug_blocking:
                    self.block_detect_pre()

                try:
                    more_events = self.loop(once=True)
                except self.SYSTEM_EXCEPTIONS:
                    self.interrupted = True
                except:
                    self.squelch_io_exception(-1, sys.exc_info())

                if self.debug_blocking:
                    self.block_detect_post()

                ## if there are no active events, just get out of here...
                if not more_events:
                    self.stopping = True

        finally:
            self._stopped()

    def _stopped(self):
        """
        Invoked when the loop has been stopped
        """
        self.running = False
        self.stopping = False

        ## remove all the timers and pollers
        for timer in self.timers:
            timer.destroy()
        for poller in self.pollers.values():
            poller.destroy()
        for callback in self.callbacks:
            callback.destroy()
        self.timers = set()
        self.pollers = {}
        self.callbacks = set()

    def loop(self, once=False):
        """
        Loop the events

        :param once: if True, polls for new events once (and it blocks if there are no pending events)
        :return: 1 if more events are expected, 0 otherwise (when *once* is False, it always returns 0)
        """
        #if self.uv_signal_checker:
        #    self.uv_signal_checker.start()

        if once:
            return self.uv_loop.run(pyuv.UV_RUN_ONCE)
        else:
            return self.uv_loop.run()

    def abort(self, wait=False):
        """
        Stop the loop. If run is executing, it will exit after completing the next loop 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.run_callback(lambda: None)
            # switch to it; when done the hub will switch back to its parent,
            # the main greenlet
            self.switch()

    def destroy(self):
        """
        Destroy the events loop

        :return: None
        """
        global _default_loop_destroyed

        if self.uv_loop:
            self._stopped()

            ## destroy all the signals stuff
            #if self.uv_signal_checker:
            #    for handler in self.uv_sighandlers: handler.stop()
            #    self.uv_signal_checker.stop()

            if self.uv_loop == pyuv.Loop.default_loop():
                _default_loop_destroyed = True

            del self.uv_loop

    @property
    def num_active(self):
        try:
            return self.uv_loop.active_handles
        except AttributeError:
            return 0

    @property
    def counters(self):
        try:
            return self.uv_loop.counters
        except AttributeError:
            return 0

    @property
    def last_error(self):
        try:
            return self.uv_loop.last_err
        except AttributeError:
            return 0

    ##
    ## timers
    ##

    def add_timer(self, timer):
        """
        Add a timer in the hub

        :param timer:
        :param unreferenced: if True, we unreference the timer, so the loop does not wait until it is triggered
        :return:
        """
        eventtimer = pyuv.Timer(self.uv_loop)
        eventtimer.data = timer
        timer.impltimer = eventtimer
        eventtimer.start(self._timer_triggered, float(timer.seconds), 0)
        self.timers.add(timer)

    def _timer_canceled(self, timer):
        """
        A timer has been canceled

        :param timer: the timer that has been canceled
        :return: nothing
        """
        try:
            timer.destroy()
        except (AttributeError, TypeError):
            pass

        try:
            self.timers.remove(timer)
        except KeyError:
            pass

    def _timer_triggered(self, handle):
        """
        Performs the timer trigger

        :param timer: the timer that has been triggered
        :return: nothing
        """
        timer = handle.data
        try:
            timer()
        except self.SYSTEM_EXCEPTIONS:
            self.interrupted = True
        except Exception, e:
            self.squelch_exception(sys.exc_info())

        try:
            timer.destroy()
        except (AttributeError, TypeError):
            pass

        try:
            self.timers.remove(timer)
        except KeyError:
            pass