[Refactor] Refactor Diffusion Scheduler/Executor Boundaries and Request State Flow (#1625)

Signed-off-by: jader <yjader@foxmail.com>
Signed-off-by: JiangJie Zhang <76905040+yJader@users.noreply.github.com>
Co-authored-by: SYLAR <125541396+lishunyang12@users.noreply.github.com>
Co-authored-by: Didan Deng <33117903+wtomin@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-03-23 20:25:32 +08:00
1 parent 5a58a1aac0
commit 61cd532cff
13 files changed
+1089 -326

No files matched your search

@@ -4,12 +4,14 @@ import weakref
from dataclasses import dataclass
from typing import Any
import zmq
from vllm.distributed.device_communicators.shm_broadcast import MessageQueue
from vllm.logger import init_logger
from vllm_omni.diffusion.data import SHUTDOWN_MESSAGE, DiffusionOutput
from vllm_omni.diffusion.executor.abstract import DiffusionExecutor
from vllm_omni.diffusion.ipc import unpack_diffusion_output_shm
from vllm_omni.diffusion.request import OmniDiffusionRequest
from vllm_omni.diffusion.scheduler import Scheduler
from vllm_omni.diffusion.worker import WorkerProc
logger = init_logger(__name__)
@@ -21,18 +23,23 @@ class BackgroundResources:
Used as a finalizer for clean shutdown.
"""
scheduler: Scheduler | None = None
broadcast_mq: MessageQueue | None = None
result_mq: MessageQueue | None = None
num_workers: int = 0
processes: list[mp.Process] | None = None
def __call__(self):
"""Clean up background resources."""
if self.scheduler is not None:
if self.broadcast_mq is not None:
try:
for _ in range(self.scheduler.num_workers):
self.scheduler.mq.enqueue(SHUTDOWN_MESSAGE)
self.scheduler.close()
for _ in range(self.num_workers):
self.broadcast_mq.enqueue(SHUTDOWN_MESSAGE)
self.broadcast_mq = None
self.result_mq = None
except Exception as exc:
logger.warning("Failed to send shutdown signal: %s", exc)
if self.processes:
for proc in self.processes:
if not proc.is_alive():
@@ -51,24 +58,42 @@ class MultiprocDiffusionExecutor(DiffusionExecutor):
self._processes: list[mp.Process] = []
self._closed = False
# Initialize scheduler
self.scheduler = Scheduler()
self.scheduler.initialize(self.od_config)
broadcast_handle = self.scheduler.get_broadcast_handle()
num_workers = self.od_config.num_gpus
self._broadcast_mq = self._init_broadcast_queue(num_workers)
broadcast_handle = self._broadcast_mq.export_handle()
# Launch workers
processes, result_handle = self._launch_workers(broadcast_handle)
if result_handle is not None:
self.scheduler.initialize_result_queue(result_handle)
else:
logger.error("Failed to get result queue handle from workers")
self._result_mq = self._init_result_queue(result_handle)
self._processes = processes
self.resources = BackgroundResources(scheduler=self.scheduler, processes=self._processes)
self.resources = BackgroundResources(
broadcast_mq=self._broadcast_mq,
result_mq=self._result_mq,
num_workers=num_workers,
processes=self._processes,
)
self._finalizer = weakref.finalize(self, self.resources)
def _init_broadcast_queue(self, num_workers: int) -> MessageQueue:
return MessageQueue(
n_reader=num_workers,
n_local_reader=num_workers,
local_reader_ranks=list(range(num_workers)),
)
def _init_result_queue(self, result_handle) -> MessageQueue | None:
if result_handle is None:
logger.error("Failed to get result queue handle from workers")
return None
return MessageQueue.create_from_handle(result_handle, 0)
def _ensure_open(self) -> None:
if self._closed:
raise RuntimeError("DiffusionExecutor is closed.")
if self._result_mq is None:
raise RuntimeError("Result queue not initialized")
def _launch_workers(self, broadcast_handle):
od_config = self.od_config
logger.info("Starting server...")
@@ -134,7 +159,36 @@ class MultiprocDiffusionExecutor(DiffusionExecutor):
return processes, result_handle
def add_req(self, request: OmniDiffusionRequest) -> DiffusionOutput:
return self.scheduler.add_req(request)
self._ensure_open()
rpc_request = {
"type": "rpc",
"method": "generate",
"args": (request,),
"kwargs": {},
"output_rank": 0,
"exec_all_ranks": True,
}
try:
self._broadcast_mq.enqueue(rpc_request)
response = self._result_mq.dequeue()
try:
unpack_diffusion_output_shm(response)
except Exception as e:
logger.warning("SHM unpack failed (data may already be inline): %s", e)
if isinstance(response, dict) and response.get("status") == "error":
raise RuntimeError(
f"Worker failed with error '{response.get('error')}', "
"please check the stack trace above for the root cause"
)
if not isinstance(response, DiffusionOutput):
raise RuntimeError(f"Unexpected response type for generate: {type(response)!r}")
return response
except Exception as e:
logger.error(f"Generate call failed: {e}")
raise
def collective_rpc(
self,
@@ -144,8 +198,7 @@ class MultiprocDiffusionExecutor(DiffusionExecutor):
kwargs: dict | None = None,
unique_reply_rank: int | None = None,
) -> Any:
if self._closed:
raise RuntimeError("DiffusionExecutor is closed.")
self._ensure_open()
deadline = None if timeout is None else time.monotonic() + timeout
kwargs = kwargs or {}
@@ -160,44 +213,32 @@ class MultiprocDiffusionExecutor(DiffusionExecutor):
}
try:
# Acquire lock with timeout awareness so that a stalled add_req
# (holding the lock while blocked on dequeue) does not prevent
# this RPC from honouring its own timeout.
lock_timeout = None if deadline is None else max(0, deadline - time.monotonic())
acquired = self.scheduler._lock.acquire(timeout=lock_timeout if lock_timeout is not None else -1)
if not acquired:
raise TimeoutError(f"RPC call to {method} timed out waiting for scheduler lock.")
try:
# Broadcast RPC request to all workers via unified message queue
self.scheduler.mq.enqueue(rpc_request)
# Broadcast RPC request to all workers via unified message queue
self._broadcast_mq.enqueue(rpc_request)
# Determine which workers we expect responses from
num_responses = 1 if unique_reply_rank is not None else self.od_config.num_gpus
# Determine which workers we expect responses from
num_responses = 1 if unique_reply_rank is not None else self.od_config.num_gpus
responses = []
for _ in range(num_responses):
dequeue_timeout = None if deadline is None else max(0, deadline - time.monotonic())
try:
if self.scheduler.result_mq is None:
raise RuntimeError("Result queue not initialized")
responses = []
for _ in range(num_responses):
dequeue_timeout = None if deadline is None else max(0, deadline - time.monotonic())
try:
response = self._result_mq.dequeue(timeout=dequeue_timeout)
response = self.scheduler.result_mq.dequeue(timeout=dequeue_timeout)
# Check if response indicates an error
if isinstance(response, dict) and response.get("status") == "error":
raise RuntimeError(
f"Worker failed with error '{response.get('error')}', "
"please check the stack trace above for the root cause"
)
# Check if response indicates an error
if isinstance(response, dict) and response.get("status") == "error":
raise RuntimeError(
f"Worker failed with error '{response.get('error')}', "
"please check the stack trace above for the root cause"
)
responses.append(response)
except TimeoutError as e:
raise TimeoutError(f"RPC call to {method} timed out.") from e
return responses[0] if unique_reply_rank is not None else responses
finally:
self.scheduler._lock.release()
responses.append(response)
except zmq.error.Again as e:
raise TimeoutError(f"RPC call to {method} timed out.") from e
except TimeoutError as e:
raise TimeoutError(f"RPC call to {method} timed out.") from e
return responses[0] if unique_reply_rank is not None else responses
except Exception as e:
logger.error(f"RPC call failed: {e}")
raise
@@ -210,4 +251,10 @@ class MultiprocDiffusionExecutor(DiffusionExecutor):
def shutdown(self) -> None:
self._closed = True
self._finalizer()
try:
self._finalizer()
finally:
self._broadcast_mq = None
self._result_mq = None
self.resources = None
self._processes = []