Sign up

Using futures on the GTK event loop

gtkmm has a fairly horrible way of putting function calls onto the GTK event loop queue from another thread. Rust has a function for one thread to add a closure for execution onto the event queue. It was nicer than the gtkmm way of doing things but then… the gtk-rs folk added the ability to use futures and channels of futures with the GTK even loop. This makes connection things from one thread to the GTK thread really easy and very "reactive programming".

Does GtkD already have something like this (and I have missed it) or has it yet to do something like this?

Re: Using futures on the GTK event loop

On 18-05-18 17:56, Russel Winder wrote:

gtkmm has a fairly horrible way of putting function calls onto the GTK event loop queue from another thread. Rust has a function for one thread to add a closure for execution onto the event queue. It was nicer than the gtkmm way of doing things but then… the gtk-rs folk added the ability to use futures and channels of futures with the GTK even loop. This makes connection things from one thread to the GTK thread really easy and very "reactive programming".

Does GtkD already have something like this (and I have missed it) or has it yet to do something like this?

We currently don't have anything like that, do you have an example on
how this works with gtkmm and/or gtk-rs?

Re: Using futures on the GTK event loop

We currently don't have anything like that, do you have an example on
how this works with gtkmm and/or gtk-rs?

I shall ignore C++ and hence gtkmm. :-)

In gtk-rs master (not yet released) the GTK event loop can process standard futures and channels of futures. There is a for_each function on channels of futures that does asynchronous processing of received futures on the channel.

let context = glib::MainContext::ref_thread_default();
context.spawn_local({
    let c_w = control_window.clone();
    message_channel.for_each(move |message| {
        match message {
            Message::FrontendAppeared { fei } => add_frontend(&c_w, fei.clone()),
            Message::FrontendDisappeared { fei } => remove_frontend(&c_w, fei.clone()),
        }
        Ok(())
    }).map(|_| ())
});

This "reactive" way of processing events works so well.

https://github.com/Me-TV/Me-TV

The above code is in src/control_windows.rs.

Re: Using futures on the GTK event loop

I am beginning to wonder if the gtk-rs solution to the problem, whilst nice, and very "reactive", can actually be handled in a slightly different way in GtkD – since spawned threads have an implicit channel.

As you noted in a different discussion point,

https://github.com/gtkd-developers/GtkD/blob/master/demos/gtkD/DemoMultithread/DemoMultithread.d

shows how to use receiveTimeout in an idle callback on the GTK+3 event loop – this is analogous to putting a future on the event queue in gtk-rs. My spawned processes are external to the ApplicationWindow rather than handled by it, but that is just a parameter passing issue – though there may be some ordering problems. Also choosing the timeout value so as not to upset the UI may be tricky, and here lies the different between this solution and the gtk-rs one, the GtkD one requires polling, the gtk-rs one relies on events throughout (as far as I know).

I shall give this idea a go anyway. Isn't it great how reading all the threads is a good thing. :-)

Re: Using futures on the GTK event loop

If of course I can get the spawned thread receives working in the Me TV context. I tried the "actor" arrangement in a stripped down form and it works fine. However what should be the same set up in the full GtkD context fails to work, and I have no idea why. :-(

Re: Using futures on the GTK event loop

It seems the receive in the Me TV code, immediately on triggering, is terminating the thread silently. This does not happen in my sample stripped down version. I have no idea why a receive might cause it's thread to exit. :-(

Re: Using futures on the GTK event loop

On 01-06-18 11:52, Russel Winder wrote:

If of course I can get the spawned thread receives working in the Me TV context. I tried the "actor" arrangement in a stripped down form and it works fine. However what should be the same set up in the full GtkD context fails to work, and I have no idea why. :-(

The spawned thread may be throwing an exception.

Re: Using futures on the GTK event loop

It was a faulty assertion error deep inside Phobos. Steven Schveighoffer sorted things out and has a fix. All that remains is the PR being put forward, accepted, merged, and a new distribution created.

Re: Using futures on the GTK event loop

In gtk-rs master (not yet released) the GTK event loop can process standard futures and channels of futures. There is a for_each function on channels of futures that does asynchronous processing of received futures on the channel.

let context = glib::MainContext::ref_thread_default();
context.spawn_local({
    let c_w = control_window.clone();
    message_channel.for_each(move |message| {
        match message {
            Message::FrontendAppeared { fei } => add_frontend(&c_w, fei.clone()),
            Message::FrontendDisappeared { fei } => remove_frontend(&c_w, fei.clone()),
        }
        Ok(())
    }).map(|_| ())
});

This way of working relies on having a ready made futures, executors, and channels system with a single threaded task pool system. D doesn't seem to have one, though I am sure std.parallelism and vibe.d both have what is needed within them. Of course Rust has Tokio (equivalent of vibe.d) and Rayon (equivalent of std.parallelism). Tokio appears based on Rust futures, but Rayon appears not to use them. I can sort of understand this, Tokio is I/O bound and so can use the I/O bound futures, whereas Rayon needs something much more lightweight with better work-stealing.

I am not sure making GtkD rely on vibe.d is the right thing to do, and I am not sure the vibe.d people would be up for pulling out their futures stuff as a separate dependency. Creating something just for GtkD doesn't seem cost effective.

I suspect I'll just go with glib.Timeout as a way of mocking up something analogous. I think Timeout rather than Idle, as it should use less CPU resources.

Re: Using futures on the GTK event loop

On 25-06-18 20:05, Russel Winder wrote:

This way of working relies on having a ready made futures, executors, and channels system with a single threaded task pool system. D doesn't seem to have one, though I am sure std.parallelism and vibe.d both have what is needed within them. Of course Rust has Tokio (equivalent of vibe.d) and Rayon (equivalent of std.parallelism). Tokio appears based on Rust futures, but Rayon appears not to use them. I can sort of understand this, Tokio is I/O bound and so can use the I/O bound futures, whereas Rayon needs something much more lightweight with better work-stealing.

I am not sure making GtkD rely on vibe.d is the right thing to do, and I am not sure the vibe.d people would be up for pulling out their futures stuff as a separate dependency. Creating something just for GtkD doesn't seem cost effective.

I suspect I'll just go with glib.Timeout as a way of mocking up something analogous. I think Timeout rather than Idle, as it should use less CPU resources.

Writing a vibe.d based GSource, that would live in a small separate
library might be an option, but it wouldn't be as integrated as the Rust
version.

A async library that is part of the standard library would be the best
option, also giving some consistency between libraries from different
projects. Photon (https://github.com/DmitryOlshansky/photon) also looks
promising.