idanarye / woab

Widgets on Actors Bridge - a GUI microframework for combining GTK with Actix
https://crates.io/crates/woab
MIT License
14 stars 1 forks source link

Let futures on the Actix loop go outside and back again #29

Closed idanarye closed 3 years ago

idanarye commented 3 years ago

In #28 @piegamesde mentioned a usecase where an actor would spawn a future on the GTK loop (specifically - outside Actix) that would interact with GTK and when done send a message back to the actor. This future cannot use Actix' ActorFuture to retake the actor, because that would make the future execute inside Actix and thus it won't be able to do GTK operations that trigger signals - unless it uses woab::schedule_outside, which does offer a way to get back to the future.

We need a way to allow futures spawned on the Actix loop to yield execution to something outside the Actix loop and await for that something to finish.

idanarye commented 3 years ago

The API I want is something like this:

ctx.spawn(async move {
    // Do stuff inside Actix
    woab::outside(async {
        // Do stuff outside Actix
    }).await;
    // do stuff inside Actix again
}.into_actor(self))

This will be based on a little more manual function:

async fn outside(fut: Future) {
    woab::wake_from_outside(|waker| {
        glib::MainContext::ref_thread_default().spawn_local(async move {
            fut.await;
            waker.wake();
        });
    }).await;
}

(Not actual Rust code (don't have all the required annotations), I'll try to get return values to work, and I'll also try to ensure woab::outside does not require move)

The reason for wake_from_outside is so we can write things like

let button: gtk::Button = get_the_button_from_somewhere();
ctx.spawn(async move {
    woab::wake_from_outside(|waker| {
        button.connect_clicked(|_| {
            waker.wake();
        });
    }).await;
    // Do things after the button was clicked
}.into_actor(self))
idanarye commented 3 years ago

Actually, I think maybe woab::wake_from_outside should not actually go outside Actix:

  1. If I run the closure immediately, it will not need to be move.
  2. You'd usually use it to connect a signal, so you don't need it to run from outside.
  3. If for whatever reason you do need to run it from outside you can just use schedule_outside (or spawn_outside, which I'm probably going to replace schedule_outside with)

Also, instead of using a waker I can probably just use Tokio's oneshot - which will also make it easy to pass back data on wakeup. This will also make it trivial to pass back the async block's result in woab::outside.