cgtkglib

Stop a CPU-intensive operation by pressing a GTK button


I'm extending a GTK-application that does a group of operations that takes high CPU loads. I want to include the possibility to stop this operation by clicking on a button in the GUI. The problem is that, as expected, the signal coming from the button is actually fired just after the operation is completed.

For now, the code kinda looks like this:

[...]
// code snippet to show the dialog and to enable user interactions with the buttons on the lower side of the window
while(TRUE) {
    gint run = gtk_dialog_run (window_main);
    if (run == GTK_RESPONSE_APPLY) {
        gboolean success = start_long_operation();
    }
    else if (run == GTK_RESPONSE_HELP) {
        open_about();
    }
    else if (run == GTK_RESPONSE_CANCEL) {
        stop_long_operation();
    }
    else {
        gtk_widget_destroy (window_main);
        return;
    }
}

I've declared a global variable busy_state that is checked by the long operation's function: if it is TRUE, simply the inner loop continues to cycle. Else, the loop exits and the function returns a result.

stop_long_operation() simply sets this global var to FALSE.

As written before, I can't press the "stop" button and "send" GTK_RESPONSE_CANCEL until the operation finishes, because it blocks the entire window.

I've tried the use of while (g_main_context_iteration(NULL, FALSE)) trick inside the stop_long_operation() function, as explained in the gtk's docs, but without results.

Do I really need to set up a multithread functionality? Can I avoid this? Thanks for the help.


Solution

  • If you can break up your long operation into multiple smaller tasks you may be able to avoid using threads. The easiest way would be to just create a callback that you would pass to g_idle_add (or g_idle_add_full). Each time the callback runs it does a small amount of work, then returns TRUE. When the the task is completed, return FALSE and the callback not be run again. When you would like to interrupt the task, simply remove the callback by passing the value returned by g_idle_add to g_source_remove.

    If you can't break up the operation then threads are pretty much your only choice. g_thread_new is the low-level way to do that, but it's generally easier to use a GThreadPool. A more advanced option would be to use g_simple_async_result_run_in_thread.