views:

179

answers:

3

Hello,

I have simple C/gtk+ application. I have function in this app which load image in gtkimageview widget:

gboolean 
main_win_open( MainWin* mw, const char* file_path)
{
  ...       
  //loading and displaing image in gtkimageview
  ...
}

The loading image is work, but i need to run this function in another thread then main gui form;

I have function:

void*
argument_thread(void *args)
{
  Data *data = (Data*)args;
  gdk_threads_enter();
  main_win_open (data->win,data->argv);
  gdk_threads_leave();
}

Data it's structure for main_win_open function arguments:

typedef struct _Data
{
  MainWin *win;
  char* argv;
} Data;

Then in main function i create thread and run it:

int main(int argc, char** argv)
{
    GError*   err;
    GThread*  thread;   
    Data data;
    MainWin *win;

    // init thread support
    if(!g_thread_supported())
        g_thread_init(NULL);
        gdk_threads_init();

    // init GTK+
    gtk_init (&argc, &argv);

    win = (MainWin*)main_win_new();
    gtk_widget_show(GTK_WIDGET(win));

    data.win = win;
    data.argv = argv[1];

    if (argc == 2)
    {
       thread = g_thread_create((GThreadFunc)argument_thread,&data,FALSE, &err);
    }

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

But when i try to run app from command line and try to load big-size image, gui is blocking.

What's wrong?

Thank you.

A: 

In the documentation from gdk_threads_enter():

"This macro marks the beginning of a critical section in which GDK and GTK+ functions can be called safely and without causing race conditions. Only one thread at a time can be in such a critial section."

If I don't get it wrong, this is the cause why your app is still blocking. For GThread'ed apps the calls to gdk_threads_enter/_leave are unnecesary, so try removing them.

Matachana
gdk_threads_enter/_leave are totally unnecesary in GThread applications. GThread is the thread implementation of GLib and doesn't require any usage of GTK+. If you need mutexes or locks with GThread, you should use G_LOCK, GMutex, etc.gdk_threads_enter locks the GTK+ thread and probably these causes the block. Use the macros gdk_threads_enter()/_leave() when you need to modify GTK widgets, no while the image loading.
Matachana
Sorry, you are right. I posted without thinking. I have removed my comment, and I will un-downvote your answer when my vote unlocks. However, I still believe your answer is incorrect, because calling `gtk_main()` without `gtk_threads_enter()`/`_leave()` in an application that uses GTK functions from more than one thread is undefined behavior. So the program will not work, or work unpredictably, if those calls are removed.
ptomato
Also, the GTK main loop unlocks the GDK lock during every iteration, so that can't be what's blocking it.
ptomato
What do you mean?
shk
ptomato is right, it should not affect to the GUI responsiveness the gdk_threads_enter/_leave calls. You can safely remove the gdk_threads_enter/_leave calls made on argument_thread function and put that calls surrounding the gtk_* function calls made on main_win_open.
Matachana
+1  A: 

If you call gdk_threads_enter() before you begin to load the image, then the GUI would be frozen and unresponsive while the image is loading. Basically the same result you get by first loading the image and then creating the GUI, in one single thread. What you probably want is to call gdk_threads_enter() after the time consuming task of loading the image and only claim the GDK-lock while you quickly update the GUI.

The locking of GDK is needed because GTK+ is not thread safe. The X11 (and probably also mac) implementation is however threadaware, which means that any API can be used by any thread (but of course only one at a time hence the lock).

The windows API on the other hand can not be used from any thread in this manner, and therefore not the windows version of GTK+ either. Without understanding the details about the windows API it is a good idea to only call GTK+ from one single thread. This doesn't mean that threaded applications are out of the question. You can still load an image in another thread and then use, for example, gtk_idle_add() to make the GUI update in the main thread.

Grim
+1  A: 

Re the code posted here http://pastebin.com/BkLJ7UkR

I don't think you CAN do that sort of stuff off the main thread... (Well the reading from a file certainly, but not the GdkPixbuf stuff)

what you should do is split your main_win_open into two parts, the one that does the file reading and the one that shoves the data into the pixbuf loader then call the second function through g_idle_add() (which is thread safe, it'll add a callback to the mainloop)

If you want to do this I'd suggest loading the entire image into memory in one go then passing that through g_idle_add

However I strongly doubt any of this stuff is needed... how big an image are you loading that it spends an even remotely noticeable amount of time loading it? (Basically what I think you are doing wrong is using threads when they are very much not needed)

Spudd86