Re: Thread programming



On Tue, 2003-06-17 at 10:23, Tristan Van Berkom wrote:
Justin Hopper wrote:

On Tue, 2003-06-17 at 06:01, Tristan Van Berkom wrote:

 

I've read the FAQ on thread programming, but I must be missing 
something
fundamental on how they work.

Any help is greatly appreciated.

     
             

                  

                       

Thread lockups are generally one thread stuck on a lock that's owned
either by another thread or that was locked earlier by the same
thread.

   
           

                

                     

One thing I should mention is that I only have one thread running 
in the
background.  I assume the GUI runs in it's own thread, so that would
make a total of two threads.  I'm not doing anything in the GUI 
when it
locks up, but perhaps it is have trouble just reading the label 
that was
updated in the background thread.  I should also mention that the
background thread never locks up either, it keeps trucking along.

Can anyone confirm whether or not doing simple thread programming will
work while using libglade?



 
         

              

                   

When Havoc said he didnt think libglade was "thread-safe"; I dont 
know if he
meant the run time utility or the code that it generates. It is 
probable that glade
doesn't generate code with mutex locks on object access et cetera et 
cetera
but other than that, If you use glade to generate/parse an XML 
widget layout;
the job is quite simple.

- Use the glade_xml_get_widget(xml, "name") API
- Make _SURE_ that this is done in the same thread that you will 
access the "created objects"
- Use threads to do lengthly background jobs and dont update any GUI 
memory space objects
                 

from that thread. (i.e. if you do a db query that returns 10 
               

elements every `n' millies; pass those
to the gui thread to update your "listview" or whatnot; dont update 
directly).
- If you use "sources" in your thread (i.e. g_timeout_add), make 
sure that your thread has its own
GMainLoop running.

If you follow this strict practice, I garauntee it will work. (well, 
it works for me ;-) )
There have been a bunch of extra stuff added to make thread 
programming in gtk
more solid et cetera since "gtk+-2.x" (i.e. gdk_threads_enter/leave) 
which allow you
to do even cooler stuff.

Note. gdk_threads_enter/leave are by no means nescisary if you 
follow the above prectice.
(I'm guessing that this is just a wrapper for some global mutex for 
the "drawing kit").

Also ofcourse I have to agree with Havoc about the backtrace;
may I recommend Valgrind ? it will tell you where and when a posix 
mutex
gets locked _or_ unlocked twice by the same thread (I though that was
pretty neat).

Cheers,
                -Tristan

       

            

                 

Thanks, Tristan (et al.).  This seems like a very logical approach. 
Just as a side note, I tried debugging the application with gdb, but it
only showed:

[New Thread 1080082400 (LWP 28716)]
[New Thread 1090956592 (LWP 28717)]

So, I would probably have to use something like Valgrind to see where
the collision is occuring.

However, I'd rather pursue the idea of sending a message to the main
thread to update the GUI, and have all GUI updates done through that. 
Now my question is obvious: how do I notify the main loop?  I saw a few
examples of gtk_signal_emit_*, but where do I catch these signals in the
gtk_main loop and update certain things?  Just to make sure I've given
the right background information:

// main.c
...
pthread_create(&check_tid, NULL, check_queues, window1);
...
gdk_threads_enter();
gtk_main ();
gdk_threads_leave();

The thread that is spun off will have data that needs to be passed to
the main loop for the GUI.  Using this method, do I need to enter and
leave threads in main.c or in the check_queues function?

Thanks again for all the help.


     

          

               

=== <snip from gdk_threads_enter doc> ==========
This macro marks the beginning of a critical section in which GDK and 
GTK+ functions can be called. Only one thread at a time can be in such 
a critial section.
==========< end snip >====================

I dont know if putting your gtk_main() inside a locked 
`gdk_threads_mutex' is common
practice or something like that but AFAIK, its useless if you dont 
plan on updating the
gui from more than one thread; and even then, you wouldnt put your 
entire gtk_main
between.

As for communication; you could do that a number of ways :-)

