diff --git a/freqtrade/tests/test_watchdog.py b/freqtrade/tests/test_watchdog.py index 4d1eae1da..d795e777b 100644 --- a/freqtrade/tests/test_watchdog.py +++ b/freqtrade/tests/test_watchdog.py @@ -16,3 +16,24 @@ def test_watchdog_kill(caplog): log = ["Watchdog started", "Watchdog stopped"] for line in log: assert line in caplog.text + + +def test_try_kill_failed(mocker): + mocker.patch("os.kill") + mocker.patch("os.waitpid", return_value=(0, 0)) + watchdog = Watchdog(1, 1) + assert watchdog.try_kill(0) is False + + +def test_try_kill_success(mocker): + mocker.patch("os.kill") + mocker.patch("os.waitpid", return_value=(0, 1)) + watchdog = Watchdog(1, 1) + assert watchdog.try_kill(0) is True + + +def test_try_kill_error(mocker): + mocker.patch("os.kill") + mocker.patch("os.waitpid", side_effect=OSError) + watchdog = Watchdog(1, 1) + assert watchdog.try_kill(0) is True diff --git a/freqtrade/watchdog.py b/freqtrade/watchdog.py index ead74ce5f..aa3de8c54 100644 --- a/freqtrade/watchdog.py +++ b/freqtrade/watchdog.py @@ -7,6 +7,7 @@ from multiprocessing import Value logger = logging.getLogger('freqtrade.watchdog') WATCHDOG_TIMEOUT = 300 +KILL_TIMEOUT = 60 class Watchdog: @@ -14,8 +15,9 @@ class Watchdog: shared_heartbeat = Value('d', 0.0) kill_signal = None - def __init__(self, timeout=WATCHDOG_TIMEOUT): + def __init__(self, timeout=WATCHDOG_TIMEOUT, kill_timeout=KILL_TIMEOUT): self.timeout = timeout + self.kill_timeout = kill_timeout self.heartbeat() def heartbeat(self) -> None: @@ -26,11 +28,27 @@ class Watchdog: logger.warning("Kill signal: {}".format(signum)) self.kill_signal = signum + def try_kill(self, pid): + os.kill(pid, signal.SIGINT) + for count in range(0, self.kill_timeout): + try: + pid, err_code = os.waitpid(pid, os.WNOHANG) + if pid != 0 or err_code != 0: + return True + time.sleep(1) + except OSError: + return True + return False + def kill(self, pid): logger.info("Stopping pid {}".format(pid)) if pid: - os.kill(pid, signal.SIGTERM) # Better use sigint and then sigterm? - os.wait() + if self.try_kill(pid): + logger.info("Process finished gracefully") + else: + logger.warning("Process not responded, kill by SIGTERM") + os.kill(pid, signal.SIGTERM) + os.wait() def start(self) -> bool: pid = os.fork()