Re: Slaving the GTK+ main loop to an external main loop



On Mon, 2008-09-01 at 15:11 -0400, Owen Taylor wrote:
> One distinct problem for ports of GTK+ to other windowing systems has
> been dealing with modal operations in the windowing systems API; 

[...]

> So all we have to do is do everything before the dispatch() step in
> the thread, signal the main thread do the dispatch() call, wait for
> that to complete, then continue.

I spent some time this week trying to get the idea implemented on OS X;
although I got it working OK (I've attached the patch for reference)
there were some significant bottlenecks that I wanted to write up here
in case anyone else tries this in the future.

The big problems centered around loop ownership and reentrancy -
inherently the main thread has to be the "owner" of the main loop - the
caller of g_main_context_acquire(). If the helper thread is the owner of
the main context, then you will get deadlocks if the application calls a
function like gtk_dialog_run() because the main thread is waiting for
the helper thread to process events to finish the dialog, but the helper
thread is waiting for the main thread to do the dispatch part of the
main loop.

Once the main thread is the loop owner, then you have the problem that
the main loop iteration in the helper thread is only partially in sync
with the operation of the main thread... so a lot of the assumptions
that the GLib main loop makes don't fully work; attempts to do recursive
main loop iterations in particular tend to trigger scary warnings. Also,
you have problems where GTK+ code adds an event to the event queue in
the expectation that the main thread is not blocking, but it is blocking
in the other thread; a call to g_main_context_wakeup() needs to be
added.

Efficiency is also pretty bad. You also get interactions like:

 - Window is resized, and a GDK event is queued
 - Main thread calls g_main_context_wakeup() to wake up the helper
   thread.
 - Helper thread sees the event has been queued and then sends a Cocoa
   event to wake up the main thread.
 - Main thread wakes up and dispatches the event

There were also some quartz specific issues: for one thing, window
resizing was my main target and it there's no easy begin/end
notifications for that; you can use CFRunLoopObserver to detect entry
and leaving the run loop, but that appears as a separate Enter/Leave
notification for every motion event, which makes things even more
inefficient since we have to keep starting and stopping the iteration
in the helper thread.

A second quartz specific problem was simply that I was trying to use the
same helper thread for poll() emulation and for iteration during modal
operations so things got pretty hairy: lots of different states in the
state machine.

During work on this approach, I realized that a more simple and
efficient approach was possible on OS X based on CFRunLoopObserver...
you could simply call the g_main_context_iteration() functions directly
at the appropriate points in the native run loop. An alternate patch
based on that approach came out quite a bit better and can be found at:

 http://bugzilla.gnome.org/show_bug.cgi?id=550942

So the patch attached below, is in my opinion, a dead end as far as OS X
is concerned. I think my conclusion on the general technique is that it
is workable in situations when nothing else is possible, but shouldn't
be a first choice.

- Owen

Attachment: main-loop-separate-thread.patch
Description: application/mbox



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