Re: GTK and threads
- From: kornelix <kornelix yahoo de>
- To: Michael Torrie <torriem chem byu edu>
- Cc: gtk app dev mail list <gtk-app-devel-list gnome org>
- Subject: Re: GTK and threads
- Date: Tue, 07 Feb 2006 12:53:39 +0100
Michael, thanks for your example. It works fine.
I had done almost the same thing (see code below), but my mistake was in
trying to call the function below from event-response functions within
main() as well as from threads. In the case of main(), which is
completely enclosed within gdk_threads_enter() and gdk_threads_leave(),
the nested call to gdk_threads_enter() results in a total lockup of the
application. Some of my other (defective) variations caused a total
lockup of the gnome desktop.
I am now trying to figure out my next approach:
1. Implement some kind of thread-specific global memory where I can save
the GDK lock status of the thread and avoid the nested calls to
gdk_threads_enter(). This seems impossible without modifying all
functions to pass a thread-number argument or a pointer to the
thread-specific memory.
2. Versions of my functions with and without the gdk_threads_enter() and
_leave() calls (locking version calls non-locking version). Now I still
have to figure out if being called from main() or a thread, or from
another function that locks.
3. Do all GDK/GTK stuff within main() response functions, and deal with
the application complexity of passing data back and forth between these
functions and the thread functions. I am inclining to this because it is
safe and sure.
QUESTION
I was hoping to find a way to send a text message to a window and have a
corresponding response function, as for a menu selection or the
"destroy" signal. I cannot find such a function, but surely this exists.
regards
Mike
=================================
// print to a text view window ala printf()
void wprintf(GtkTextView *textWin, char *mess, ... )
{
GtkTextBuffer *textBuff;
GtkTextIter textIter;
GtkTextMark *textMark;
va_list arglist;
char message[200];
va_start(arglist,mess);
vsprintf(message,mess,arglist);
va_end(arglist);
gdk_threads_enter();
textBuff = gtk_text_view_get_buffer(textWin);
gtk_text_buffer_get_end_iter(textBuff,&textIter);
gtk_text_buffer_insert(textBuff,&textIter,message,-1);
gtk_text_buffer_get_end_iter(textBuff,&textIter);
textMark = gtk_text_buffer_create_mark(textBuff,0,&textIter,0);
gtk_text_view_scroll_to_mark(textWin,textMark,0,0,1,1);
gdk_flush(); // needed ??
gdk_threads_leave();
}
Michael Torrie wrote:
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]