Skip to content

vllm_omni.core.prefix_cache.controller

Runs WriteTasks and the step device→host staging pool.

The manager owns request/slot identity and when to submit. This class owns the staging pool, the GPU-byte budget, the copy queues, and the single committer that writes into the CPU block pool.

Two device→host paths — the step path does not write chunk.host in the committer:

JOIN_NEXT_STEP   save already launched a whole-step device→host into
                 a staging slot and set `chunk.host` as views.
                 Committer waits that `step_d2h_event`, then writes
                 the pool.
JOIN_ON_FINISH   committer copies the device clone → owned host
                 tensors, then writes the pool.

Async: high-priority then low-priority queues, then pool write. Eager: submit() does wait+write inline (CPU tests / no CUDA).

logger module-attribute

logger = logging.getLogger(__name__)

OmniPrefixCacheController

Staging pool + committer. Step device→host is launched at save; this thread waits that event (JOIN_NEXT_STEP) or copies deferred rows (JOIN_ON_FINISH), then writes into the CPU pool.

append_chunk

append_chunk(
    task: WriteTask,
    chunk: _WriteChunk,
    freeze_event: Event | None = None,
) -> TaskState | None

Append to a pending task. None when appended, else the closing state.

dispatch

dispatch(tasks: list[WriteTask]) -> None

Hand registered, queued tasks to the copy path. Threaded: enqueue. Eager: the copy + pool write run here, inline — never call this under the manager's state lock.

drain_completed

drain_completed() -> list[int]

Pop pool-written tasks from _completed and drop them from _tasks. Staging holders were already released at the pool write.

drain_failed

drain_failed() -> list[int]

Pop failed task ids from _failed. Does not drop _tasks.

escalate

escalate(tids: list[int]) -> None

Move pending tasks to the front of the high-priority queue.

PENDING -> QUEUED at the head; QUEUED on the low-priority queue moves up; QUEUED already high-priority is a no-op. COPYING and later belong to the worker and are untouched: the worker claims under _wake too, so a task cannot be popped and re-queued behind its back.

fetch_host

fetch_host(
    task: WriteTask, slots: Tensor, key: str
) -> Tensor

Rows for slots of one not-yet-done JOIN_ON_FINISH task.

_slot_ref puts JOIN_NEXT_STEP tids in join_tids (wait then pool). This path reads committer-written chunk.host, or the device clone if that device→host has not landed.

get_task

get_task(tid: int) -> WriteTask | None

in_flight_tasks

in_flight_tasks() -> int

Registered tasks not yet drained (diagnostics only).

join

join(tids: list[int]) -> None

Block until each task has finished the CPU-pool write (or failed).

join_host_ready

join_host_ready(tids: list[int]) -> None

Block until each task's device→host is complete (host_ready).

Staging: committer has waited step_d2h_event. Deferred: committer has written chunk.host. Does not wait for the CPU-pool write.

pin_budget

pin_budget(ticket: _BudgetTicket, tid: int) -> None

Record that tid holds a view of the clone ticket charges.

register

register(task: WriteTask, queued: bool = True) -> None

Make a task visible (registry + QUEUED) without running anything; safe under the manager's state lock. queued=False (deferred tasks) stays PENDING on the GPU clone until finish/abort or the GPU-byte budget forces a copy. Queued tasks must then go through dispatch.

Caller must reserve() the task bytes first (budget flush can block; the manager does that outside the state lock) and pin the task on its budget ticket(s) before register.

reserve

reserve(nbytes: int) -> None

Reserve GPU-clone bytes; blocking flush happens here, so callers must not hold the manager's state lock.

shutdown

shutdown() -> None

stage_step_host

stage_step_host(
    tensors: dict[str, Tensor],
    n: int,
    freeze_event: Event | None,
    step_holder: StagingBufferHolder,
) -> StepD2HClaim

Claim a staging slot and, when tensors is non-empty, launch ONE whole-step device→host into it.

Leftover-only saves pass empty tensors and still take a slot (empty views) so every step id shares this bound. A full pool waits for materialize/discard; timeout then errors. A step larger than the page overflows the next slot — that is a config break. The caller binds tasks after submit; step_holder is released by materialize/discard via staging_release.

