Ejemplo n.º 1
0
            def exec_under_watch(process_args: List[str],
                                 **kwargs: Any) -> Optional[Popen]:
                msg = (
                    "The Janitor can not work with `shell=True`. When that flag "
                    "is used a proxy shell process is used to start the real "
                    "target process, the result is that the Janitor will monitor "
                    "and kill the proxy shell instead of the target, once the "
                    "shell is killed the real targets are kept around as "
                    "orphans, which is exactly what the Janitor is trying to "
                    "prevent from happening.")
                assert not kwargs.get("shell", False), msg

                def subprocess_stopped(result: AsyncResult) -> None:
                    if janitor._exit_in_progress:
                        # During __exit__ we expect processes to stop, since
                        # they are killed by the janitor.
                        return

                    with janitor._processes_lock:
                        # Processes are expected to quit while the nursery is
                        # active, remove them from the track list to clear memory
                        janitor._processes.remove(process)

                        # if the subprocess error'ed propagate the error.
                        try:
                            exit_code = result.get()
                            if exit_code != STATUS_CODE_FOR_SUCCESS:
                                log.error(
                                    "Process died! Bailing out.",
                                    args=process.args,
                                    exit_code=exit_code,
                                )
                                exception = SystemExit(exit_code)
                                janitor._stop.set_exception(exception)
                        except Exception as exception:
                            janitor._stop.set_exception(exception)

                with janitor._processes_lock:
                    if janitor._stop.ready():
                        return None

                    process = Popen(process_args, **kwargs)
                    janitor._processes.add(process)

                    # `rawlink`s are executed from within the hub, the problem
                    # is that locks can not be acquire at that point.
                    # SpawnedLink creates a new greenlet to run the callback to
                    # circumvent that.
                    callback = SpawnedLink(subprocess_stopped)

                    process.result.rawlink(callback)

                    # Important: `stop` may be set after Popen started, but before
                    # it returned. If that happens `GreenletExit` exception is
                    # raised here. In order to have proper cleared, exceptions have
                    # to be handled and the process installed.
                    if janitor._stop.ready():
                        process.send_signal(signal.SIGINT)

                    return process
Ejemplo n.º 2
0
    def watch(self, callback):
        current = greenlet.getcurrent()
        tid = self.get()

        if hasattr(current, 'link'):
            # This is a Gevent Greenlet (capital G), which inherits from
            # greenlet and provides a 'link' method to detect when the
            # Greenlet exits.
            link = SpawnedLink(callback)
            current.rawlink(link)
            self._refs[tid] = link
        else:
            # This is a non-Gevent greenlet (small g), or it's the main
            # greenlet.
            self._refs[tid] = weakref.ref(current, callback)
Ejemplo n.º 3
0
            def spawn_under_watch(function: Callable, *args: Any,
                                  **kwargs: Any) -> Greenlet:
                greenlet = gevent.spawn(function, *args, **kwargs)

                def kill_grenlet(_result: AsyncResult) -> None:
                    # gevent.GreenletExit must **not** be used here, since that
                    # exception is considered a successful run and it is not
                    # re-raised on calls to `get`
                    exception = RuntimeError("Janitor is stopping")
                    greenlet.throw(exception)

                # The Event.rawlink is executed inside the Hub thread, which
                # does validation and *raises on blocking calls*, to go around
                # this a new greenlet has to be spawned, that in turn will
                # raise the exception.
                callback = SpawnedLink(kill_grenlet)

                janitor._stop.rawlink(callback)
                return greenlet