Fabien Roualdes
Fabien Roualdes

Reputation: 46

GTK+3 GUI freezes randomly (after 1 hour or after 20 minutes)

I am developping a program in C that uses GTK+3 and follows - more or less - the MVC architecture:

My problem is that the GUI freezes after a random running time, it can be 20 minutes or more than 1 hour and I do not know why. Maybe there is something I should know about GTK ?

Note : Access to model variables is protected by using mutex.

Thanks a lot for your help !!

#include <signal.h>
#include <gtk/gtk.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/prctl.h> 

void *
thread_gui(void* data)
{ 
    g_timeout_add(50, handler_timer_gui_update, NULL); // updates the GUI each 50ms
    gtk_main();
    pthread_exit(NULL);
}

gint
handler_timer_gui_update(gpointer data)
{
    gui_update();
    // gui_update reads the model and updates GUI by using
    // gtk_label_set_text, gtk_spin_button_set_value, cairo_paint
    return TRUE;
}

void
launch_periodical_call_updating_model( )
{
    signal( SIGRTMIN + 1, model_update );

    timer_t timer;
    struct sigevent event;
    event.sigev_notify = SIGEV_SIGNAL;
    event.sigev_signo = SIGRTMIN + 1;
    event.sigev_value.sival_ptr = &timer;
    timer_create(CLOCK_REALTIME, &event, &timer);

    struct itimerspec spec;
    spec.it_value.tv_nsec = 20 * 1000000; // updates the model each 20 ms
    spec.it_value.tv_sec = 0;
    spec.it_interval = spec.it_value;

    timer_settime( timerModel, 0, &spec, NULL);
}

int
main( int argc, char *argv[] )
{ 
    pthread_t pthread_gui;

    init_model( ); // init model variables
    launch_periodical_call_updating_model( ); 

    // signal capture to exit
    signal( SIGINT, ctrlc_handler );
    signal( SIGTERM, ctrlc_handler );

    // GUI
    g_thread_init(NULL);
    gdk_threads_init();
    gdk_threads_enter();
    gtk_init( &argc, &argv );
    create_gui ( ); // building GTK Window with widgets  
    pthread_create(&pthread_gui, NULL, thread_gui, NULL);
    gdk_threads_leave();

    // Leaving the program
    pthread_join( pthread_gui, NULL ); 
    stop_model( ); //It stops to update the model and releases memory

    return 0;
}

void
ctrlc_handler( int sig )
{
    gtk_main_quit(); 
} 

Upvotes: 1

Views: 1273

Answers (2)

liberforce
liberforce

Reputation: 11454

You should just stop using signals, threads, and just use event sources of the GTK+ main loop. So if you want to call some code every 50ms, then just use g_timeout or g_timeout_full if you need to refresh the UI more than once per second (otherwise g_idle_add or g_timeout_add_seconds are better options).

However I don't see the point in updating your model every 20ms and displaying results every 50ms. You're just wasting computing power, as you compute things that won't be displayed. Updating the model shouldn't be too expensive since you can do it in less than 20ms. So if you don't care about the exact period, just update the model in a callback connected to g_idle_add, and at the end of the callback, ask to update the UI by calling gtk_widget_queue_draw or gtk_widget_queue_draw_area on the widgets that need to be redrawn. It will then compute as fast as it can (so can adapt to each computer performance), and won't compute things not displayed. Using timers you then determine the time elapsed between two refresh. If it's too high, you can use g_timeout_add instead of g_idle_add to limit the refresh rate by using a longer refresh period.

Upvotes: 3

You should not use Unix signals with GTK. See signal-safety(7) and signal(7).

You should consider using only Glib timers instead (and not use any POSIX timer). Read about Glib event loop (used in GTK) and use g_timeout_add or related functions.

If you insist on handling Unix signals in a GTK program, do what Qt recommends about them: setup a pipe(7) to self at initialization, and poll that file descriptor (in GTK, using g_source_add_unix_fd or g_io_channel_unix_new) which your Unix signal handler would write(2) (one or very few bytes at a time). BTW, Linux also provides signalfd(2).

At last, GTK is not really thread-friendly (see also this), and all GTK functions should be called (only) from your main thread (not from some other pthread_gui). Actually, if you really need to code with several threads, consider having your non-GUI threads also communicating and synchronizing thru pipes with GTK and use Glib thread functions to start and manage them.

You could even consider some multi-processing approach: having the GUI program in one process, and the other processing (multi-threaded) in another process (probably started by the GUI program using g_spawn_async_with_pipes).

Read about continuations and continuation-passing style. It may help you to think in those terms.

Upvotes: 4

Related Questions