Beispiel #1
0
 def _install_signal_checker(self):
     self._socketpair = SocketPair()
     if hasattr(signal, 'set_wakeup_fd') and os.name == 'posix':
         try:
             old_wakeup_fd = signal.set_wakeup_fd(self._socketpair.writer_fileno())
             if old_wakeup_fd != -1:
                 # Already set, restore it
                 signal.set_wakeup_fd(old_wakeup_fd)
                 self._socketpair.close()
                 self._socketpair = None
             else:
                 self._signal_checker = pyuv.util.SignalChecker(self._loop, self._socketpair.reader_fileno())
                 self._signal_checker.start()
                 self._signal_checker.unref()
         except ValueError:
             self._socketpair.close()
             self._socketpair = None
Beispiel #2
0
class EventLoop(object):
    DEFAULT_EXECUTOR_WORKERS = 100

    def __init__(self):
        global _tls
        if getattr(_tls, 'loop', None) is not None:
            raise RuntimeError('cannot instantiate more than one event loop per thread')
        _tls.loop = self
        self._loop = pyuv.Loop()
        self._loop.excepthook = self._handle_error
        self._loop.event_loop = self
        self._default_executor = None
        self._threadpool = ThreadPool(self)
        self.tasklet = tasklet(self._run_loop)

        self._started = False

        self._fd_map = dict()
        self._signals = dict()
        self._timers = set()
        self._ready = deque()

        self._ready_processor = pyuv.Check(self._loop)
        self._ready_processor.start(self._process_ready)
        self._ready_processor.unref()

        self._ticker = Ticker(self._loop)

        self._waker = pyuv.Async(self._loop, self._ticker.tick)
        self._waker.unref()

        self._install_signal_checker()

    def call_soon(self, callback, *args, **kw):
        handler = Handler(callback, args, kw)
        self._ready.append(handler)
        self._ticker.tick()
        return handler

    def call_from_thread(self, callback, *args, **kw):
        handler = Handler(callback, args, kw)
        self._ready.append(handler)
        self._waker.send()
        return handler

    def call_later(self, delay, callback, *args, **kw):
        if delay <= 0:
            return self.call_soon(callback, *args, **kw)
        timer = pyuv.Timer(self._loop)
        handler = Timer(callback, args, kw, timer)
        timer.handler = handler
        timer.start(self._timer_cb, delay, 0)
        self._timers.add(timer)
        return handler

    def call_repeatedly(self, interval, callback, *args, **kw):
        if interval <= 0:
            raise ValueError('invalid interval specified: {}'.format(interval))
        timer = pyuv.Timer(self._loop)
        handler = Timer(callback, args, kw, timer)
        timer.handler = handler
        timer.start(self._timer_cb, interval, interval)
        self._timers.add(timer)
        return handler

    def run_in_executor(self, executor, callback, *args, **kw):
        assert not isinstance(callback, Handler)
        if executor is None:
            executor = self._default_executor
            if executor is None:
                executor = TaskPoolExecutor(self.DEFAULT_EXECUTOR_WORKERS)
                self._default_executor = executor
        return executor.submit(callback, *args, **kw)

    def set_default_executor(self, executor):
        self._default_executor = executor

    def add_reader(self, fd, callback, *args, **kw):
        handler = Handler(callback, args, kw)
        try:
            poll_h = self._fd_map[fd]
        except KeyError:
            poll_h = self._create_poll_handle(fd)
            self._fd_map[fd] = poll_h
        else:
            if poll_h.read_handler:
                raise RuntimeError('another reader is already registered for fd {}'.format(fd))
            poll_h.stop()

        poll_h.pevents |= pyuv.UV_READABLE
        poll_h.read_handler = handler
        poll_h.start(poll_h.pevents, self._poll_cb)

        return handler

    def remove_reader(self, fd):
        try:
            poll_h = self._fd_map[fd]
        except KeyError:
            return False
        else:
            poll_h.stop()
            poll_h.pevents &= ~pyuv.UV_READABLE
            poll_h.read_handler = None
            if poll_h.pevents == 0:
                poll_h.close()
                del self._fd_map[fd]
            else:
                poll_h.start(poll_h.pevents, self._poll_cb)
            return True

    def add_writer(self, fd, callback, *args, **kw):
        handler = Handler(callback, args, kw)
        try:
            poll_h = self._fd_map[fd]
        except KeyError:
            poll_h = self._create_poll_handle(fd)
            self._fd_map[fd] = poll_h
        else:
            if poll_h.write_handler:
                raise RuntimeError('another writer is already registered for fd {}'.format(fd))
            poll_h.stop()

        poll_h.pevents |= pyuv.UV_WRITABLE
        poll_h.write_handler = handler
        poll_h.start(poll_h.pevents, self._poll_cb)

        return handler

    def remove_writer(self, fd):
        try:
            poll_h = self._fd_map[fd]
        except KeyError:
            return False
        else:
            poll_h.stop()
            poll_h.pevents &= ~pyuv.UV_WRITABLE
            poll_h.write_handler = None
            if poll_h.pevents == 0:
                poll_h.close()
                del self._fd_map[fd]
            else:
                poll_h.start(poll_h.pevents, self._poll_cb)
            return True

    def add_signal_handler(self, sig, callback, *args, **kwargs):
        self._validate_signal(sig)
        signal_h = pyuv.Signal(self._loop)
        handler = SignalHandler(callback, args, kwargs, signal_h)
        signal_h.handler = handler
        signal_h.signum = sig
        try:
            signal_h.start(self._signal_cb, sig)
            signal_h.unref()
        except Exception as e:
            signal_h.close()
            raise RuntimeError(str(e))
        else:
            self._signals.setdefault(sig, set()).add(signal_h)
        return handler

    def remove_signal_handler(self, sig):
        self._validate_signal(sig)
        try:
            handles = self._signals.pop(sig)
        except KeyError:
            return False
        for signal_h in handles:
            del signal_h.handler
            signal_h.close()
        return True

    def switch(self):
        if not self._started:
            self._run(forever=False)
            return
        current = get_current()
        assert current is not self.tasklet, 'Cannot switch to MAIN from MAIN'
        try:
            if self.tasklet.parent is not current:
                current.parent = self.tasklet
        except ValueError:
            pass  # gets raised if there is a tasklet parent cycle
        return self.tasklet.switch()

    def run(self):
        self._run(forever=False)

    def run_forever(self):
        self._run(forever=True)

    def stop(self):
        if not self._started:
            raise RuntimeError('event loop has not been started yet')
        if self._loop:
            self._loop.stop()

    def destroy(self):
        global _tls
        try:
            loop = _tls.loop
        except AttributeError:
            return
        else:
            if loop is not self:
                raise RuntimeError('destroy() can only be called from the same thread were the event loop was created')
            del _tls.loop, loop

        self._uninstall_signal_checker()

        self._cleanup_loop()
        self._loop.event_loop = None
        self._loop.excepthook = None
        self._loop = None
        self._default_executor = None
        self._threadpool = None

        self._ready_processor = None
        self._ticker = None
        self._waker = None

        self._fd_map.clear()
        self._signals.clear()
        self._timers.clear()
        self._ready.clear()

    # internal

    def _handle_error(self, typ, value, tb):
        if not issubclass(typ, (TaskletExit, SystemExit)):
            traceback.print_exception(typ, value, tb)
        if issubclass(typ, (KeyboardInterrupt, SystemExit, SystemError)):
            current = get_current()
            assert current is self.tasklet
            self.tasklet.parent.throw(typ, value)

    def _run(self, forever):
        current = get_current()
        if current is not self.tasklet.parent:
            raise RuntimeError('run() can only be called from MAIN tasklet')
        if self.tasklet.dead:
            raise RuntimeError('event loop has already ended')
        if self._started:
            raise RuntimeError('event loop was already started')
        self._started = True
        self.tasklet.switch(forever=forever)

    def _run_loop(self, forever=False):
        if forever:
            handler = self.call_repeatedly(24*3600, lambda: None)
        try:
            self._loop.run(pyuv.UV_RUN_DEFAULT)
        finally:
            if forever:
                handler.cancel()

    def _cleanup_loop(self):
        def cb(handle):
            if not handle.closed:
                handle.close()
        self._loop.walk(cb)
        # All handles are now closed, run will not block
        self._loop.run(pyuv.UV_RUN_NOWAIT)

    def _create_poll_handle(self, fd):
        poll_h = pyuv.Poll(self._loop, fd)
        poll_h.pevents = 0
        poll_h.read_handler = None
        poll_h.write_handler = None
        return poll_h

    def _process_ready(self, handle):
        # Run all queued callbacks
        ntodo = len(self._ready)
        for x in range(ntodo):
            handler = self._ready.popleft()
            # loop.excepthook takes care of exception handling
            handler()
        if not self._ready:
            self._ticker.stop()

    def _timer_cb(self, timer):
        assert not timer.handler.cancelled
        self._ready.append(timer.handler)
        if not timer.repeat:
            timer.close()
            self._timers.remove(timer)
            del timer.handler

    def _signal_cb(self, signal_h, signum):
        self._ready.append(signal_h.handler)

    def _poll_cb(self, poll_h, events, error):
        fd = poll_h.fileno()
        if error is not None:
            # An error happened, signal both readability and writability and
            # let the error propagate
            if poll_h.read_handler is not None:
                if poll_h.read_handler.cancelled:
                    self.remove_reader(fd)
                else:
                    self._ready.append(poll_h.read_handler)
            if poll_h.write_handler is not None:
                if poll_h.write_handler.cancelled:
                    self.remove_writer(fd)
                else:
                    self._ready.append(poll_h.write_handler)
            return

        old_events = poll_h.pevents
        modified = False

        if events & pyuv.UV_READABLE:
            if poll_h.read_handler is not None:
                if poll_h.read_handler.cancelled:
                    self.remove_reader(fd)
                    modified = True
                else:
                    self._ready.append(poll_h.read_handler)
            else:
                poll_h.pevents &= ~pyuv.UV_READABLE
        if events & pyuv.UV_WRITABLE:
            if poll_h.write_handler is not None:
                if poll_h.write_handler.cancelled:
                    self.remove_writer(fd)
                    modified = True
                else:
                    self._ready.append(poll_h.write_handler)
            else:
                poll_h.pevents &= ~pyuv.UV_WRITABLE

        if not modified and old_events != poll_h.pevents:
            # Rearm the handle
            poll_h.stop()
            poll_h.start(poll_h.pevents, self._poll_cb)

    def _install_signal_checker(self):
        self._socketpair = SocketPair()
        if hasattr(signal, 'set_wakeup_fd') and os.name == 'posix':
            try:
                old_wakeup_fd = signal.set_wakeup_fd(self._socketpair.writer_fileno())
                if old_wakeup_fd != -1:
                    # Already set, restore it
                    signal.set_wakeup_fd(old_wakeup_fd)
                    self._socketpair.close()
                    self._socketpair = None
                else:
                    self._signal_checker = pyuv.util.SignalChecker(self._loop, self._socketpair.reader_fileno())
                    self._signal_checker.start()
                    self._signal_checker.unref()
            except ValueError:
                self._socketpair.close()
                self._socketpair = None

    def _uninstall_signal_checker(self):
        if self._socketpair:
            self._signal_checker.close()
            self._signal_checker = None
            self._socketpair.close()
            self._socketpair = None

    def _validate_signal(self, sig):
        if not isinstance(sig, int):
            raise TypeError('sig must be an int, not {!r}'.format(sig))
        if signal is None:
            raise RuntimeError('Signals are not supported')
        if not (1 <= sig < signal.NSIG):
            raise ValueError('sig {} out of range(1, {})'.format(sig, signal.NSIG))
        if sys.platform == 'win32':
            raise RuntimeError('Signals are not really supported on Windows')