This could work,

- Create a GQueue and a GMutex to handle the queue.
- in your main (GUI updating) thread, create a g_timeout_add
 function and every once and a while, come and check the queue like so
 ...
 g_mutex_lock(queue_mutex);
 /* process data in queue */
 g_mutex_unlock(queue_mutex);
 ....

 - in your child thread; when you have ready data; lock your mutex,
 add data to the queue and unlock mutex.

It would usualy be nicer to be "event based" and use the GCond API but
that would defeat the purpose (putting the GUI thread to sleep *doh*). 
This
polling method should keep the GUI responsive.

Cheers,
                            -Tristan
   

        

             

Actualy, (*doh*) you could make a socket or a named pipe and write from the
child and read in the parent. This way you avoid dumb polling.
(why did I sugest that ?)

Cheers,
                 -Tristan
 

      

           

One thing I still don't understand: I only have one thread created, and
it just updates the text of some labels.  So, this thread is not
colliding with any other threads I've created, but perhaps is colliding
with the gtk_main() thread.  So basically, my check_queues thread is the
only thread that is making updates to the GUI, unless the gtk_main()
thread is trying to make updates to the widgets as well.

So creating a socket or named pipe would not seem to help me currently,
or creating another thread that just does GUI updates, as that is
basically all my current thread does.

I'm starting to think that I the gtk_main() thread is getting locked out
of reading/updating the widgets and that I just need to somehow get a
lock on these labels before I update them in the check_queues thread.  A
mutex would work great, except how would I get the gtk_main() thread to
even be aware of the mutex and use it.

Perhaps I'm going about this all wrong?


    

         

Ok, where did we start out.

==============<snip>==============
...

g_thread_init(NULL);
...
pthread_create(&check_tid, NULL, check_queues, window1);
... 
gdk_threads_enter();
gtk_main ();
gdk_threads_leave();


// check_queues.c
  while (1) {
 ...(check some mysql database for data)...
      gdk_threads_enter();
 label = lookup_widget(GTK_WIDGET(toplevel),"label_name");
 gtk_label_set_text(GTK_LABEL(label), "whatever");
 gdk_threads_leave();
  }

==============<snip>==============
I'm afraid this code _obviously_ locks up before you call gtk_main()
unless the freak occurence that your testing (1) in your while statement
at the same time.

gdk_threads_enter/leave are a mutex lock around a critical section. (can I be
more clear ?)

FORGET gdk_threads_enter/leave!

void main() {
...
g_thread_init(NULL);

g_io_channel_unix_somethingorother_new(hook to a pipe or socket or something
                                 to call, label_update_function);
...
pthread_create(&check_tid, NULL, check_queues, window1);
... 
gtk_main ();

}

label_update_function() {
  read(socket_fd, updated stuff from database);
  label = lookup_widget(GTK_WIDGET(toplevel),"label_name");
  gtk_label_set_text(GTK_LABEL(label), "whatever");
}



// check_queues.c

  while (mysql condition maybe ?) {
 ...(check some mysql database for data)...
 write(socket_fd, the data that should go on the label);
  }


I think we're suffering from a lack of proper communication.

I very much hope that this is clear to you now :-)

Cheers all!,
                  -Tristan
  

       

I think the body of this thread might be getting out of control.  I'm
tempted to clear it, but the mailing list etiquette cops will hunt me
down and destroy me.....

OK, I now fully understand, and thank you for posting that bit from the
threads doc, that entering and leaving threads is not going to be needed
in my app.

What I still don't understand: where is the collision occurring? 

     

If I base myself on your original code; your main executions space locks 
a mutex
that will only unlock when `gtk_main_quit()' gets called and your db query
thread will only execute then. (or it may have done exactly one itteration
before `gtk_main()')

   

If I only have one thread, and only that thread is making updates to the GUI,
why does the GUI itself (and I assume the gtk_main() thread), lock up? 


     

