Refactor shutdown and cleanup logic in OmniRunner and OmniStage to improve process termination and resource management. The changes ensure that stage workers are stopped before closing queues, preventing potential orphaned subprocesses. Additionally, enhanced error handling during process termination provides clearer logging for failed shutdown attempts. This update aims to increase the reliability and stability of the shutdown process across the application.

Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
This commit is contained in:
tzhouam committed 2026-03-17 09:33:28 +00:00
1 parent cc4ade4ba7
commit 004c60a031
4 files changed
+77 -45

No files matched your search

+24 -7
View File
@@ -1911,6 +1911,7 @@ class OmniRunner:
def _cleanup_process(self):
try:
keywords = ["enginecore"]
matched = []
for proc in psutil.process_iter(["pid", "name", "cmdline", "username"]):
try:
@@ -1923,16 +1924,32 @@ class OmniRunner:
if is_process:
print(f"Found vllm process: PID={proc.pid}, cmd={cmdline[:100]}")
try:
proc.terminate()
time.sleep(2)
except Exception:
proc.kill()
matched.append(proc)
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
for proc in matched:
try:
proc.terminate()
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
_, still_alive = psutil.wait_procs(matched, timeout=5)
for proc in still_alive:
try:
proc.kill()
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
if still_alive:
_, stubborn = psutil.wait_procs(still_alive, timeout=3)
if stubborn:
print(f"Warning: failed to kill residual vllm pids: {[p.pid for p in stubborn]}")
else:
print(f"Force-killed residual vllm pids: {[p.pid for p in still_alive]}")
elif matched:
print(f"Terminated vllm pids: {[p.pid for p in matched]}")
except Exception as e:
print(f"Error in psutil vllm cleanup: {e}")
+7 -9
View File
@@ -24,7 +24,7 @@ from vllm_omni.entrypoints.cfg_companion_tracker import CfgCompanionTracker
from vllm_omni.entrypoints.client_request_state import ClientRequestState
from vllm_omni.entrypoints.omni import OmniBase
from vllm_omni.entrypoints.omni_stage import OmniStage
from vllm_omni.entrypoints.stage_utils import SHUTDOWN_TASK, OmniStageTaskType
from vllm_omni.entrypoints.stage_utils import OmniStageTaskType
from vllm_omni.entrypoints.stage_utils import maybe_load_from_ipc as _load
from vllm_omni.entrypoints.utils import (
get_final_stage_id_for_e2e,
@@ -51,11 +51,14 @@ def _weak_close_cleanup_async(
except Exception as e:
logger.warning("Failed to close inline diffusion engine: %s", e)
if stage_list:
for q in stage_in_queues:
# Stop workers first, then close queues. Closing queues early can
# prevent workers from receiving shutdown tasks.
for stage in stage_list:
try:
q.put_nowait(SHUTDOWN_TASK)
stage.stop_stage_worker()
except Exception as e:
logger.warning(f"Failed to send shutdown signal to stage input queue: {e}")
logger.warning(f"Failed to stop stage worker: {e}")
for q in stage_in_queues:
close_fn = getattr(q, "close", None)
if callable(close_fn):
close_fn()
@@ -63,11 +66,6 @@ def _weak_close_cleanup_async(
close_fn = getattr(q, "close", None)
if callable(close_fn):
close_fn()
for stage in stage_list:
try:
stage.stop_stage_worker()
except Exception as e:
logger.warning(f"Failed to stop stage worker: {e}")
try_close_ray(ray_pg)
# Cancel output handler
if output_handler is not None:
+7 -9
View File
@@ -38,7 +38,7 @@ from vllm_omni.distributed.ray_utils.utils import (
)
from vllm_omni.entrypoints.cfg_companion_tracker import CfgCompanionTracker
from vllm_omni.entrypoints.omni_stage import OmniStage
from vllm_omni.entrypoints.stage_utils import SHUTDOWN_TASK, OmniStageTaskType
from vllm_omni.entrypoints.stage_utils import OmniStageTaskType
from vllm_omni.entrypoints.stage_utils import maybe_load_from_ipc as _load
from vllm_omni.entrypoints.utils import (
filter_dataclass_kwargs,
@@ -72,11 +72,14 @@ def _weak_close_cleanup(
):
"""Weak reference cleanup function for OmniBase instances."""
if stage_list:
for q in stage_in_queues:
# Stop workers first, then close queues. Closing queues too early can
# drop shutdown signals and leave engine subprocesses orphaned.
for stage in stage_list:
try:
q.put_nowait(SHUTDOWN_TASK)
stage.stop_stage_worker()
except Exception as e:
logger.warning(f"Failed to send shutdown signal to stage input queue: {e}")
logger.warning(f"Failed to stop stage worker: {e}")
for q in stage_in_queues:
close_fn = getattr(q, "close", None)
if callable(close_fn):
close_fn()
@@ -84,11 +87,6 @@ def _weak_close_cleanup(
close_fn = getattr(q, "close", None)
if callable(close_fn):
close_fn()
for stage in stage_list:
try:
stage.stop_stage_worker()
except Exception as e:
logger.warning(f"Failed to stop stage worker: {e}")
try_close_ray(ray_pg)
# Gracefully shutdown handshake server thread
+39 -20
View File
@@ -616,28 +616,17 @@ class OmniStage:
If graceful shutdown fails, forcefully terminates the process.
Handles both multiprocessing Process and Ray Actor.
"""
if self._in_q is not None:
in_q = self._in_q
out_q = self._out_q
if in_q is not None:
try:
self._in_q.put_nowait(SHUTDOWN_TASK)
# Prefer a blocking put to reduce the chance that shutdown
# is dropped when queue/socket buffers are momentarily full.
in_q.put(SHUTDOWN_TASK)
except Exception as e:
# Queue may already be closed by the weak-ref cleanup
# (e.g. ZmqQueue socket closed) — this is expected.
# Queue may already be closed by cleanup paths.
logger.debug("Failed to send shutdown to in_q: %s", e)
close_fn = getattr(self._in_q, "close", None)
if callable(close_fn):
try:
close_fn()
except Exception:
pass
self._in_q = None
if self._out_q is not None:
close_fn = getattr(self._out_q, "close", None)
if callable(close_fn):
try:
close_fn()
except Exception:
pass
self._out_q = None
if self._ray_actor is not None:
kill_ray_actor(self._ray_actor)
@@ -645,7 +634,7 @@ class OmniStage:
self._ray_task_ref = None
elif self._proc is not None:
try:
self._proc.join(timeout=5)
self._proc.join(timeout=10)
except Exception as e:
logger.debug("join() failed: %s", e)
if self._proc.is_alive():
@@ -653,6 +642,36 @@ class OmniStage:
self._proc.terminate()
except Exception as e:
logger.warning("terminate() failed: %s", e)
try:
self._proc.join(timeout=5)
except Exception as e:
logger.debug("join() after terminate failed: %s", e)
if self._proc.is_alive():
try:
self._proc.kill()
except Exception as e:
logger.warning("kill() failed: %s", e)
try:
self._proc.join(timeout=3)
except Exception as e:
logger.debug("join() after kill failed: %s", e)
if in_q is not None:
close_fn = getattr(in_q, "close", None)
if callable(close_fn):
try:
close_fn()
except Exception:
pass
if out_q is not None:
close_fn = getattr(out_q, "close", None)
if callable(close_fn):
try:
close_fn()
except Exception:
pass
self._in_q = None
self._out_q = None
def submit(self, payload: dict[str, Any]) -> None:
"""Submit a task to the stage worker.