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