← Design Verification Interviews
Interview Question · Debug
The Timeout That Killed the Monitor
The Question
The interviewer asks
After 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?
// 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=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 fork kills 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 join around it, so disable fork only reaches the response and timeout branches.
disable fork; a solid badge is a child finishing.- 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 fork acts 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 fork inside 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 fork in shared library tasks. The author of a library task cannot know what the caller has forked.
Cross-Questions
Where the interviewer goes next. Open a node to practise that follow-up.
WhyWhat IfProve ItCompareScale It
- The Timeout That Killed the Monitor
-
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
What People Get Wrong
Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.
Misconception
"disable fork kills 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.
ok=1 beats=3 FAIL: 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
endmodule
Prints: ok=1 beats=8 PASS: only the timeout was killed; the monitor kept counting
Missed Corner
"Use disable wait_resp instead; 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