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

Unable to send an Event outside of the main thread #23

Open
OrionNebula opened this issue Apr 19, 2021 · 2 comments
Open

Unable to send an Event outside of the main thread #23

OrionNebula opened this issue Apr 19, 2021 · 2 comments

Comments

@OrionNebula
Copy link

Currently, sending an event requires a &mut State, which can't be obtained outside of event handlers. This makes it difficult to write any code that requires external triggering. A minimal example would be a timer app - an external time-keeping thread can't trigger a redraw, so the displayed time can't be updated.

The workaround I'm currently using is to push a WindowEvent::Redraw during the on_draw handler, which will initiate a manual redraw right after the last one is finished. This isn't ideal though - a lot of the renders are redundant.

@ollpu
Copy link

ollpu commented Apr 19, 2021

Recapping discussions on Rust Audio Discord:

  • Need some way to send events to the event queue from outside. This could be an MPSC.
  • However, MPSC as a one-size-fits-all solution is a poor fit for communication from a real-time audio thread
  • Instead, allow a widget to register itself for poll events, which are sent each frame. The user then implements whatever inter-thread communication is needed

The poll events can be done manually for now, but ultimately the ability to submit async tasks seems useful overall:

state.spawn(|handle| async move {
    let timer = ...;
    timer.await;
    handle.insert_event(Event::new(TickEvent::new()).direct(entity));
});

Needs some design work though, and unclear how an async executor can be integrated with the main event loop.

EDIT: Most likely, for now, we want the async executor to run on a separate thread. This way the user can set up an executor of their choice. For example:

// Use a multi-threaded executor. If a single-threaded one is desired (which - it probably is),
// it needs to be started on a separate thread.
// We can also of course provide feature-gated helpers for spinning up some executor.
#[tokio::main]
fn main() {
    let app = Application::new(|state, window| {
        // Invent a better name for this. The purpose of this "handle" is only to be able to
        // send events to the main event loop.
        // Internally clones either an EventLoopProxy or an MPSC producer handle.
        let handle = state.get_async_handle();
        tokio::spawn(async move {
            let mut timer = time::interval(Duration::from_secs(5));
            loop {
                timer.tick().await;
                handle.insert_event(Event::new(TickEvent::new()).propagate(Propagation::All));
            }
        });
    });
    // Run the actual app synchronously. This is why we need a multi-threaded or separate executor.
    app.run();
}

@geom3trik
Copy link
Owner

So my thinking for a solution to this would be to add timers. In the future I think it would be great to have some way to send events from another thread or to use async, but for the time being my suggested stopgap solution would be something like entity.add_timer(state, Timer::new(Duration::from_secs(5))). After the interval duration the entity would receive a TimerEvent with the id of the timer and could then call for a redraw or whatever. The tricky part will be figuring out how to get the timers to trigger. Currently a defualt app is set up to wait for events from winit. It's possible we might be able to use the event loop proxy to inject an event during the interval but it might also require the timers to run on another thread. I will investigate and report back.

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

3 participants