Files
vllm-omni/vllm_omni/engine/resource_ledger.py
T
tzhouam 723c550209 Integrate portable edge graph backends with Omni runtime
Extract versioned stage contracts, model shared CPU/iGPU/NPU memory constraints, and add explicit graph routes, reservations, output acknowledgements, and worker retirement. Fix native Windows shared-buffer lifetime and host paths. Document qualified hardware paths and remaining rollout gates.
2026-09-22 13:12:33 +08:00

102 lines
4.1 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Atomic reservations shared by native and external stage startup.
Capacities are admission ceilings for this controller, captured before loads.
They are not fresh `available` samples: resident allocations remain charged,
so sampling available memory cannot subtract our own allocation a second time.
Refresh ceilings only with an explicit, reconciled controller budget.
"""
from __future__ import annotations
import threading
from collections.abc import Mapping
from dataclasses import dataclass
from types import MappingProxyType
class ResourceUnavailable(RuntimeError): # noqa: N818 - public admission result, like PlanNotAdmitted
pass
@dataclass(frozen=True)
class Reservation:
owner: str
demands: Mapping[str, int]
class ResourceLedger:
def __init__(self, capacities: Mapping[str, int]) -> None:
if not capacities or any(type(n) is not int or n < 0 for n in capacities.values()):
raise ValueError("explicit nonnegative memory ceilings are required")
self.capacities = MappingProxyType(dict(capacities))
self._active: dict[str, Reservation] = {}
self._quarantined: set[str] = set()
self._lock = threading.RLock()
def reserve(self, owner: str, demands: Mapping[str, int]) -> Reservation:
with self._lock:
if not owner or owner in self._active:
raise ValueError(f"reservation owner missing or already active: {owner}")
if not demands or any(type(n) is not int or n < 0 for n in demands.values()):
raise ValueError("nonnegative memory demands are required")
for pool, count in demands.items():
if pool not in self.capacities:
raise ResourceUnavailable(f"unknown memory pool: {pool}")
used = sum(r.demands.get(pool, 0) for r in self._active.values())
if used + count > self.capacities[pool]:
raise ResourceUnavailable(f"{pool}: {used} reserved + {count} requested > {self.capacities[pool]}")
reservation = Reservation(owner, MappingProxyType(dict(demands)))
self._active[owner] = reservation
return reservation
def release(self, reservation: Reservation, *, drained: bool) -> bool:
with self._lock:
if self._active.get(reservation.owner) is not reservation:
return False # stale token must never release a newer allocation
if not drained:
self._quarantined.add(reservation.owner)
return False
del self._active[reservation.owner]
self._quarantined.discard(reservation.owner)
return True
def snapshot(self) -> dict:
with self._lock:
return {
"capacities": dict(self.capacities),
"reserved": {p: sum(r.demands.get(p, 0) for r in self._active.values()) for p in self.capacities},
"owners": list(self._active),
"quarantined": sorted(self._quarantined),
}
class GraphRequestGate:
"""One admitted pipeline request, held through the final consumer ACK.
Stage reservations already include its payload allowance. Rejecting excess
work before the ingress queue keeps slow consumers from accumulating inputs
or error outputs, and leaves the existing control queue available to abort.
"""
def __init__(self):
self._lock = threading.Lock()
self._active = None
def acquire(self, request_id):
with self._lock:
if self._active is not None:
raise ResourceUnavailable("graph pipeline capacity is one; consume or cancel the active request first")
ticket = (request_id, object())
self._active = ticket
return ticket
def current(self, request_id):
with self._lock:
return self._active if self._active is not None and self._active[0] == request_id else None
def release(self, ticket):
with self._lock:
if ticket is not None and self._active is ticket:
self._active = None