Graeme Jensz
Graeme Jensz

Reputation: 277

C and GTK3 - How to run a thread uninterrupted?

I've been trying to: show 4 random dice faces, 0.4 seconds apart, giving the impression that a dice is rolling. I've put the code to do this in a thread.

But the program crashes after a small number of "rolls". Sometimes it does not even get through the first roll. All sorts of run-time errors are generated.

The last thing I've tried is mutex protection of the thread code. I suspect this is just protecting the code from other threads but my program does not have other threads.

The program is running under Linux and is compiled with: gcc -rdynamic -o test3d1 test3d1.c pkg-config --cflags --libs gtk+-3.0

#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>

GtkWidget *window;
GtkWidget *vbox;
GtkWidget *butt;
GtkWidget *image;
GdkPixbuf *pixbuf1;
GdkPixbuf *pixbuf2;
GdkPixbuf *pixbuf3;
GdkPixbuf *pixbuf4;
GdkPixbuf *pixbuf5;
GdkPixbuf *pixbuf6;

pthread_t thread;

void *fn_thread ()
{
    gtk_widget_set_sensitive (butt, FALSE);

    int i;
    for(i = 1; i < 5; i++)
    {
        int j;
        j = rand () % 6 + 1;
        switch (j)
        {
            case 1: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf1); break;
            case 2: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf2); break;
            case 3: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf3); break;
            case 4: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf4); break;
            case 5: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf5); break;
            default: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf6);
        }
        usleep (400000);
    }

    gtk_widget_set_sensitive (butt, TRUE);

    return NULL;
}

void startthread ()
{
    pthread_create (&thread, NULL, fn_thread, NULL);
}

int main (int argc, char *argv[])
{
    gtk_init (&argc, &argv);

    srand (time(0));

    pixbuf1 = gdk_pixbuf_new_from_file ("dice_1_48x48.png", NULL);
    pixbuf2 = gdk_pixbuf_new_from_file ("dice_2_48x48.png", NULL);
    pixbuf3 = gdk_pixbuf_new_from_file ("dice_3_48x48.png", NULL);
    pixbuf4 = gdk_pixbuf_new_from_file ("dice_4_48x48.png", NULL);
    pixbuf5 = gdk_pixbuf_new_from_file ("dice_5_48x48.png", NULL);
    pixbuf6 = gdk_pixbuf_new_from_file ("dice_6_48x48.png", NULL);

    image = gtk_image_new ();
    gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf1);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
    gtk_window_set_default_size (GTK_WINDOW (window), 150, 100);

    butt = gtk_button_new_with_label ("Roll");
    g_signal_connect (G_OBJECT (butt), "clicked", G_CALLBACK (startthread), NULL);

    vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL,10);
    gtk_box_pack_start (GTK_BOX (vbox),image,0,0,0);
    gtk_box_pack_start (GTK_BOX (vbox),butt,0,0,0);
    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show_all (window);

    gtk_main ();

    pthread_join (thread, NULL);

    return 0;
}

Upvotes: 0

Views: 142

Answers (1)

r3mus n0x
r3mus n0x

Reputation: 6144

As pointed out in this answer, you should not be calling GTK functions from any other thread than the one in which gtk_main is executed.

In fact, in the event-driven program you should not be using additional threads at all unless you need to optimize some heavy computations. What you should do instead is utilize the features of your event-driven framework, in your case GTK, in order to execute all event handling in the main thread.

In your case this means that you should use g_timeout_add function to schedule calling your animation function at regular intervals from the main thread:

gboolean fn_roll_dice (gpointer)
{
    gtk_widget_set_sensitive (butt, FALSE);

    int j = rand () % 6 + 1;
    switch (j)
    {
        case 1: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf1); break;
        case 2: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf2); break;
        case 3: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf3); break;
        case 4: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf4); break;
        case 5: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf5); break;
        default: gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf6);
    }

    gtk_widget_set_sensitive (butt, TRUE);

    return TRUE;
}
    
int main (int argc, char *argv[])
{
    ...
    g_timeout_add (400, fn_roll_dice, NULL);

    gtk_main ();

    return 0;
}

Upvotes: 1

Related Questions