Most tasks use storage allocated by the compiler, but tasks that control buffering or publication can manage their own storage. This page explains both lifecycles and builds on the Holoflow task model.
Most numerical tasks borrow their input and output storage. The compiler allocates the tensors, the scheduler puts their views in the context, and the task reads or writes those views during its operation. Such tasks keep the default acquire_input(...) and release_output(...) implementations.
Some tasks must control storage themselves. Their factories mark the relevant slots in InferResult::owned_inputs and InferResult::owned_outputs, and the tasks implement the corresponding lifecycle hooks.
Borrowed tasks use compiler allocations directly; owning tasks publish regions from their own buffers through stable Storage objects.
For owned slots, IOStorageAccess exposes the stable storage objects prepared by the compiler:
An owning version of the rate limiter can expose its one-element staging slot directly to upstream and downstream work instead of copying into and out of a private temporary buffer.
Before execution or a push, the scheduler calls acquire_input(index). The task either returns a writable view or std::nullopt when no storage is currently available. The scheduler keeps retrying while the result is empty, but stops waiting when cancellation is requested.
The task publishes the selected pointer through the stable Storage object returned by storage_access().owned_input_storage(index). Context views already refer to that object, so upstream work writes directly into the rate limiter's slot. try_push(...) only commits the filled slot; it does not copy the input.
When the deadline is reached, try_pop(...) publishes that same slot through the stable output Storage. The scheduler calls release_output(index) only after the remaining work in the consumer section has finished using it. Until then, pending_ remains true and acquire_input(...) applies backpressure.
The two implementations differ only in where their slot is allocated. Neither task submits a copy or a kernel.
The CUDA task deliberately receives no streams. With synchronizes_producer_stream set to false, the scheduler completes upstream work on the producer stream before try_push(...) publishes the slot. The consumer can therefore expose the same device pointer without an internal copy or an additional synchronization.
Ownership is a declared contract
Call acquire_input(...) and release_output(...) only for slots marked as owned by factory inference. Failing to implement the lifecycle for an owned slot, or using the hooks for a borrowed slot, is undefined behavior.
At most one acquired input group and one unreleased output group may exist at a time under the current single-producer, single-consumer assumptions. An acquired input view is valid only through the corresponding execution or push call. An owned output remains valid until its release call.
Cancellation and acquired inputs
Holoflow does not yet define how to roll back an owned input acquired immediately before cancellation. Task authors should not assume an implicit discard or commit operation exists.