Skip to practice questions

Part 2 · Fork and process trees

Fork/join and process topology

Draw the runtime process tree, predict join behavior, and preserve per-iteration state without relying on source indentation.

Reading path
2 of 3
Concept chapters
2
Practice links
5

Question-first preparation

Practice the mechanisms on this page.

Each question maps directly to one of the chapters below, so you can test the contract before reading the explanation.

Recommended start

One representative prompt from each major mechanism. Open any card in the live question bank.

  1. Q966Fork-join_anyVerification Utilities · EasyPremium
  2. Q330Fork-join_noneVerification Utilities · MediumPremium
  3. Q887Wait forkVerification Utilities · EasyPremium
  4. Q599Reason about same-region forked output orderSystemVerilog Scheduling · EasyPremium
  5. Q842Capture per-iteration values in fork...join_noneTestbench Concurrency · MediumPremium
Complete question directoryFilter every stable practice link by mechanism.5 questions
Showing 5 questions across every track.Choose a track to narrow the practice set without changing its stable question links.
  1. Q966Fork-join_anyVerification Utilities · EasyPremium
  2. Q330Fork-join_noneVerification Utilities · MediumPremium
  3. Q887Wait forkVerification Utilities · EasyPremium
  4. Q599Reason about same-region forked output orderSystemVerilog Scheduling · EasyPremium
  5. Q842Capture per-iteration values in fork...join_noneTestbench Concurrency · MediumPremium

03 · Fork and join semantics

Choose when the parent continues and who waits for the rest.

join, join_any, and join_none all create child processes; they differ only in the condition that releases the parent. That condition changes start time, variable capture, and cleanup obligations.

Rule to say firstUse join for a complete parallel phase, join_any only with an explicit policy for surviving children, and join_none only when a later synchronization point owns completion.

Parent release condition

Same children, different continuation

Child A · #12
runs t0→t12runs t0→t12runs after parent yields
Child B · #5
runs t0→t5runs t0→t5runs after parent yields
Child C · #0
finishes at t0finishes at t0runs after parent yields
The same three immediate children exist under every join variant; only the parent-release condition changes.
Parent resumes
t12 · all completet0 · first completeimmediately
With join_none, child execution begins when the continuing parent next blocks or terminates.
  1. 01joinall finish

    Parent resumes after the slowest immediate child.

  2. 02join_anyfirst finishes

    Other children continue unless explicitly stopped or awaited.

  3. 03join_noneparent does not wait

    Children begin when the parent next blocks or terminates.

joinall finish

Parent resumes after the slowest immediate child.

Child start
Parent yields
With join_none, creation precedes execution until the parent blocks or ends.
Loop capture
automatic local
Create per-iteration storage inside the forked context.
Same-time wakeup
Potential race
No deterministic order exists within the same event region.

A fork creates topology. The join keyword states only when the parent resumes, not how surviving children are managed.

01

Observe

Mark the parent process, each immediate child, the first statement after the fork, and which child lifetimes may outlast local variables.

02

Decide

Select a join variant from the required parent-release condition, then add wait fork, process handles, or scoped cancellation for remaining children.

03

Failure

join_any leaves siblings running, and join_none loop bodies can all observe the loop's final value unless each iteration captures an automatic local.

04

Prove

Draw absolute start/finish times, log parent and child IDs, randomize delays, and assert every spawned child reaches completion or a reviewed cancellation path.

join_none startup is deferred until the parent yields

The parent schedules children and continues. Children start when the parent reaches a blocking statement or terminates; an artificial #0 is not required if a real blocking point already exists. Without one, a loop can finish spawning before any child runs.

Capture a per-iteration valuesystemverilog
for (int i = 0; i < 8; i++) begin
  fork
    automatic int lane = i;
    drive_lane(lane);
  join_none
end

wait fork; // waits for this process's immediate outstanding children

Same-time branches have no source-order priority

When two child processes wake in the same event region, their execution order is not defined. A branch that writes x with blocking assignment and another that evaluates y <= x can capture either old or new x depending on scheduling.

Join selection
IntentConstructRequired follow-up
Parallel work, then combine allfork…joinNone beyond result checks
Race worker versus timeoutjoin_anyCancel or await survivor
Launch background workersjoin_noneStore handles or later wait fork
Iterative launchjoin_none + automatic captureBound count and completion
One branch containing sequential stepsbegin…end inside forkUse absolute timeline

