send in unroll_for! without let binding seems to get lost
- Dominant language
- C++
- Stars
- 1.9k
- Forks
- 283
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 135
Description
**Describe the bug**
I tried to save a few characters by inlining a `send` inside a `join` within an `unroll_for!` (the idea being to send in parallel and order subsequent channel ops after all the sends completing). The test behaved as if the send never happened (deadlock).
But if I instead use an explicit `let` binding for the `send` token, the procs no longer deadlock.
**To Reproduce**
Minimal repro:
```
proc DutProc {
control: chan in;
data: chan[2] in;
ack: chan out;
config(control: chan in, data: chan[2] in, ack: chan out) {
(control, data, ack)
}
init { }
next(state: ()) {
let (tok, ctrl, _) = recv_non_blocking(join(), control, false);
let (tok, _) = recv_if(tok, data[0], ctrl, u32:0);
let (tok, _) = recv_if(tok, data[1], ctrl, u32:0);
let tok = send_if(tok, ack, ctrl, true);
}
}
#[test_proc]
proc my_dut_test {
control: chan out;
data: chan[2] out;
ack: chan in;
terminator: chan out;
config(terminator: chan out) {
let (control_s, control_r) = chan("control");
let (data_s, data_r) = chan[2]("data");
let (ack_s, ack_r) = chan("complete");
spawn DutProc(control_r, data_r, ack_s);
(control_s, data_s, ack_r, terminator)
}
init { () }
next(state: ()) {
let tok = send(join(), control, true);
let sent_data_tok = unroll_for! (i, data_tok): (u32, token) in u32:0..u32:2 {
join(data_tok, send(tok, data[i], u32:1 + i))
}(join());
let (tok, done) = recv(sent_data_tok, ack);
assert_eq(done, true);
let tok = send(tok, terminator, true);
}
}
```
Running this DSLX test, you get the following error:
```
: internal error: DEADLINE_EXCEEDED: Procs are deadlocked:
dut.x:28:31-28:58: proc `DutProc` is blocked on receive on channel `my_dut_test->DutProc#0::data[0]`
dut.x:57:31-57:51: proc `my_dut_test` is blocked on receive on channel `my_dut_test::ack`
```
If you change the test_proc `next` so that the `send` gets its own `let` binding:
```
next(state: ()) {
let tok = send(join(), control, true);
let sent_data_tok = unroll_for! (i, data_tok): (u32, token) in u32:0..u32:2 {
let tok = send(tok, data[i], u32:1 + i);
join(data_tok, tok)
}(join());
let (tok, done) = recv(sent_data_tok, ack);
assert_eq(done, true);
let tok = send(tok, terminator, true);
}
```
the test will no longer deadlock.
**Expected behavior**
It shouldn't matter whether the `send` gets its own `let` binding.
Contributor guide
Assessment
This issue has not been assessed yet.