Difficulty: Easy · Puzzle 4 of 6 · Topic: Semaphore & Mailbox Puzzles
Predict the output. Does worker B wait or time out when it tries for the token, and what does the missing put cost?
module tb;
semaphore sem = new(1);
task worker_a();
sem.get(1);
$display("[%0t] A got token", $time);
if (1) return; // missing sem.put(1)
endtask
task worker_b();
int ok;
#10;
ok = sem.try_get(1);
$display("[%0t] B try_get=%0d", $time, ok);
endtask
initial fork
worker_a();
worker_b();
join
endmoduletry_get never blocks and never times out: it returns 0 at once if no token is available.
1) A gets the only token at t=0 and returns without put.
2) The token count stays 0.
3) At t=10 B calls try_get(1). It returns 0 immediately; nothing 'times out'.
4) A blocking sem.get(1) in B would hang forever here.Answer: Prints [0] A got token and [10] B try_get=0. The leaked token means every later get blocks forever.
Checked by running the code in Verilator 5; the printed output supports the answer above. try_get returns 0 at once; the leaked token is never returned. A blocking get in B would wait forever on the leaked token.
B does not time out; try_get is non-blocking and simply returns 0. A blocking get would be the silent hang, which is why leaks are deadlocks in slow motion.
How would you enforce token release on every exit path? Model answer: structure the task so there is one exit: acquire, do the work in a block without early return, then put (or wrap the body in a task that returns a status and always call put after it).
A gets the only token at zero and returns without putting it back, so the count stays at zero. At ten, B's try_get returns 0 immediately, because it is non-blocking and does not time out. A blocking get would hang forever, which is why leaks are deadlocks in slow motion. The trap is expecting B to wait or time out. I would put the token back on every exit path, and check the count at the end of the test.