Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

send in unroll_for! without let binding seems to get lost #1953

Open
mikex-oss opened this issue Feb 24, 2025 · 1 comment
Open

send in unroll_for! without let binding seems to get lost #1953

mikex-oss opened this issue Feb 24, 2025 · 1 comment
Labels
bug Something isn't working or is incorrect dslx DSLX (domain specific language) implementation / front-end

Comments

@mikex-oss
Copy link
Collaborator

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<bool> in;
    data: chan<u32>[2] in;
    ack: chan<bool> out;

    config(control: chan<bool> in, data: chan<u32>[2] in, ack: chan<bool> 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<bool> out;
    data: chan<u32>[2] out;
    ack: chan<bool> in;
    terminator: chan<bool> out;

    config(terminator: chan<bool> out) {
        let (control_s, control_r) = chan<bool>("control");
        let (data_s, data_r) = chan<u32>[2]("data");
        let (ack_s, ack_r) = chan<bool>("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.

@mikex-oss mikex-oss added bug Something isn't working or is incorrect dslx DSLX (domain specific language) implementation / front-end labels Feb 24, 2025
@mikex-oss
Copy link
Collaborator Author

Note: since there was some discussion about whether the deadlock is accidentally "fixed" because of the shadowing of the tok variable, the deadlock also goes away using a different binding inside the unroll_for!:

    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 t = send(tok, data[i], u32:1 + i);  // it doesn't matter if this is t or tok
            join(data_tok, t)
        }(join());

        let (tok, done) = recv(sent_data_tok, ack);
        assert_eq(done, true);

        let tok = send(tok, terminator, true);
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working or is incorrect dslx DSLX (domain specific language) implementation / front-end
Projects
Status: No status
Development

No branches or pull requests

1 participant