1

When I use any of the codes in this page without modifying anything: https://wiki.gnome.org/Projects/Vala/AsyncSamples

I always get: warning: ‘g_simple_async_result_new’ is deprecated: Use 'g_task_new' instead.

So I proceed with the recommendation of using GTask. However, when I try to use GLib.Task in Vala, I get stuck just declaring a task. So instead of using async from GIO in my own code, since it is deprecated, I try to use GLib.Task to simply update the label of a Gtk Button with numbers from a for loop, such that the code looks like this:

using Gtk;
Button button;

public static int main (string[] args) {
    Gtk.init (ref args);

    var window = new Window ();
    window.title = "Count without blocking the UI";
    window.border_width = 10;
    window.window_position = WindowPosition.CENTER;
    window.set_default_size (350, 70);
    window.destroy.connect (Gtk.main_quit);
    button = new Button.with_label ("Start counting");
    button.clicked.connect (() => {
    GLib.Task task = new GLib.Task(button, new Cancellable());
});

window.add (button);
window.show_all ();

Gtk.main ();

return 0;
}

void count(){
    for(int i = 0; i < 10000; i++){
        button.label = i.to_string();
    }
}

But when compiling I get: error: ‘_data_’ undeclared (first use in this function) _tmp3_ = g_task_new_finish (_data_->_res_);

The line number 15 is what is causing the compiler to throw that error. It comes from the C code that the vala compiler generates.

The main problem I found is that the GTask constructor signatures in Vala are different from C. Therefore, I could not re-create the code found here: GUI becomes unresponsive after clicking the button using GTK+ in C

Because for starters, I am not allowed to pass more than two arguments to the GLib.Task object constructor. The constructors of the Task object are different in each language. The constructor for GLib.Task in Vala can be found here.

Hence my question:

Are there any examples on how to use GLib Task (GTask) in Vala to perform an operation that updates the UI without blocking it? If not, is there another way to update the UI without blocking it? A way that is not deprecated?

Thanks.

P.S: I have already tried GLib.Thread, GLib.ThreadPool, and GLib.Idle. They all block the UI while in the for loop. GLib.Idle does not block the UI completely, but it renders it buggy in the sense that it becomes really slow to respond to user input while the loop is running.

g_l
  • 621
  • 5
  • 15
  • See the discussion here: https://bugzilla.gnome.org/show_bug.cgi?id=763345 – Jens Mühlenhoff Sep 15 '16 at 11:24
  • Updating the UI as quickly as that will almost surely block it in a single-thread model. You can either make more significant computation or use `GLib.Timeout` in the loop from my answer. – arteymix Sep 15 '16 at 15:06
  • I'm not so sure I understand what is going on there and the efficacy of that patch. I have a newer version of glib: 2.48.1 and regardless of using --target-glib=2.48.1 with valac in my code I still get a warning. I don't think that temporary solution is working. – g_l Sep 15 '16 at 19:12

1 Answers1

5

It's perfectly fine to use async and there's some work already for porting the current code to use GTask.

Your counting code is blocking, so even if its execution is cushioned with GTask, it will still block the UI.

The correct way of performing CPU intensive background operations either use subprocess asynchronously or launch the work in a thread and dispatch in the main loop.

async void test_async () {
    new Thread<void> (() => {
         // count here...
        test_async.callback ();
    });
    yield;
}

The GTask or more generally GAsyncResult only provide a container for holding the result of an asynchronous operation. They also recommend to use a GThreadPool, but it's a bit more boilerplate.

Another interesting thing is that test_async.callback is actually a SourceFunc, so you can pass it around in GLib.Timeout.

EDIT:

To fit more your question, if you want to update the UI while it progress, use an async loop:

async test_callback () {
    for (var i = 0; i < 10000; i++) {
        button.label = i.to_string ();
        Idle.add (test_async.callback);
        yield; // pause execution until retriggered in idle
    }
}

Here's a full and working example:

using Gtk;
Button button;

public static int main (string[] args) {
    Gtk.init (ref args);

    var window = new Window ();
    window.title = "Count without blocking the UI";
    window.border_width = 10;
    window.window_position = WindowPosition.CENTER;
    window.set_default_size (350, 70);
    window.destroy.connect (Gtk.main_quit);
    button = new Button.with_label ("Start counting");
    button.clicked.connect (() => {
        count ();
    });

    window.add (button);
    window.show_all ();

    Gtk.main ();

    return 0;
}

async void count(){
    for(int i = 0; i < 10000; i++){
        button.label = i.to_string();
        Idle.add (count.callback);
        yield;
    }
}
arteymix
  • 433
  • 4
  • 6
  • Thanks. I marked it as a solution, but I still get the same warnings. Also, the first code snipping, says the compiler, does not allow a lambda expression there. And the second one needs a minor fixing. It should be: "async void test_callback (){..." and "Idle.add (test_callback.callback);" Putting it that way makes it compile fine and do what I was intending for it to do. Although the warnings still appear. – g_l Sep 15 '16 at 19:13
  • What calls my attention is that in valadoc, the "Idle.add ()" method signature does not match yours, yet yours compiles fine. I guess the valadoc documentation is outdated. Although I still get the warnings to use GTask with your code. I hope this gets fixed in the future since the current patch doesn't work and I'm using valac version 0.32.1 and glib-2.0 version 2.48.1 and I still get the warnings even with explicit command "--target-glib [switch]". – g_l Sep 15 '16 at 19:14
  • You will still get warnings for GTask because the patch has not been merged yet. If you don't use async, you will end-up with a lot of boilerplate. – arteymix Sep 15 '16 at 23:40
  • @arteymix Why didn't you include the full example code here? – Jens Mühlenhoff Sep 19 '16 at 11:13
  • @JensMühlenhoff Sorry, I'm still need to eat more StackOverflow crusts, I thought it would be nicer not to repost the whole example with small changes. – arteymix Sep 20 '16 at 20:10
  • No problem :). Questions and answers should be self-contained if possible. Of course linking to additional resources is great as well. When the code in the question is too long, you should tell the op to create an [MVCE](https://stackoverflow.com/help/mcve). – Jens Mühlenhoff Sep 21 '16 at 10:24