We implement a producer-consumer pipeline using dependent-async tasks, demonstrating how tf::Executor::dependent_async naturally expresses stage-level dependencies between data items and how production and consumption overlap in time without any manual synchronization.
A producer generates N data items one by one. Each item must pass through a validator before it can be consumed. Consumption can begin as soon as an item is validated. There is no need to wait for the entire input to be produced first. The three-stage pipeline per item is:
Items are independent of each other across all three stages, so stages of different items can overlap in time. This is exactly the kind of dynamic, data-driven structure that tf::Executor::dependent_async is designed for.
The following diagram illustrates the overlapping execution of four items through the three stages. Each item's stages are wired by dependency edges so that Produce must finish before Validate, and Validate before Consume. Because items are independent of one another, the executor schedules stages of different items in parallel whenever workers are available:
We create three dependent-async tasks per item. Each task depends on the previous stage of the same item. Because tf::Executor::dependent_async begins executing a task as soon as all its predecessors complete, item i+1 can be produced while item i is being validated and item i-1 is being consumed:
Because each stage only depends on the previous stage of the same item rather than on the previous item's stages, the executor's work-stealing scheduler can run all three stages of different items concurrently across available workers. No mutex, condition variable, or queue is needed; the dependency edges express all synchronization requirements.
In some pipelines, the main thread needs to inspect intermediate results before deciding what to submit next. tf::AsyncTask::is_done provides a non-blocking way to check whether a specific task has completed. Combined with tf::Executor::corun_until, the calling thread remains active in the work-stealing loop while polling and never blocks.
The example below produces and validates one item, polls completion, and conditionally submits a downstream task based on the validation result:
is_done in a tight spin-wait without corun_until risks starving the worker thread pool if the calling thread is itself one of the executor's workers. See Asynchronous Tasking with Dependencies for a full discussion of the dependent-async API.