← Design Verification Interviews
Interview Question · Code Reading
Join, Join Any and Join None
- Asked at Google
The Question
The interviewer asks
When does the parent resume after join, join_any and join_none? Predict the output of this block.
initial begin
fork
#1 $display("t=%0t fast child done", $time);
begin #3 late = 1; $display("t=%0t slow child done", $time); end
join_any
$display("t=%0t parent resumes", $time);
#4 $display("late=%0d", late);
endYou have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
join resumes when every child has finished, join_any when the first one does, and join_none at once without waiting. Here the parent resumes at t=1 with the fast child, the slow child still finishes at t=3, and late=1 prints at t=5. join_any stops the waiting, not the other children.
- Cycle 0
The fork creates children A, B and C. After
join_nonethe parent goes on at once; afterjoinorjoin_anyit waits. - Cycle 2
A finishes first, so
join_anyreleases its parent. B and C keep running. - Cycle 5
B finishes. The parent after
joinstill waits, because C is running. - Cycle 9
C finishes, the last child, and
joinreleases its parent.
One column per time unit. The children run the same way in all three cases; only the parent differs.
t=1 fast child done t=1 parent resumes t=3 slow child done late=1Self-Check
Tick each key point you covered in your answer.
What Interviewers Listen For (Fresher)
A good fresher answer states the three rules and draws the timeline. It shows the slow child finishing after the parent has already moved on.
What Interviewers Listen For (Junior)
A good junior answer adds disable fork after a join_any that implements a timeout. It also explains what else that call might kill.
What Interviewers Listen For (Senior)
A good senior answer talks about processes left running in a long regression. One example is a timer from a join_any that fires in a later test. It states the team's rule for cleaning them up.
Cross-Questions
Where the interviewer goes next. Open a node to practise that follow-up.
WhyWhat IfProve ItCompareScale It
- Join, Join Any and Join None
-
What IfThe Timeout That Killed the MonitorL1 JuniorL2 Mid-Level
Asked once they add
disable forkAfter a request-with-timeout task was added, a background beat counter in the same test stops counting. Here is the task and the log. What happened?
log // task wait_resp: fork response / timeout join_any; disable fork; @0 beat counter forked (join_none) @25 beats=3 @30 response received, disable fork @80 beats=3 <- counter stopped at t=302:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
disable forkkills every active descendant of the calling process, not just the fork above it. The task runs in the test's process, so it also killed the counter the test had forked earlier. Put the race in its own scope,fork begin ... end joinaround it, sodisable forkonly reaches the response and timeout branches.Which Children Disable Fork StopsA test runs a response-or-timeout race twice: bare inside a task, then wrapped in its own process. Arrows point from a parent process to the children it forks. A dashed badge is a fork or a disable fork; a solid badge is a child finishing.Illustrative sequenceCallValueReturnWaiting- Bare: everything is a child of the testt=5 (illustrative)
The test forks the monitor. Then
wait_rsp_bareruns inside the test process, so the response and timer branches it forks are children of the test too.More context
The response model,
fork #6 rsp = 1; join_none, is a child of the test as well. It setsrspat t=11 and ends, so it is not drawn.Follow the calls, values and waits
- call fork: Test process → Monitor
- call fork: Test process → Response branch
- call fork: Test process → Timer branch
- Waiting: Test process — join_any
- Waiting: Response branch — waiting for rsp
- Waiting: Timer branch — counting to t=25
- The response winst=11 (illustrative)
The response branch prints
t=11 responseand finishes, sojoin_anyreturns. The monitor has beaten once, at t=9.Follow the calls, values and waits
- return done: Response branch → Test process
- Released: Response branch
- Released: Test process
- disable fork in the test processt=11 (illustrative)
disable forkstops every child of the process that runs it. That is the test, so the timer and the monitor both stop, and t=22 printsbeats=1.Follow the calls, values and waits
- call disable: Test process → Timer branch
- call disable: Test process → Monitor
- Released: Timer branch
- Wrapped: the race gets its own parentt=22 (illustrative)
The test forks a new monitor.
wait_rsp_wrappedforks a wrapper process, and the wrapper forks the response and timer branches.Follow the calls, values and waits
- call fork: Test process → Monitor
- call fork: Test process → Wrapper process
- call fork: Wrapper process → Response branch
- call fork: Wrapper process → Timer branch
- Waiting: Test process — join
- Waiting: Wrapper process — join_any
- Waiting: Response branch — waiting for rsp
- Waiting: Timer branch — counting to t=42
- disable fork in the wrappert=28 (illustrative)
The response wins at t=28. Now
disable forkruns in the wrapper, so it stops only the timer. The wrapper ends and the task returns.Follow the calls, values and waits
- return done: Response branch → Wrapper process
- call disable: Wrapper process → Timer branch
- Released: Response branch
- Released: Timer branch
- Released: Wrapper process
- Released: Test process
- The monitor survivest=39 (illustrative)
The monitor beats at t=26, 30, 34 and 38, so t=39 prints
beats=4.
Read the diagram as text
- Monitor: forever #4 beats++
- Test process: calls the wait task
- Wrapper process: fork begin ... end join
- Response branch: bare race: wait (rsp)
- Timer branch: bare race: #20 TIMEOUT
- Response branch: wrapped race: wait (rsp)
- Timer branch: wrapped race: #20 TIMEOUT
- Test process → Monitor: child
- Test process → Response branch: child
- Test process → Timer branch: child
- Test process → Wrapper process: child
- Wrapper process → Response branch: child
- Wrapper process → Timer branch: child
disable forkacts on the process that runs it. The wrapper gives the race a process of its own.Self-Check
Tick each key point you covered in your answer.
What Interviewers Listen For (Junior)
A good junior answer says that calling a task does not create a new process. So
disable forkinside the task reaches the caller's children too.What Interviewers Listen For (Mid-Level)
A good mid-level answer wraps the race in its own fork scope. It then searches the rest of the bench for the same pattern.
What Interviewers Listen For (Senior)
A good senior answer bans a bare
disable forkin shared library tasks. The author of a library task cannot know what the caller has forked.Misconception
"
disable forkkills only the processes of the fork statement just before it."It kills all active descendants of the current process. The twin loses a background monitor forked earlier by the same test.
Run It
A response-or-timeout task called from a test that already forked a beat counter.
RUNVerilator · simulator serviceExpected:ok=1 beats=3FAIL: disable fork also killed the monitor this process forked earlierSee the Fix
module tb; logic clk = 0; int beats = 0; bit ok; always #5 clk = ~clk; task automatic wait_resp(); fork begin // the fix: a private scope for the race fork begin #30 ok = 1; end // the response begin #100 ok = 0; end // the timeout join_any disable fork; // kill the loser end join endtask initial begin fork forever @(posedge clk) beats++; // a background monitor forked earlier join_none wait_resp(); #50 $display("ok=%0d beats=%0d", ok, beats); if (beats < 5) $display("FAIL: disable fork also killed the monitor this process forked earlier"); else $display("PASS: only the timeout was killed; the monitor kept counting"); $finish; end endmodulePrints:
ok=1 beats=8PASS: only the timeout was killed; the monitor kept countingMissed Corner
"Use
disable wait_respinstead; it only touches the task."Disabling a named task stops every active call of that task, in every thread, which is a different trap.
Source IEEE 1800-2017 §9.6.2
- Bare: everything is a child of the testt=5 (illustrative)
-
WhyWhy a Task Call Shares the Caller's ProcessL1 JuniorL2 Mid-Level
Why did the task's
disable forkreach a process the task never created?2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
Calling a task does not start a new process; the task's statements run in the caller's process, as if pasted in. So the children of "the current process" include everything the caller had forked before the call. Only
forkcreates new processes.Self-Check
Tick each key point you covered in your answer.
Misconception
"Every task call runs in its own thread."
Tasks run in the calling thread. Concurrency comes only from
fork,initial,alwaysand similar constructs.Source IEEE 1800-2017 §9.5
-
What IfScoping Disable Fork to the TaskL1 JuniorL2 Mid-Level
What if you must keep
disable forkin the task? How do you make it stop only the task's own children?2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
Wrap the race in its own process:
fork begin ... end join. Inside that block, the race's children are the only children of the new process. Adisable forkthere stops only them. The caller's monitors keep running.Self-Check
Tick each key point you covered in your answer.
Missed Corner
"Use
wait forkinstead; it is safer."wait forkalso acts on every child of the process, including the caller's monitors. A forever monitor makes it hang.Source Lesson 3.3 key points
-
CompareProcess Handles vs Disable ForkL2 Mid-Level
Compare killing the timeout through a
processhandle with usingdisable fork.2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
Inside each branch,
process::self()returns a handle, and callingkill()on the timeout's handle stops exactly that process and its descendants.disable forkis shorter but reaches everything the current process has forked. Handles also let youawait()a process or check itsstatus(), which suits library code that must not touch the caller's threads.Self-Check
Tick each key point you covered in your answer.
Misconception
"
kill()on a handle stops only that process; its children keep running."Killing a process also kills the processes it spawned.
Source IEEE 1800-2017 §9.7
-
Prove ItSolve 3.3.3: Cancellation Scope RepairSimulator-graded
Show it, don't just say it: this Silicon Lab problem is graded by the simulator.
-
Scale ItProcesses That Outlive Their TestL3 Senior
A regression runs hundreds of sequences in one simulation. How do leftover timers and watchers from
join_anyraces show up, and what rule prevents them?2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
They fire later, in an unrelated sequence. A stale timeout reports an error, or a watcher takes an event meant for someone else. The failure depends on which sequences ran before. The rule is that every race ends with an explicit cleanup in its own scope,
fork begin ... end joinaround the race plusdisable fork. End-of-test checks then confirm that no sequence-owned processes are still alive.The losing branch is still alive One column per 100 ns. The task returned at 100 ns, but its timer branch kept running.
Self-Check
Tick each key point you covered in your answer.
What Interviewers Listen For (Senior)
A good senior answer links failures that depend on test order to processes left running. It turns the cleanup into a review rule and an end-of-test check.
Misconception
"Leftover processes die when the sequence object is garbage collected."
A process is not owned by the object that started it. It runs until it finishes or is killed.
Source IEEE 1800-2017 §9.7
-
CompareFork Join vs Begin EndL0 Fresher
Compare
begin #5 a = 1; #5 b = 1; endwith the same two statements insidefork ... join. When doesbbecome 1?2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
In
begin ... endthe statements run in sequence, sobis set at t=10. Infork ... join, each statement is its own process, and all start at the same time. Both delays count from t=0, sobis set at t=5. The fork still finishes only when both are done.Self-Check
Tick each key point you covered in your answer.
Half-Truth
"
forkruns statements in parallel, so it is always faster."It changes when they start, not how long each takes. Dependent steps inside a fork can race or run in the wrong order.
Source IEEE 1800-2017 §9.3.2
-
Prove ItSolve 3.3.1: Wait for Two WorkersSimulator-graded
Show it, don't just say it: this Silicon Lab problem is graded by the simulator.
-
Scale ItProcesses That Outlive Their TestL3 Senior
A regression runs hundreds of sequences in one simulation. How do leftover timers and watchers from
join_anyraces show up, and what rule prevents them?2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
They fire later, in an unrelated sequence. A stale timeout reports an error, or a watcher takes an event meant for someone else. The failure depends on which sequences ran before. The rule is that every race ends with an explicit cleanup in its own scope,
fork begin ... end joinaround the race plusdisable fork. End-of-test checks then confirm that no sequence-owned processes are still alive.The losing branch is still alive One column per 100 ns. The task returned at 100 ns, but its timer branch kept running.
Self-Check
Tick each key point you covered in your answer.
What Interviewers Listen For (Senior)
A good senior answer links failures that depend on test order to processes left running. It turns the cleanup into a review rule and an end-of-test check.
Misconception
"Leftover processes die when the sequence object is garbage collected."
A process is not owned by the object that started it. It runs until it finishes or is killed.
Source IEEE 1800-2017 §9.7
What People Get Wrong
Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.
Misconception
"join_any kills the children that did not finish."
The other children keep running. In the twin a timeout left behind fires after the response has already won.
Run It
A response-or-timeout race with join_any and no cleanup.
t=2 response arrived timer_fired=1 FAIL: join_any only stops waiting; the timer kept running and fired after the responseSee the Fix
module tb;
bit timer_fired = 0;
initial begin
fork
#2 $display("t=%0t response arrived", $time);
begin #10 timer_fired = 1; end // the timeout
join_any
disable fork; // the fix: stop the losing timer explicitly
#20;
$display("timer_fired=%0d", timer_fired);
if (timer_fired) $display("FAIL: join_any only stops waiting; the timer kept running and fired after the response");
else $display("PASS: the timer was stopped when the response won");
end
endmodule
Prints: t=2 response arrived timer_fired=0 PASS: the timer was stopped when the response won
Misconception
"join_none children start immediately, in parallel with the next statement."
They are scheduled but do not start until the parent blocks or ends, so code after join_none runs first.
Source IEEE 1800-2017 §9.3.2
Buzzword Without Mechanism
"fork ... join runs its branches in parallel, so their order is random."
They are concurrent, so order within a time step is not guaranteed, but delays still order them in time. "Random" is the wrong word; "unordered within a time slot" is right.