#include <unistd.h>
#include "gdkprivate-quartz.h"
+#include <gdk/gdkdisplayprivate.h>
/*
* This file implementations integration between the GLib main loop and
* a run loop iteration, so we need to detect that and avoid triggering
* our "run the GLib main looop while the run loop is active machinery.
*/
-static gboolean getting_events;
+static gint getting_events = 0;
/************************************************************
********* Select Thread *********
*/
if (!run_loop_polling_async)
CFRunLoopSourceSignal (select_main_thread_source);
-
- if (CFRunLoopIsWaiting (main_thread_run_loop))
- CFRunLoopWakeUp (main_thread_run_loop);
+
+ /* Don't check for CFRunLoopIsWaiting() here because it causes a
+ * race condition (the loop could go into waiting state right after
+ * we checked).
+ */
+ CFRunLoopWakeUp (main_thread_run_loop);
}
static void *
{
gboolean retval;
- GDK_THREADS_ENTER ();
+ gdk_threads_enter ();
*timeout = -1;
- retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
- _gdk_quartz_event_loop_check_pending ());
+ if (_gdk_display->event_pause_count > 0)
+ retval = FALSE;
+ else
+ retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
+ _gdk_quartz_event_loop_check_pending ());
- GDK_THREADS_LEAVE ();
+ gdk_threads_leave ();
return retval;
}
{
gboolean retval;
- GDK_THREADS_ENTER ();
+ gdk_threads_enter ();
- /* XXX: This check isn't right it won't handle a recursive GLib main
- * loop run within an outer CFRunLoop run. Such loops will pile up
- * memory. Fixing this requires setting a flag *only* when we call
- * g_main_context_check() from within the run loop iteraton code,
- * and also maintaining our own stack of run loops... allocating and
- * releasing NSAutoReleasePools not properly nested with CFRunLoop
- * runs seems to cause problems.
- */
- if (current_loop_level == 0)
- {
- if (autorelease_pool)
- [autorelease_pool release];
- autorelease_pool = [[NSAutoreleasePool alloc] init];
- }
-
- retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
- _gdk_quartz_event_loop_check_pending ());
+ if (_gdk_display->event_pause_count > 0)
+ retval = FALSE;
+ else
+ retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
+ _gdk_quartz_event_loop_check_pending ());
- GDK_THREADS_LEAVE ();
+ gdk_threads_leave ();
return retval;
}
{
GdkEvent *event;
- GDK_THREADS_ENTER ();
+ gdk_threads_enter ();
- _gdk_events_queue (_gdk_display);
+ /* Refresh the autorelease pool if we're at the base CFRunLoop level
+ * (indicated by current_loop_level) and the base g_main_loop level
+ * (indicated by g_main_depth()). Messing with the autorelease pool at
+ * any level of nesting can cause access to deallocated memory because
+ * autorelease_pool is static and releasing a pool will cause all pools
+ * allocated inside of it to be released as well.
+ */
+ if (current_loop_level == 0 && g_main_depth() == 0)
+ {
+ if (autorelease_pool)
+ [autorelease_pool drain];
+
+ autorelease_pool = [[NSAutoreleasePool alloc] init];
+ }
+
+ _gdk_quartz_display_queue_events (_gdk_display);
event = _gdk_event_unqueue (_gdk_display);
if (event)
{
- if (_gdk_event_func)
- (*_gdk_event_func) (event, _gdk_event_data);
+ _gdk_event_emit (event);
gdk_event_free (event);
}
- GDK_THREADS_LEAVE ();
+ gdk_threads_leave ();
return TRUE;
}
NSDate *limit_date;
gint n_ready;
+ static GPollFD *last_ufds;
+
+ last_ufds = ufds;
+
n_ready = select_thread_start_poll (ufds, nfds, timeout_);
if (n_ready > 0)
timeout_ = 0;
else
limit_date = [NSDate dateWithTimeIntervalSinceNow:timeout_/1000.0];
- getting_events = TRUE;
+ getting_events++;
event = [NSApp nextEventMatchingMask: NSAnyEventMask
untilDate: limit_date
inMode: NSDefaultRunLoopMode
dequeue: YES];
- getting_events = FALSE;
-
- if (n_ready < 0)
+ getting_events--;
+
+ /* We check if last_ufds did not change since the time this function was
+ * called. It is possible that a recursive main loop (and thus recursive
+ * invocation of this poll function) is triggered while in
+ * nextEventMatchingMask:. If during that time new fds are added,
+ * the cached fds array might be replaced in g_main_context_iterate().
+ * So, we should avoid accessing the old fd array (still pointed at by
+ * ufds) here in that case, since it might have been freed. We avoid this
+ * by not calling the collect stage.
+ */
+ if (last_ufds == ufds && n_ready < 0)
n_ready = select_thread_collect_poll (ufds, nfds);
if (event &&
static void
run_loop_entry (void)
{
- current_loop_level++;
-
if (acquired_loop_level == -1)
{
if (g_main_context_acquire (NULL))
}
else if (timeout > 0)
{
- /* We need to get the run loop to break out of it's wait when our timeout
+ /* We need to get the run loop to break out of its wait when our timeout
* expires. We do this by adding a dummy timer that we'll remove immediately
* after the wait wakes up.
*/
static void
run_loop_exit (void)
{
- g_return_if_fail (current_loop_level > 0);
-
- if (current_loop_level == acquired_loop_level)
+ /* + 1 because we decrement current_loop_level separately in observer_callback() */
+ if ((current_loop_level + 1) == acquired_loop_level)
{
g_main_context_release (NULL);
acquired_loop_level = -1;
GDK_NOTE (EVENTLOOP, g_print ("EventLoop: Ended tracking run loop activity\n"));
}
-
- current_loop_level--;
}
static void
CFRunLoopActivity activity,
void *info)
{
- if (getting_events) /* Activity we triggered */
+ switch (activity)
+ {
+ case kCFRunLoopEntry:
+ current_loop_level++;
+ break;
+ case kCFRunLoopExit:
+ g_return_if_fail (current_loop_level > 0);
+ current_loop_level--;
+ break;
+ default:
+ break;
+ }
+
+ if (getting_events > 0) /* Activity we triggered */
return;
-
+
switch (activity)
{
case kCFRunLoopEntry: