Skip to content

Incremental compile OOM when rsx has a loop over a component #3903

Closed
@TapGhoul

Description

@TapGhoul

Problem

When compiling code with a loop, a server function, and some other bits and bobs, the compiler will continuously eat memory and eventually OOM. This occurs on my machine with 86GB of RAM free - it fully OOMs.

There's a few edits that prevent duplicating this - removing the onclick handler, and removing the loop around the div {} both prevent this occurring, but in the larger app the only way to consistently prevent this was to remove the loop(?)
I do 50 here, but a loop of 0..1 is enough to cause a problem.

Once you do get a hang, the issue can crop up in places that it did not previously occur - you need to cargo clean between each set of tests to ensure that you have a clean slate, otherwise you will get false positives in when things break.

You cannot reproduce this purely with clean builds - this only occurs during incremental rebuilds.

Steps To Reproduce

Note, for all compiles, cargo build --profile server-dev --verbose --features dioxus/server suffices to reproduce - but this can be replicated with the dioxus CLI too. Just you can't as easily kill it and prevent the OOM killer stomping all over your machine with the dioxus CLI, as it sometimes fails to properly kill the processes when cancelling.

Steps to reproduce the behavior:

  • Start with a basic bare-bones dioxus project, and copy the below code into src/main.rs
  • Compile - successful
  • Comment out the line use_effect(move || loaded.set("Yes")); and rebuild - should be successful
  • Uncomment out that line and rebuild - hang and eventual OOM

Expected behavior

Dioxus should build just fine

Screenshots

Image

Environment:

  • Dioxus version: 0.6.3
  • Rust version: 1.85.1 stable
  • OS info: Arch Linux
  • App platform: Web fullstack

Questionnaire

I'm interested in fixing this myself but don't know where to start. I don't know how much time I will have but if I do have time I'm happy to go for it.

Code:
use dioxus::prelude::*;

fn main() {
    dioxus::launch(App);
}

#[server]
pub async fn notify_update(_v: Option<u32>) -> Result<u32, ServerFnError> {
    Ok(0)
}

#[server]
pub async fn trigger_update() -> Result<(), ServerFnError> {
    Ok(())
}

#[component]
fn App() -> Element {
    let initial_state =
        use_server_future(move || async move { notify_update(None).await.unwrap() })?;

    let mut state = use_signal(|| initial_state.unwrap());

    use_future(move || async move {
        loop {
            let old_state = *state.read();
            let new_state = notify_update(Some(old_state)).await.unwrap();
            state.set(new_state);
        }
    });

    let mut loaded = use_signal(|| "No");
    use_effect(move || loaded.set("Yes"));

    rsx! {
        button {
            onclick: async |_| { trigger_update().await.unwrap(); }
        }

        div {
            "{loaded}"
        }
        for _ in 0..50 {
                div {}
        }
    }
}

Full example project: breakshit.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions