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

Facilitate using step() while processing other events #299

Open
john01dav opened this issue Aug 7, 2021 · 1 comment
Open

Facilitate using step() while processing other events #299

john01dav opened this issue Aug 7, 2021 · 1 comment

Comments

@john01dav
Copy link

john01dav commented Aug 7, 2021

I'm trying to use this library in a way where I have multiple windows that are stepped by a thread (which happens to be the main thread), and then a background thread uses MPSC to send commands to the window stepping thread to make things happen. The problem is that webview as it is right now blocks on step() until events need to be processed, thus leading to my MPSC events not processing correctly. My window stepping thread loops roughly like this (simplified to get the point across):

loop{
    while let Ok(msg) = mpsc_recv.recv(){
        match msg{
            // ...
        }
    }
    
    for window in &windows{
        let result = window.step(); //blocks here
        if result.is_none(){
            // remove from windows list
        }
    }
}

When step() blocks on some window, mpsc_recv's events do not get processed, which is problematic. I could create a complicated severally-threaded system to process those events via another background thread that uses window handles, but this is very much non-ideal due to the complexity and it's not even a complete solution as window creation is still blocked by step() (one of the MPSC command types creates a new window).

I see a few solutions to this problem, ordered in descending order of what I think is best:

  • Add some way to wake up the windows from another thread — it looks like step() when blocking on any window will wake up if any other window receives an event, so it must be some sort of global event bus. As such, a web_view::notify_event_bus() that can be called from any thread would be a good API.
  • Add a timeout to step(), such that it will not block for more than, for example, a 60th of a second, or, even better, integrate with the underlying platform API to get the display frequency of the fastest display and do not block for longer than that
  • Allow an option for step() to be non-blocking. This should be a very easy change. See Set blocking in use of webview_loop to avoid busy-waiting #263 and Changed step to run unblocking, which effects how the UI responds to … #218, but it will introduce some amount of busy waiting which is another problem. If this were implemented, I'd have to do my own sleeps between event processing, but still process at a high enough frequency (e.g. at the display's frequency).
@Stay1444
Copy link

Stay1444 commented Mar 29, 2024

Quite late since it has been a couple of years since this issue was opened, but I encountered this same roadblock yesterday.

You can fix easily using webview.run() on a thread and then on a different thread using the .dispatch function to send the events.

This is what I'm doing:

let mut view = web_view::builder()
            .content(Content::Url("http://127.0.0.1:62000/"))
            .resizable(true)
            .size(1600, 900)
            .debug(true)
            .user_data(())
            .invoke_handler(|_webview, _arg| Ok(()))
            .build()
            .unwrap();

        // Create a task that will run in parallel, in another tokio thread
        let _t = tokio::spawn({
            let ui_tx = ui_tx;
            async move {
                loop {
                    tokio::time::sleep(Duration::from_secs(1)).await;
                    ui_tx.send(serde_json::json!({ "example": "data" })).await;
                }
            }
        });

        // grab the view handle
        let view_handle = view.handle();

        // create another task that will receive the ui messages
        let _event_sender = tokio::spawn(async move {
            loop {
                if let Some(message) = ui_rx.recv().await {
                    // Relay the messages to the UI
                    view_handle
                        .dispatch(move |view| {
                            view.eval(&format!(
                                "
                                    {{
                                        const event = new Event(\"message\");
                                        event.data = {};
                                        window.dispatchEvent(event);
                                    }}
                                ",
                                serde_json::to_string(&message).unwrap()
                            ))
                        })
                        .unwrap();
                }
            }
        });

        view.run().unwrap();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants