§ 15 — Step groups and parallelism
15. Step groups and parallelism
Normative. This chapter defines how registered work units are grouped for parallel execution, and the barrier and interactive-drain rules that constrain that parallelism.
15.1. Step groups
Work units registered inside a single invocation of cook.step_group form a step group. The units in one step group MAY execute in any order, and MAY execute in parallel, subject only to cross-recipe dependencies (§ 16.1) and the interactive rule (§ 16.2).
Work units registered outside any cook.step_group invocation are sequential. A sequential unit MUST execute strictly after every work unit that was registered earlier in the same recipe, and strictly before every work unit that is registered later in the same recipe.
When step groups and sequential units are interleaved, the implementation MUST enforce a barrier between them: every unit of one step group MUST complete before any unit of a later step group or later sequential unit in the same recipe begins. Equivalently, the set of units registered in a single step group acts as a single synchronisation point from the perspective of units registered after it.
The Cookfile surface syntax in § 6 produces step groups and sequential units as follows:
| Surface construct | Registration behaviour |
|---|---|
cook step (§ 8.4, § 8.5) | One step group per cook step. Recorded by the declarative region. |
test step (§ 8.6) | One step group per test step. Recorded by the declarative region. |
register_block body (§ 5.4) | Spliced verbatim into the compiled register-phase Lua program at its source position. Evaluated during the register phase; produces units only via explicit Cook Lua API calls. |
top-level module_call (§ 4.4) | Spliced verbatim at source position; same registration behaviour as register_block. |
recipe-body module_call (§ 8.1, rule 7) | Auto-classified register-phase (CS-0134). Inlined into the recipe-body Lua function and evaluated during the register phase; produces units only via explicit Cook Lua API calls — the same behaviour as register_block. |
Example 15.1.1
recipe build
gather "src/*.c"
cook "build/$<in.stem>.o" { gcc -c $<in> -o $<out> }
cook "build/app" { gcc -o $<out> $<in> }
The first cook step expands to one step group containing one compile unit per source file; the compiles MAY run in parallel. The second cook step is a distinct step group. The barrier rule forces every compile to complete before the link unit begins.
Note 15.1.1
Parallelism is permissive, not prescriptive. A conforming implementation MAY run step-group members serially — for example, under a one-job flag. The Standard only requires that step-group members are not ordered with respect to one another; it does not require that they actually execute concurrently.
15.2. Body bundling — removed by CS-0134
Body bundling coalesced the imperative region of a recipe into execute-phase work units. As of CS-0134 a recipe body is purely declarative (§ 6.3) and has no imperative region, so there is nothing left to bundle: this section is vacuous and non-normative. A recipe’s execute-phase work now comes exclusively from the block body of a cook or test step (>{ … } / { … }, § 8.4, § 8.6, § 8.7), and each such block body is its own work unit — there is no cross-step coalescing.
This section does not govern chore bodies. Chore-body execution — including its single-drain semantics for shell_command, lua_line, and lua_block steps — is defined in § 7 (§ 7.3) and has never been subject to body bundling.
Non-normative (historical). Before CS-0134, a run of consecutive imperative-region recipe steps containing zero
interactive_commandsteps coalesced into one execute-phase body unit whoselua_codewas the source-order concatenation of the run’slua_line/lua_blockbodies and itsshell_commandlines (adjacent shell lines joined into a singleset -e-prefixedcook.shcall), with the Lua VM persisting across the whole bundle andinteractive_commandsteps breaking it. The removal of the standalone>/>{/ shell /@recipe-body steps (CS-0134) retired this mechanism.