what is `one thread' ? once you `pthread_create' ; you have two, do you 
not ?
who cares. lets say you have "two executions spaces"; that works.

the `void main() {}' execution space; the one that has the gtk_main() in 
it should
update the gui. if you want to update the gui from a child execution space;
put the gtk_main() in the child instead.

In other words, as long as the functions you write that update the gui are
marshalled by the GMainloop in gtk_main() (i.e. run in the same space),
you shouldn't have any race conditions.

   

I think until I have an answer to this, I won't understand how any code
rework will help, as they should all run into the same problem of making
updates to the GUI and then having the GUI lock up.

Thanks for everyone's patience.


     

Bah, I always take an hour or so reading/writing lists, it puts
variety in the day and keeps the technolagies fresh in my head.

Cheers,
                                               -Tristan

PS:
  someone threw `GAsynchQueue' in the mix, you should read up,
its a good idea. (I'll use that next time).
   


The app works correctly now.  I changed the code to the following, just
to see if the idea would work:

// main.c
...
g_thread_init(NULL);
...
pthread_create(&check_tid, NULL, run_main, NULL);
check_queues(window1);

// that's the end, no entering or leaving threads

// run_main thread function
gtk_main();

// check_queues.c
...
while (loop)
  ...(check databases for info)...
  label = lookup_widget(GTK_WIDGET(toplevel),"label_whatever");
  gtk_label_set_text(GTK_LABEL(label), rtq->tickets_new);
  sleep(sometime);
 

You may have race conditions here (or you DO have race conditions here).
what happens when the area of your label is marked "dirty" and is being
redrawn at the same time as your other thread is performing a 
"gtk_label_set_text" ?

this is why you want to use a GAsynchQueue or a GIOChannel or something for
your child thread to tell mister "gtk_main()" thread _instead_ of calling
gtk_label_set_text directly. (why does it seem so complicated to say 
that you should
update the GUI in the SAME thread that gtk_main() is running in ?)

Ok, last question, and I really have learned a lot from all of this:

How do I make updates to the GUI from the same thread that gtk_main() is
run from, since that thread gets tied up as soon as gtk_main() gets
called?  I'm sure I'm being stupid here and I'm missing something
fundamental, but I understand that I should be making all GUI updates
from the same thread as the gtk_main() thread, to avoid lockups and race
conditions, but I'm just not seeing how to do that.

I should be set after this.


This seems to work just fine.  The GUI thread does not lock up, and the
check_queues thread does not lock up either.  I could probably put the
gtk_main() function as the function being called in the pthread_create
function.

I think I was thrown off by the GTK+ FAQ on thread programming.  I must
have implemented it wrong, as I'm sure the example in the FAQ runs, but
when I did something similar, I had all the collisions.

Is there a good reference out on the web somewhere for thread
programming in glib/gtk+?  I don't want to bother the list members with
my constant minor questions.
 

http://developer.gnome.org/doc/API/2.0/glib/glib-Asynchronous-Queues.html
http://developer.gnome.org/doc/API/2.0/glib/glib-Threads.html

One last question I have is how do I kill my check_queues thread once
the gtk_quit() function has been called to exit the application?  I
assume there is a pthread_kill(threadID) function?  I was unable to find
anything so far, and it would be preferable to have someone point me in
the right direction first before I find something that I think *might*
work but is again the wrong approach entirely.
 

you could set some variable called "quit" to true and then call 
g_thread_join() in your
main thread, and in your "check_queues" thread check it once per loop 
and: if (quit) g_thread_exit();
after g_thread_join returns in the parent, call gtk_main_quit ;-)


Cheers,
                                -Tristan


_______________________________________________
gtk-app-devel-list mailing list
gtk-app-devel-list gnome org
http://mail.gnome.org/mailman/listinfo/gtk-app-devel-list
-- 
Justin Hopper <gus gusalmighty com>




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