my summary on how to use gtk with threads



Hi,

here is my short summery of how to use gtk with threads, ordered by the
main problems to be worked around. I aimed to find a small set of
*general* rules to follow rather than solutions for individual cases.
So this guides shall turn gtk's thread awareness into thread
safety--for those who need that.

All this has been tested and works for me--but may have some flaws, so
especially if you have doubts this was a reasonable approach please
comment! :-)


(1) SYNCHRONIZE THREADS
Nothing new: All access to gtk needs to be wrapped into
gdk_threads_enter/leave function pairs.


(2) DEADLOCK WHILE CONSTRUCTING COMPLEX WINDOWs?
Even when you follow rule (1) you might freeze gtk. This happens
easily when you e.g. write functions to create widgets or groups of it
and then use this functions in higher contexts: So you call
gdk_threads_enter() twice before gdk_threads_leave().

A general and most comfortable way for a later easy use of gtk is
using recursive mutexes like this (see below). So, subfunctions can lock
as much as they want to--gdk_threads_enter/leave is called only once.

(a) In your app replace gdk_threads_enter()/leave() functions by calls
to your own _enter/_leave functions.

(b) have _enter() look like this:
- lock_recursive_mutex(...)
- check if this is lock level 1, if yes: call gdk_threads_enter()

(c) have _leave() look like this:
- check if this is lock level 1, if yes: call gdk_threads_leave()
- unlock_recursive_mutex(...)

(d) leave the *direct* calls to gdk_threads_enter/leave around your
gtk_main() or gtk_main_iteration() as is (otherwise you trap into a
deadlock on the second call to it)

Note: Do not use gdk_threads_set_lock_functions() to facilitate (a)
since you may encountere the same problems as me (strange unlocking
before locking issues, again gtk freezes etc).


(3) DEADLOCKs AT RUNTIME
Even if you follow the rules (1) and (2) gtk may freeze in situations
like these: You have coded a thread which reads a value from a gtk
spinbutton. This works as long as you do not launch the thread by a
button click and wait for the threads return. The reason: The button
click event locks the gdk lock automatically, and the thread tries to
lock also and has to wait--however the button waits for the thread ->
deadlock.

The general solution which I use is a bunch of work, however it works
here in various situations (if there are easier solutions let me know):
We need to seperate all events like button clicks (see a below) and data
(see b) from gtk. So, the event's code can return immediately and the
gdk lock is unlocked again; after that the user's code starts. This
implies these two steps:

(a) Wrap the needed widgets into your own structures which hold event
callback(s) to the user. Here I assume a button widget. On creation
pass the user's click event callback. Connect the original gtk's button
click event to a static wrapper which calls the user's callback using a
thread (to optimize: think thread pools or idle functions). To make life
a little easier for the client programmer, connect gtk's widget destroy
signal to a function which cleans up the user's widget wrapper
struct. All this (a) applies not only to *events* but also to any gtk
*actions* like renaming a label etc. for which little delayed
execution is o.k.

(b) When you need to query gtk widgets (let's say you need a
spinbutton's value) however you *cannot* launch a thread since you need
the result immediately. So, for these cases, (a) does not apply. To
achieve this your widget struct needs to additionally hold the widgets
data (e.g. a double for a spinbutton, a string for a labels name) which
can be returned *without* entering gtk at all. Keep in mind that when
destroying your widget wrapper struct, you may need to free a labels
name string as well.

Thank's for reading ;-)

Felix



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