Skip to content

Tasks and Ordering

What must finish before a task starts — how the runtime works that out on its own, and the interfaces for saying it yourself when it cannot.

Prerequisites: Scopes and Placement and Types § parameter directions.

What this chapter is

Scopes and Placement answers where code runs. This chapter answers when — the shape of the dependency graph the runtime actually executes.

Most programs never need it. The runtime derives the graph from the buffers each task touches and the direction each parameter declares, and that inference is correct by default. You come here when it is not enough:

  • the runtime inferred an edge that is not a real dependency, and serialized work that could have overlapped;
  • it could not infer a real edge, because the relationship is not visible as a buffer overlap;
  • or the work itself is conditional, and you want the task skipped rather than dispatched.

Contents

Page Covers
The dependency model What a task is, how edges are derived, why statement order expresses nothing
Runtime scopes pl.scope / pl.manual_scope / ScopeMode — the auto-tracking boundary
Declaring an edge TaskIds and deps= — via pl.at blocks or pl.submit, and fan-in through a TaskId array
Refining the graph pl.no_dep, predicate=, allow_early_resolve=, pl.system.task_dummy

Reading order

Read The dependency model first — every interface in the other pages is either a way to switch that inference off, or a way to add to it:

00-model ──► 01-scopes ──► 02-submit ──► 03-tuning
  what the      where the      how to say      how to refine
  runtime       inference      an edge         what you said
  infers        is on/off      yourself

A useful thing to know before you start: explicit edges and automatic tracking are not alternatives. deps= works inside an ordinary auto scope, and the final wait set is the union of both. Reaching for pl.manual_scope is a separate, heavier decision — it turns the inference off entirely and makes every edge yours to declare.

See Also