After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 615480 - Advanced usage of GMainLoop fails under Windows
Advanced usage of GMainLoop fails under Windows
Status: RESOLVED WONTFIX
Product: glib
Classification: Platform
Component: win32
2.22.x
Other Windows
: Normal normal
: ---
Assigned To: gtk-win32 maintainers
gtk-win32 maintainers
Depends on:
Blocks:
 
 
Reported: 2010-04-11 21:11 UTC by Paolo Bonzini
Modified: 2010-04-13 09:54 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
test program (4.79 KB, text/x-csrc)
2010-04-11 21:11 UTC, Paolo Bonzini
Details

Description Paolo Bonzini 2010-04-11 21:11:28 UTC
Created attachment 158450 [details]
test program

The attached program uses g_main_context_wait in order to coordinate between two threads that both attempt to use the main loop.

To add to the complication, the program also uses the GMainLoop API "manually", as described under "Customizing the main loop iteration" (http://library.gnome.org/devel/glib/stable/glib-The-Main-Event-Loop.html).  The threads cooperate using a condition variable so that prepare/query/poll/check run in one thread, and dispatch runs in the other.  The calls however are perfectly serialized, as if they were running on one thread only.

(The program is somewhat abstracted from the GNU Smalltalk bindings to GTK+; I can provide more details on why I did this, if necessary).

The program runs under Linux perfectly, but deadlocks under Windows.  I tested under Wine and Windows XP, with the same results.

My guess is that somehow condition variables are not correctly implemented, but I have not attempted to debug it.  In this case, this would be just a symptom of a more severe problem.
Comment 1 Tor Lillqvist 2010-04-12 08:28:11 UTC
As you sure know, one can't call gtk+ from multiple threads on Windows. It is known to not work.

Of course, that is just a simplification, the actual details are quite technical, and I certainly don't understand/remember them in details... Anyway, one thing it actually means is that one can't call gtk+ functions on one thread and run the GLib main loop that among other things polls for window messages in another thread. 

You call gtk_init(), gdk_threads_init(), gtk_window_new() and gtk_widget_show() from the initial thread but then run the main loop in another thread.

Also, I think there can't be any reason to ever use gdk_threads_init() on Windows, as the whole idea of that function is to prepare gdk to be called from multiple threads, isn't it?

Note that as soon as _gdk_events_init() in gdk/win32/gdkevents-win32.c is called, a polling source for window messages is set up and attached to the default context. _gdk_events_init() gets called from gdk_display_open() which gets called from gtk_init(). But as such that does not seem to be a problem, it is the default context which is used in the test program.

But still, if I understand correctly, what you want to do is to run the main loop in one thread, and do gtk+ calls in another thread. I don't think that can work, as g_poll uses MsgWaitForMultipleObjectsEx() when it notices that it needs to poll both for "normal" handles and messages. The MsgWaitFor* functions as far as I understand poll for messages to the thread's message queue. Messages for windows are sent to the message queue of the thread that created the window. So if you create windows in one thread, a g_poll() in another thread will never wake up to the messages for those windows.

But, if I modify your test case to do the gtk_init() and window creation in the main_loop_thread() function, it still doesn't seem to work sensibly. This time it doesn't get stuck waiting forever, but the opposite, it just loops tightly and never gets to even draw the inital empty contents of the created window for instance...

It's hard to say whether this is because of actual fixable bugs in the Win32 code in GLib, or because of fundamental limitations.

I am afraid that the summary of this bug pretty much is a statement of a fact... Advanced usage of GMainLoop fails under Windows. Period.
Comment 2 Tor Lillqvist 2010-04-12 08:44:47 UTC
Hmm, actually I think it's pretty clear why a modification as I say above (where the gtk_init() call and window creation is done in main_loop_thread() still can't work, as it is the other thread that does the dispatching, i.e. calls the gdk event handler, so then again we end up calling gtk+ from multiple threads.
Comment 3 Paolo Bonzini 2010-04-13 08:19:11 UTC
First of all, thanks for the great answer.  I was not overly optimistic because this looks like an "academic" issue, so this was much better than I expected.  The only better thing could have been a fix, but hey...

(BTW it's simplified from real-world code; I'm using this in a virtual machine to run the glib main loop in the background and send the wakeups to the VM.  I'm calling gdk_threads_init in the example just because the program is supposed to run under both Unix and Win32).

> As you sure know, one can't call gtk+ from multiple threads on Windows. It is
> known to not work.

Yes, I thought I was circumventing the limitation but I forgot message queues are per-thread.

> The MsgWaitFor* functions as far as I understand poll for
> messages to the thread's message queue.

... this looks like a deal breaker for a seamless rendez-vous approach as the one implemented in the test program.  There's no way to work around that, other than using the undocumented win32k.sys interfaces (maybe fun, but not practical).

I suppose under Win32 I'll have to resort to polling.  I can wrap g_poll and check for G_WIN32_MSG_HANDLE.  If it is present, I ask the client if it is idle and, if not, I use a small timeout such as 20 ms.

Closing.
Comment 4 Paolo Bonzini 2010-04-13 09:54:46 UTC
(That, and I have to move the check phase in the dispatching thread, too).