Explain it out loud

Interview reasoning checkpoints

Strong answer

They start when the parent process next blocks or terminates. The fork statement creates them, but the parent continues until it yields.

Reason it through
  1. Find the first blocking statement after the fork.
  2. Determine what shared variables change before that point.
  3. Capture per-child values automatically before relying on them.
Interviewer lens

This tests scheduler knowledge and explains common loop-capture bugs.

Common trap

Adding #0 reflexively and assuming it creates a portable priority.

Strong answer

They continue running. The parent must deliberately await, cancel, or transfer ownership of every survivor.

Reason it through
  1. Identify which child completed first.
  2. List all remaining live children.
  3. Use handles or a narrow named scope to clean them up without killing unrelated work.
Interviewer lens

The key is that join_any is not race-and-cancel.

Common trap

Calling disable fork without checking the caller's full child scope.

04 · Parentage and wait fork

Process control follows the runtime tree, not visual indentation.

Named blocks help humans and disable statements, but fork parentage is determined by which process executes a fork. wait fork waits for the caller's immediate outstanding children.

Rule to say firstDraw the process tree before using wait fork or disable fork; lexical proximity does not prove parent-child ownership.

Runtime process tree

The caller owns only its immediate children

  1. 01P0phase process

    Calls wait fork.

  2. 02P1 / P2immediate children

    P0 waits until both complete.

  3. 03P1anested descendant

    P1's completion semantics determine whether P0 indirectly waits.

  4. 04Q0unrelated process

    Lexically nearby but not a child of P0; wait fork ignores it.

P0phase process

Calls wait fork.

wait fork
Immediate children
Includes outstanding children created earlier by the same caller.
Named block
Lexical scope
Useful for declarations and targeted disable, not a replacement for topology.
Loop use
Legal but serializing
A wait fork in each iteration can remove intended parallelism.

To predict wait and cancellation behavior, draw arrows for “spawned by” rather than boxes for source indentation.

01

Observe

Assign an identity to the caller, each immediate child, nested child, and long-lived background process.

02

Decide

Use join for local ownership, wait fork for the caller's own immediate children, and handles when individual nested processes need control.

03

Failure

A helper task that calls wait fork can wait on a different child set than the source layout suggests, while an earlier background child can unexpectedly extend the wait.

04

Prove

Log process::self identities and status, isolate ownership in wrapper processes, and assert phase completion counts.

wait fork waits for the caller's outstanding children

The set is not limited to the most recent fork statement. Any unfinished immediate child previously spawned by that process is included. Nested descendants matter only through whether their immediate parent itself remains alive.

Create a wrapper when wait ownership must be narrowsystemverilog
task automatic run_batch();
  fork
    begin : owned_batch
      for (int i = 0; i < 4; i++) begin
        fork
          automatic int id = i;
          worker(id);
        join_none
      end
      wait fork; // only owned_batch's worker children
    end
  join
endtask

Avoid accidental serialization

Placing wait fork inside a launch loop is legal, but each iteration waits for its newly created and any earlier outstanding children before the next launch. To run all iterations concurrently, launch the full set first and wait once from the owning process.

Topology questions
QuestionWhy it mattersEvidence
Who executed fork?Defines the parentProcess tree
Who calls wait fork?Defines waited child setRuntime identity
Did child use join_none?May finish before descendantsNested timeline
Were children spawned earlier?They are included if outstandingCreation log
Does loop wait each time?May serialize workStart/finish trace

Explain it out loud

Interview reasoning checkpoints

Strong answer

It waits for all outstanding immediate child processes of the process that executes wait fork, including children spawned by earlier fork…join_none statements.

Reason it through
  1. Identify the runtime caller.
  2. Enumerate its immediate unfinished children.
  3. Do not substitute lexical block nesting for process parentage.
Interviewer lens

The expected terms are caller, immediate children, and outstanding.

Common trap

Assuming it waits only for the textually nearest fork.

Strong answer

It can wait at the end of each iteration, preventing the next iteration from launching and therefore serializing work that was intended to overlap.

Reason it through
  1. Draw launch and wait points for two iterations.
  2. Move the wait after all launches if full parallelism is intended.
  3. Bound concurrency explicitly if partial overlap is required.
Interviewer lens

This is legal syntax; the issue is the resulting topology and schedule.

Common trap

Calling it illegal instead of explaining serialization.