staging_bind

staging_bind(
    slot: int, holder: StagingBufferHolder
) -> None

staging_release

staging_release(
    slot: int, holder: StagingBufferHolder
) -> None

submit

submit(task: WriteTask, queued: bool = True) -> None

register + dispatch in one call (callers not holding the state lock).

StagingBufferHolder

Bases: NamedTuple

One owner of a staging-buffer slot. The slot is free when none remain.

Not a buffer state — concurrent owners share the same slot: - for_step: claimed at save, released when materialize/discard consumes the ctx - for_task: bound before WriteTask submit, released when that task completes

kind instance-attribute

kind: Literal['step', 'task']

owner_id instance-attribute

owner_id: int

for_step classmethod

for_step(step_id: int) -> StagingBufferHolder

for_task classmethod

for_task(tid: int) -> StagingBufferHolder

StagingBufferPool

Reusable pinned landing zone for ONE whole-step device→host at save.

Per-task chunk.host is a row-range view into a slot, so the committer skips a per-task device→host. Slots recycle; this is not the CPU block pool.

A slot stays busy while anyone still holds it: the step (until materialize/discard) and each immediate write that views the page (until its pool write). Prefix hits do not hold a slot — they wait for the pool write and read the durable pool.

Saves with only leftover mm still claim a slot (empty views) so every step id shares this bound. A full pool waits; timeout then errors.

capacity instance-attribute

capacity = capacity

depth instance-attribute

depth = depth

bind

bind(slot: int, holder: StagingBufferHolder) -> None

claim

claim(holder: StagingBufferHolder, timeout: float) -> int

Grab a free slot for holder. Waits until one is free, then times out. timeout<=0 fails immediately if none are free.

close

close() -> None

release

release(slot: int, holder: StagingBufferHolder) -> None

views

views(
    slot: int,
    key: str,
    n: int,
    width: int,
    dtype: dtype,
    pin: bool,
) -> Tensor

StepD2HClaim dataclass

One whole-step landing in StagingBufferPool.

Return of stage_step_host. The manager stores this on _StepContext until materialize/discard releases the step holder.

event class-attribute instance-attribute

event: Event | None = None

staging_slot instance-attribute

staging_slot: int

views instance-attribute

views: dict[TensorName, Tensor]

TaskState

Bases: Enum

WriteTask lifecycle: a strict chain, plus FAILED from any non-terminal.

PENDING -> QUEUED -> COPYING -> HOST_READY -> WRITTEN

PENDING registered, not on a copy queue (deferred: waiting for finish / budget pressure) QUEUED on a copy queue COPYING one thread owns the copy stage (staging: waits the step device→host; deferred: copies the device clone) HOST_READY host rows ready (host_ready set); device clone dropped WRITTEN in the CPU pool (done set) FAILED committer could not finish (host_ready + done set; manager raises from every later entry)

Moves only through WriteTask.transition; skipping a step is illegal.

COPYING class-attribute instance-attribute

COPYING = auto()

FAILED class-attribute instance-attribute

FAILED = auto()

HOST_READY class-attribute instance-attribute

HOST_READY = auto()

PENDING class-attribute instance-attribute

PENDING = auto()

QUEUED class-attribute instance-attribute

QUEUED = auto()

WRITTEN class-attribute instance-attribute

WRITTEN = auto()

is_terminal property

is_terminal: bool

WriteTask dataclass

One write of (slot, key) rows for a single request.

Identity: tid is the handle in the manager's (slot, key) tables. req_id + write_n mark whose write this is and the nth time that request opened a write. One write may cover several keys.

state is the single source of truth for where the write is (see TaskState). host_ready / done are wait primitives set by the transitions into HOST_READY / WRITTEN / FAILED, never directly.

How HOST_READY is reached: - JOIN_NEXT_STEP: chunk.host is a staging view set at save; the copy stage only waits step_d2h_event. - JOIN_ON_FINISH: the copy stage copies device→host into chunk.host.

