Difficulty: Medium · Puzzle 6 of 6 · Topic: Semaphore & Mailbox Puzzles
Does this pattern avoid deadlock? What time does it print, and what does the bounded retry change if the consumer never answers?
`timescale 1ns/1ps
module tb;
mailbox #(int) req_mb = new(2);
mailbox #(int) rsp_mb = new(2);
task automatic producer();
int rsp;
int tries = 0;
while (!req_mb.try_put(7)) #1;
while (!rsp_mb.try_get(rsp)) begin
#1;
if (++tries == 5) begin
$display("[%0.1f] producer: no response after 5 retries", $realtime);
return;
end
end
$display("[%0.1f] rsp=%0d", $realtime, rsp);
endtask
task automatic consumer();
int req;
#0.5;
while (!req_mb.try_get(req)) #1;
rsp_mb.put(req + 1);
endtask
initial begin
fork
producer();
consumer();
join
$finish;
end
endmoduleNo thread holds anything while it waits, and every wait is a time delay.
1) The producer's try_put succeeds at t=0; its first try_get fails, so it backs off #1.
2) The consumer starts at t=0.5 (explicit delay, so the order is deterministic), try_get gets 7, and rsp_mb.put(8) completes at once.
3) At t=1.0 the producer's try_get returns 8 and prints [1.0] rsp=8. Same result with the fork order swapped (verified in Verilator).
4) There is no circular wait. Without the consumer (removed in a test) the producer gives up and prints '[5.0] producer: no response after 5 retries' instead of spinning forever.Answer: Yes, it is deadlock-free. It prints [1.0] rsp=8. The retry bound turns a missing peer into a clean report at t=5 instead of an endless polling loop.
Checked by running the code in Verilator 5; the printed output supports the answer above. No thread holds anything while waiting, so the request/response finishes at t=1.0. If nobody answers, the retry bound reports at t=5.0 instead of spinning forever.
Deadlock-free is not livelock-free: an unbounded poll-with-#1 loop never hangs a time step, but it never ends either if the peer is dead, which is why the retry bound reports at t=5.0. Nothing is held while waiting, so no lock is needed here.
What timeout and error-report strategy would you add for regression triage? Model answer: bound by simulation time (a watchdog or timeout fork), report with `uvm_error` including the mailbox name and last request, and kill/disable the poller so the test fails fast instead of hitting the global timeout.
Yes, it avoids deadlock, and it prints rsp equal to 8 at time one. The producer's first try_get fails and it backs off, the consumer starts at point five and answers at once, and there is no circular wait because nothing is held while waiting. The retry bound is the other half: if the consumer never answers, it reports at time five instead of polling forever. The trap is mixing up deadlock-free and livelock-free, so I would check what the bounded loop does when the peer is dead.