Re: GTK and threads



kornelix wrote:
Thanks. I eagerly anticipate your example.

I have attached a sample file that uses threads in the way you described in your last post. The gui consists of a single text display and a button. When you click the button, a new thread is started that periodically (every second) sends a message to the text window. When you close the window, the program will send a signal to each thread to terminal (well not a real signal, just sets a flag). Once all the threads have terminated, the program ends. I have not implemented a proper signalling system to send signals to the threads nor have I used the pthread_join to properly rejoin threads that have quit. Mainly because pthread_join blocks and that causes problems.

You'll notice the on_destroy callback has a loop that iterates the gtk event loop while we're waiting for the threads to stop. This is because we're in a callback, already holding the gdk_thread_lock, so any waiting in here will prevent threads from aquiring their lock and thus finishing their loops. So we iterate it.

The callbacks (including the pthread_create calls) already occur in the context of the main gdk lock, so no explicit locking is needed there.

See what you think.



I made a small code example like the one you described. It hung up. Then I figured out why: I am calling a function that has the gdk_threads_enter() and _leave() wrappers from main() as well as from a created thread. In the case of main(), the call to gdk_threads_enter() was a second call for that thread, and this is apparently fatal. I have to figure out how to detect and avoid this, or make multiple versions of the functions with and without the wrappers. If I call the function from the thread only, it works.


I really am not following what you are describing (please provide code) but it feels like you're definitely doing something wrong. It needn't be this complicated from my experience.

One way of looking at the issue is that the FAQ example includes all main() code within the thread wrapper functions, meaning that any called functions better not have the wrappers. Would it be legitimate to treat main() like any other thread, and put the wrappers only around gtk/gdk calls? If I could do this, then functions would be usable from both main() and created threads.

Hmm. Sounds complicated. My gut feeling is that most of your difficulties are in not knowing when you are in the gdk lock already and when you need to initiate a lock. Please tell me if my example code addresses any of the problems you have been having. (and for the real GTK experts out there, feel free to point out where I'm doing everything wrong.)

Michael



It will not take long, so I will just try this.

regards



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

gint thread_count=0;
gint dead_threads;

GSList *threads;

struct thread_info_data {
        GtkTextBuffer *buffer;
        gint id;
        pthread_t tid;
        gint terminate;
};

void thread (void *data) {
        struct thread_info_data *info;
        GtkTextIter iter;
        GString *message;


        info=(struct thread_info_data *)data;
        message=g_string_new("");

        while(info->terminate == 0 ) {
                sleep(1);
                g_string_printf(message,"Thread %d here.\n",info->id);
                g_print("%s",message->str);
                
                gdk_threads_enter();
                gtk_text_buffer_get_end_iter(info->buffer,&iter);
                gtk_text_buffer_insert(info->buffer,&iter,message->str,-1);
                gdk_threads_leave();
        }
        g_print("Thread %d stopping.\n",info->id);
        gdk_threads_enter(); //borrowing a lock from gtk to lock our variable
        dead_threads++;
        gdk_threads_leave(); //we could and should use a counting semaphore
        g_string_free(message,NULL);
}

void stop_thread(gpointer data, gpointer user_data) {
        struct thread_info_data *thread;

        thread=data;
        thread->terminate=1;
        g_print("Asked thread %d to stop.\n",thread->id);
        
        //here we ought to make sure the thread really has died
//      pthread_join(thread->tid,NULL);
}

void on_destroy(GtkWidget *widget, gpointer data) {
        g_slist_foreach(threads,stop_thread,NULL);

        while(dead_threads < thread_count) {
                while(gtk_events_pending()) {
                        gtk_main_iteration();
                }
                sleep(0);
        }
        gtk_main_quit();
}

void on_button_clicked(GtkWidget *widget, gpointer data) {
        GtkTextBuffer *buffer;
        GtkTextIter iter;
        struct thread_info_data *thread_info;
        
        buffer=GTK_TEXT_BUFFER(data);

        thread_info=g_malloc(sizeof(struct thread_info_data));
        thread_info->buffer=buffer;
        thread_info->id=thread_count++;
        thread_info->terminate=0;

        threads=g_slist_append(threads,thread_info);

        pthread_create(&(thread_info->tid),NULL,thread,thread_info);
        

        gtk_text_buffer_get_end_iter(buffer,&iter);
        gtk_text_buffer_insert(buffer,&iter,"Button clicked!\n",-1);
        g_print("Button clicked.\n");


}


int main(int argc, char *argv[]) {
        GtkWidget *window;
        GtkWidget *button;
        GtkWidget *vbox;
        GtkWidget *viewarea;
        GtkWidget *view;
        GtkWidget *buffer;

        threads=NULL;


        g_thread_init (NULL);
        gdk_threads_init ();

        gtk_init(&argc, &argv);

        window=gtk_window_new(GTK_WINDOW_TOPLEVEL);

        gtk_signal_connect(GTK_OBJECT(window),"destroy",
                           GTK_SIGNAL_FUNC(on_destroy),NULL);
        gtk_container_set_border_width(GTK_CONTAINER(window),10);

        vbox=gtk_vbox_new(FALSE,5);
        gtk_container_add(GTK_CONTAINER(window),vbox);
        gtk_widget_show(vbox);

        viewarea=gtk_scrolled_window_new(NULL,NULL);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewarea),
                                       GTK_POLICY_AUTOMATIC,
                                       GTK_POLICY_AUTOMATIC);
        gtk_box_pack_start(GTK_BOX(vbox),viewarea,TRUE,TRUE,2);
        gtk_widget_show(viewarea);
        
        view=gtk_text_view_new();

        buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

        gtk_text_buffer_set_text(buffer,"The threads will write here.\n",-1);
        gtk_container_add(GTK_CONTAINER(viewarea),view);
        gtk_widget_show(view);
        
        button=gtk_button_new_with_label("Click me");
        gtk_signal_connect(GTK_OBJECT(button),"clicked",
                           GTK_SIGNAL_FUNC(on_button_clicked),buffer);
                           
        gtk_box_pack_end(GTK_BOX(vbox),button,FALSE,FALSE,3);
        
        gtk_widget_show(button);
        gtk_widget_show(window);

        gdk_threads_enter ();
        gtk_main();
        gdk_threads_leave ();

        return 0;
}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]