blob: aeaa5fa9158e077e120651085b102f9d3158d09c [file] [log] [blame]
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import annotations
import contextlib
import logging
from subprocess import Popen, TimeoutExpired
from typing import TYPE_CHECKING, Final, Optional
import psutil
if TYPE_CHECKING:
from crossbench.plt.base import Platform, ProcessLike
from crossbench.plt.signals import Signals
PROCESS_NOT_FOUND_EXCEPTIONS: Final = (psutil.NoSuchProcess,
psutil.AccessDenied,
psutil.ZombieProcess, ProcessLookupError)
def terminate_gracefully(platform: Platform,
process: ProcessLike,
timeout: int = 1,
signal: Optional[Signals] = None) -> None:
"""Graceful process termination
1. Send the provided signal or SIGTERM by default
2. Wait for the process to terminate
3. Kill the process
"""
if not signal:
signal = platform.signals.SIGTERM
try:
platform.send_signal(process, signal)
# TODO(392938079): support timeout on more process types
if isinstance(process, Popen):
process.wait(timeout)
return
except TimeoutExpired as e:
logging.debug("Got timeout while waiting "
"for process shutdown (%s): %s", process, e)
except PROCESS_NOT_FOUND_EXCEPTIONS as e: # noqa: BLE001
logging.debug("Ignoring exception during process termination: %s", e)
finally:
with contextlib.suppress(*PROCESS_NOT_FOUND_EXCEPTIONS):
platform.kill(process)