开发者

deadlock problem with threads in GTK

In my GUI, I have a list store treeview in my main window. When the user double clicks a row, a dialog pops up. The problem is that the data I populate in the dialog box takes a while to process, so what I've done is started a thread (using boost threads) to do the dialog box calculations.

In main:
.......
g_signal_connect (G_OBJECT (m_treeview), "row_activated", G_CALLBACK (m_row_activated),
                  (gpointer) main_window);
.......

In m_row_activated:
.........
// combo_box and dialog are GtkWidget* global variables 
create_dialog(dialog, combo_box); // function creates the combobox
set_combo_box_with_loading_message;
gtk_widget_show_all (dialog);
thread m_thread (bind (&do_dialog_calculations, data1, data2, combobox));
.........

In do_dialog_calculations:
.........
// do_calculations takes about 15 seconds to complete
do_calculations(MyData data1, MyData data2, combobox);
gdk_threads_enter();
gtk_combo_box_append_text(...);
gdk_threads_leave()

Everything works fine (i.e. when the user double clicks a row, a dialog pops up immediately with a loading message and it is populated eventually when the thread returns), but my problem is when the user closes the dialog before do_calculations in do_dialog_calculations completes. If the dialog is destroyed, my combobox within it will be destroyed and my call to gtk_combo_box_append_text will seg fault.

I tried to test the combo box before updating it:

In do_dialog_calculations:
.........
do_calculations(MyData data1, MyData data2, combobox);
gdk_threads_enter();
if (GTK_IS_COMBO_BOX (combobox))
   gtk_combo_box_append_text(...);
gdk_threads_leave()

but this results in a deadlock at the call to GTK_IS_COMBO_BOX. I think this is beause GTK_IS_COMBO_BOX probably calls gdk_threads_enter(). I've also tried testing 开发者_C百科for NULL

 if (combobox == NULL)

but that doesn't seem to work either. Any suggestions on how to get around this problem?

UPDATE: The deadlock at GTK_IS_COMBO_BOX only occurs if I close the dialog immediately after it opens (i.e. before do_calculations() completes. If I just let the dialog sit, it will eventually update. Also, if I switch the combobox check before writing calling gdk_threads_enter():

if (GTK_IS_COMBO_BOX (combobox)
{
   gdk_threads_enter();
   gtk_combo_box_append_text(...);
   gdk_threads_leave();
}

No deadlock occurs when I destroy the dialog before this code executes. However, I'm afraid of the rare possibility that the user will close the dialog after the GTK_IS_COMBO_BOX check completes.

PS - I use threads to do my dialog box calculations because the dialog boxes are non modal, and I want the user to be able to do other things with the main UI while the dialog boxes populate.


I think this is beause GTK_IS_COMBO_BOX probably calls gdk_threads_enter()

I don't think this is the case. These macros are generally pretty simple and I wouldn't expect it to take a lock. In fact, as far as I can tell the whole idea of gdk_threads_enter is that the library itself shouldn't call this, only code that knows it's running in another thread should.

Here's my idea: Did you forget to call g_thread_init and gdk_threads_init?

Also, one thing to keep in mind... By default, gdk_threads_enter is not using a recursive mutex. Though some people have religious objections to recursive mutexes, it's possible to have gdk_threads_enter use one:

static GStaticRecMutex my_gdk_lock;

static void my_gdk_lock_enter() {g_static_rec_mutex_lock(&my_gdk_lock);}
static void my_gdk_lock_leave() {g_static_rec_mutex_unlock(&my_gdk_lock);}

// ...

   g_thread_init(NULL);

   g_static_rec_mutex_init(&my_gdk_lock);

   gdk_threads_set_lock_functions(G_CALLBACK(my_gdk_lock_enter),
                                  G_CALLBACK(my_gdk_lock_leave));

   gdk_threads_init();

// ...

Update: From your comment it sounds like you have a race condition between destroying the dialog and populating the combo box. One potential solution is that you bump up the reference count of the combo box (i.e. gtk_widget_ref) so that it doesn't get freed while your asynchronous worker is doing something. Then release it with gtk_widget_unref when the other thread no longer needs the pointer.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