JOIN_NEXT_STEP is queued at submit. JOIN_ON_FINISH stays PENDING until finish or GPU-byte-budget pressure escalates it; budget flush takes the unfinished task with the oldest enqueued_time.

Concurrent readers/writers: - Staging readers (materialize clone, committer pool write) all wait the same step_d2h_event before touching the view. - A later task taking the same (slot, key) records those rows in reassigned; the old pool write skips them. - append_chunk is refused once the copy stage is claimed; the caller opens a fresh task rather than mutating a closed one. - lock covers state / reassigned / append_chunk / host↔freeze / slot_to_row. scatter_rows snapshots reassigned.

chunks instance-attribute

chunks: list[_WriteChunk]

done class-attribute instance-attribute

done: Event = field(default_factory=threading.Event)

enqueued_time class-attribute instance-attribute

enqueued_time: float = field(default_factory=time.monotonic)

freeze_event class-attribute instance-attribute

freeze_event: Event | None = None

host_ready class-attribute instance-attribute

host_ready: Event = field(default_factory=threading.Event)

is_terminal property

is_terminal: bool

lock class-attribute instance-attribute

lock: Lock = field(default_factory=threading.Lock)

reassigned class-attribute instance-attribute

reassigned: dict[TensorName, Tensor] = field(
    default_factory=dict
)

req_id instance-attribute

req_id: ReqId

schedule instance-attribute

schedule: WriteSchedule

staging_slot class-attribute instance-attribute

staging_slot: int | None = None

state class-attribute instance-attribute

step_d2h_event class-attribute instance-attribute

step_d2h_event: Event | None = None

tid instance-attribute

tid: Tid

write_n instance-attribute

write_n: int

add_reassigned

add_reassigned(key: str, slots: Tensor) -> None

append_chunk

append_chunk(
    chunk: _WriteChunk, freeze_event: Event | None = None
) -> TaskState | None

Grow this write with one save's rows. Returns None when appended, else the state that closed the task (COPYING or later).

freeze_event is stored in the same snapshot: events on one compute stream are ordered, so the newest also covers every earlier clone.

budget_tickets

budget_tickets() -> list[_BudgetTicket]

Distinct tickets across this task's chunks (by identity; one per save).

claim_copy

claim_copy() -> bool

QUEUED -> COPYING. Only one thread may run the copy stage; True if this caller won.

clear_tensors

clear_tensors() -> None

Drop the device freeze. Host is unchanged (staging wait / fail).

get_host_tensor

get_host_tensor(si: int, key: str) -> Tensor | None

chunks[si] host if written, else device freeze. One snapshot.

mark_done

mark_done() -> None

HOST_READY -> WRITTEN.

mark_failed

mark_failed(*, defer_wake: bool = False) -> bool

-> FAILED from any non-terminal state; unblocks joiners. False if already terminal. defer_wake: the caller publishes the failure record first, then calls wake_failed_waiters.

mark_host_ready

mark_host_ready() -> None

COPYING -> HOST_READY for staging views: wait the step device→host, drop freeze.

reassigned_intersects

reassigned_intersects(chunk: _WriteChunk) -> bool

True if this write already lost ownership of any (key, slot) the chunk would add; the caller opens a fresh WriteTask instead.

scatter_rows

scatter_rows() -> list[tuple[TensorName, Tensor, Tensor]]

(key, slots, host) to write, one entry per key. Omits slots in reassigned. A slot written twice by this task (preempt + resume onto the same block) keeps the later chunk's row.

set_host_tensor

set_host_tensor(
    rows: list[tuple[_WriteChunk, str, Tensor]],
) -> None

COPYING -> HOST_READY: write these host tensors, drop the device freeze.

slot_to_row

slot_to_row() -> dict[int, tuple[int, int]]

transition

transition(to: TaskState) -> None

Move to to; raises unless it is the next chain step or FAILED.

try_transition

try_transition(to: TaskState) -> bool

Move to to if legal from the current state. True if moved.

wake_failed_waiters

wake_failed_waiters() -> None

Set the events after the failure record is published (see mark_failed(defer_wake=True)): a woken joiner must find it.