[PATCH] Iterate the main loop in a separate thread while in a modal operation gdkeventloop-quartz.c: * Define the interaction between the "select thread" and the main thread as an explict state machine. * Use a CFRunLoopObserver to detect when a modal operation is running; while the modal operation is running, use the select thread to do the prepare/poll/check stages of the main loop, then once we have something wake the main thread back up to dispatch. * Use GLib thread primitives instead of pthread since we are counting on GLib threading. GdkQuartzWindow.c: Call g_main_context_wakeup() after adding an event to the event queue on resize.
- From: Owen W. Taylor <otaylor fishsoup net>
- Subject: [PATCH] Iterate the main loop in a separate thread while in a modal operation gdkeventloop-quartz.c: * Define the interaction between the "select thread" and the main thread as an explict state machine. * Use a CFRunLoopObserver to detect when a modal operation is running; while the modal operation is running, use the select thread to do the prepare/poll/check stages of the main loop, then once we have something wake the main thread back up to dispatch. * Use GLib thread primitives instead of pthread since we are counting on GLib threading. GdkQuartzWindow.c: Call g_main_context_wakeup() after adding an event to the event queue on resize.
- Date: Wed, 3 Sep 2008 00:14:04 -0400
Background: http://mail.gnome.org/archives/gtk-devel-list/2008-September/msg00008.html
---
gdk/quartz/GdkQuartzWindow.c | 3 +
gdk/quartz/gdkeventloop-quartz.c | 608 ++++++++++++++++++++++++++++++--------
2 files changed, 495 insertions(+), 116 deletions(-)
diff --git a/gdk/quartz/GdkQuartzWindow.c b/gdk/quartz/GdkQuartzWindow.c
index 3f16815..49c9e39 100644
--- a/gdk/quartz/GdkQuartzWindow.c
+++ b/gdk/quartz/GdkQuartzWindow.c
@@ -180,6 +180,7 @@
[[self contentView] setFrame:NSMakeRect (0, 0, impl->width, impl->height)];
+ GDK_THREADS_ENTER();
/* Synthesize a configure event */
event = gdk_event_new (GDK_CONFIGURE);
event->configure.window = g_object_ref (window);
@@ -189,6 +190,8 @@
event->configure.height = impl->height;
_gdk_event_queue_append (gdk_display_get_default (), event);
+ g_main_context_wakeup (NULL);
+ GDK_THREADS_LEAVE();
}
-(id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag
diff --git a/gdk/quartz/gdkeventloop-quartz.c b/gdk/quartz/gdkeventloop-quartz.c
index 50b2c7e..96e8535 100644
--- a/gdk/quartz/gdkeventloop-quartz.c
+++ b/gdk/quartz/gdkeventloop-quartz.c
@@ -8,26 +8,81 @@
#include "gdkprivate-quartz.h"
+typedef enum {
+ BEFORE_START,
+
+ /**** Select thread is waiting for main thread ****/
+ WAITING,
+ WAITING_FOR_DISPATCH,
+
+ /**** Main thread is waiting for select thread ****/
+ POLLING_DESCRIPTORS,
+ POLLING_INTERRUPTED,
+ ITERATING_MAIN_LOOP,
+ ITERATING_INTERRUPTED
+} SelectThreadState;
+
+static const char *const state_names[] = {
+ "BEFORE_START",
+ "WAITING",
+ "WAITING_FOR_DISPATCH",
+ "POLLING_DESCRIPTORS",
+ "POLLING_INTERRUPTED",
+ "ITERATING_MAIN_LOOP",
+ "ITERATING_INTERRUPTED"
+};
+
+static SelectThreadState select_thread_state = BEFORE_START;
+static GMutex *select_thread_mutex;
+static GCond *select_thread_cond;
+static int modal_operation_count;
+
+#define LOCK() g_mutex_lock (select_thread_mutex)
+#define UNLOCK() g_mutex_unlock (select_thread_mutex)
+#define SIGNAL() g_cond_signal (select_thread_cond)
+#define WAIT() g_cond_wait (select_thread_cond, select_thread_mutex)
+
static GPollFD event_poll_fd;
static GQueue *current_events;
static GPollFunc old_poll_func;
-static gboolean select_fd_waiting = FALSE, ready_for_poll = FALSE;
-static pthread_t select_thread = 0;
static int wakeup_pipe[2];
-static pthread_mutex_t pollfd_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER;
+
+/* This is the poll array that is used to communicate between the main thread
+ * and the select thread when the select thread is polling file descriptors.
+ *
+ * If select_thread_state == POLLING_DESCRIPTORS or POLLING_INTERRUPTED, then
+ * the select thread is allowed to read and modify these. Otherwise, only
+ * the main loop is allowed to access them.
+ */
static GPollFD *pollfds;
static guint n_pollfds;
+
+/* This is the poll array used internal to the select thread when the
+ * select thread is iterating the main loop.
+ */
+#define CACHED_POLLFDS_INITIAL_SIZE 16
+static GPollFD *cached_pollfds;
+static guint n_cached_pollfds;
+
static CFRunLoopSourceRef select_main_thread_source;
+static GThread *main_thread;
static CFRunLoopRef main_thread_run_loop;
static NSAutoreleasePool *autorelease_pool;
+gboolean getting_events;
+
gboolean
_gdk_quartz_event_loop_check_pending (void)
{
- return current_events && current_events->head;
+ gboolean retval;
+
+ LOCK ();
+ retval = current_events && current_events->head;
+ UNLOCK ();
+
+ return retval;
}
NSEvent*
@@ -35,9 +90,13 @@ _gdk_quartz_event_loop_get_pending (void)
{
NSEvent *event = NULL;
+ LOCK ();
+
if (current_events)
event = g_queue_pop_tail (current_events);
+ UNLOCK ();
+
return event;
}
@@ -55,17 +114,24 @@ gdk_event_prepare (GSource *source,
gboolean retval;
GDK_THREADS_ENTER ();
-
- *timeout = -1;
- event = [NSApp nextEventMatchingMask: NSAnyEventMask
- untilDate: [NSDate distantPast]
- inMode: NSDefaultRunLoopMode
- dequeue: NO];
+ *timeout = -1;
- retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
- (current_events && current_events->head) ||
- event != NULL);
+ if (g_thread_self () == main_thread)
+ {
+ getting_events = TRUE;
+ event = [NSApp nextEventMatchingMask: NSAnyEventMask
+ untilDate: [NSDate distantPast]
+ inMode: NSDefaultRunLoopMode
+ dequeue: NO];
+ getting_events = FALSE;
+ }
+ else
+ event = NULL;
+
+ retval = (event != NULL ||
+ _gdk_quartz_event_loop_check_pending () ||
+ _gdk_event_queue_non_empty (_gdk_display));
GDK_THREADS_LEAVE ();
@@ -83,7 +149,7 @@ gdk_event_check (GSource *source)
[autorelease_pool release];
autorelease_pool = [[NSAutoreleasePool alloc] init];
- if (_gdk_event_queue_find_first (_gdk_display) != NULL ||
+ if (_gdk_event_queue_non_empty (_gdk_display) ||
_gdk_quartz_event_loop_check_pending ())
retval = TRUE;
else
@@ -127,97 +193,286 @@ static GSourceFuncs event_funcs = {
NULL
};
+static void
+select_thread_set_state(SelectThreadState new_state)
+{
+ gboolean old_waits_for_thread = !(select_thread_state == WAITING || select_thread_state == WAITING_FOR_DISPATCH);
+ gboolean new_waits_for_thread = !(new_state == WAITING || new_state == WAITING_FOR_DISPATCH);
+
+ GDK_NOTE(MISC, g_print("ST: %s => %s\n", state_names[select_thread_state], state_names[new_state]));
+
+ g_assert (!(!old_waits_for_thread && new_waits_for_thread));
+
+ select_thread_state = new_state;
+ if (old_waits_for_thread && !new_waits_for_thread)
+ SIGNAL ();
+}
+
+static void
+select_thread_wait(void)
+{
+ g_assert (select_thread_state == WAITING || select_thread_state == WAITING_FOR_DISPATCH);
+ WAIT ();
+}
+
+static void
+main_thread_set_state(SelectThreadState new_state)
+{
+ gboolean old_waits_for_thread = !(select_thread_state == WAITING || select_thread_state == WAITING_FOR_DISPATCH);
+ gboolean new_waits_for_thread = !(new_state == WAITING || new_state == WAITING_FOR_DISPATCH);
+
+ GDK_NOTE(MISC, g_print("MT: %s => %s\n", state_names[select_thread_state], state_names[new_state]));
+
+ g_assert (!(old_waits_for_thread && !new_waits_for_thread));
+
+ select_thread_state = new_state;
+ if (!old_waits_for_thread && new_waits_for_thread)
+ SIGNAL ();
+}
+
+static void
+main_thread_wait(void)
+{
+ g_assert (!(select_thread_state == WAITING || select_thread_state == WAITING_FOR_DISPATCH));
+ g_assert (!(select_thread_state == POLLING_DESCRIPTORS || select_thread_state == ITERATING_MAIN_LOOP));
+ WAIT ();
+}
+
static void
got_fd_activity (void *info)
{
NSEvent *event;
- /* Post a message so we'll break out of the message loop */
- event = [NSEvent otherEventWithType: NSApplicationDefined
- location: NSZeroPoint
- modifierFlags: 0
- timestamp: 0
- windowNumber: 0
- context: nil
- subtype: GDK_QUARTZ_EVENT_SUBTYPE_EVENTLOOP
- data1: 0
- data2: 0];
-
- [NSApp postEvent:event atStart:YES];
+ LOCK ();
+
+ if (select_thread_state == WAITING_FOR_DISPATCH)
+ {
+ UNLOCK ();
+ GDK_NOTE(MISC, g_print("Dispatching from main thread\n"));
+ g_main_context_dispatch (g_main_context_default ());
+ LOCK ();
+ main_thread_set_state (ITERATING_MAIN_LOOP);
+ }
+ else if (select_thread_state != ITERATING_MAIN_LOOP)
+ {
+ /* Post a message so we'll break out of the message loop */
+ event = [NSEvent otherEventWithType: NSApplicationDefined
+ location: NSZeroPoint
+ modifierFlags: 0
+ timestamp: 0
+ windowNumber: 0
+ context: nil
+ subtype: GDK_QUARTZ_EVENT_SUBTYPE_EVENTLOOP
+ data1: 0
+ data2: 0];
+
+ [NSApp postEvent:event atStart:YES];
+ }
+
+ UNLOCK ();
}
-static void *
-select_thread_func (void *arg)
+static void
+signal_main_thread(void)
{
- int n_active_fds;
+ GDK_NOTE(MISC, g_print("Signalling\n"));
+ CFRunLoopSourceSignal (select_main_thread_source);
+ if (CFRunLoopIsWaiting (main_thread_run_loop))
+ CFRunLoopWakeUp (main_thread_run_loop);
+}
- pthread_mutex_lock (&pollfd_mutex);
+static void
+select_thread_poll_descriptors()
+{
+ char c;
+
+ UNLOCK ();
+ old_poll_func (pollfds, n_pollfds, -1);
+ read (wakeup_pipe[0], &c, 1);
+ LOCK ();
+}
- while (1)
+static void
+select_thread_iterate_main_loop()
+{
+ gint max_priority;
+ gint timeout;
+ gint nfds, allocated_nfds;
+ GPollFD *fds = NULL;
+ GMainContext *context = g_main_context_default ();
+ gboolean interrupted;
+
+ if (!cached_pollfds)
{
- char c;
- int n;
-
- ready_for_poll = TRUE;
- pthread_cond_signal (&ready_cond);
- pthread_cond_wait (&ready_cond, &pollfd_mutex);
- ready_for_poll = FALSE;
-
- select_fd_waiting = TRUE;
- pthread_cond_signal (&ready_cond);
- pthread_mutex_unlock (&pollfd_mutex);
- n_active_fds = old_poll_func (pollfds, n_pollfds, -1);
- pthread_mutex_lock (&pollfd_mutex);
- select_fd_waiting = FALSE;
- n = read (wakeup_pipe[0], &c, 1);
- if (n == 1)
- {
- g_assert (c == 'A');
+ n_cached_pollfds = CACHED_POLLFDS_INITIAL_SIZE;
+ cached_pollfds = g_new (GPollFD, n_cached_pollfds);
+ }
- n_active_fds --;
- }
- pthread_mutex_unlock (&pollfd_mutex);
+ allocated_nfds = n_cached_pollfds;
+ fds = cached_pollfds;
+
+ UNLOCK ();
- if (n_active_fds)
+ g_main_context_prepare (context, &max_priority);
+
+ while ((nfds = g_main_context_query (context, max_priority, &timeout, fds,
+ allocated_nfds)) > allocated_nfds)
+ {
+ g_free (fds);
+ n_cached_pollfds = allocated_nfds = nfds;
+ cached_pollfds = fds = g_new (GPollFD, nfds);
+ }
+
+ /* Checking that we weren't interrupted is just an optimzation .. the
+ * other thread has to call g_main_context_wakeup() unconditionally
+ * since there otherwise is an unavoidable race: we can't atomically
+ * drop the lock and start polling.
+ */
+ LOCK ();
+ interrupted = (select_thread_state == ITERATING_INTERRUPTED);
+ UNLOCK ();
+
+ if (!interrupted && (nfds || timeout != 0))
+ old_poll_func (fds, nfds, interrupted ? timeout : 0);
+
+ g_main_context_check (context, max_priority, fds, nfds);
+
+ LOCK ();
+}
+
+static void *
+select_thread_func (void *arg)
+{
+ LOCK ();
+
+ while (TRUE)
+ {
+ GDK_NOTE(MISC, g_print("ST: %s\n", state_names[select_thread_state]));
+
+ switch (select_thread_state)
{
- /* We have active fds, signal the main thread */
- CFRunLoopSourceSignal (select_main_thread_source);
- if (CFRunLoopIsWaiting (main_thread_run_loop))
- CFRunLoopWakeUp (main_thread_run_loop);
+ case BEFORE_START:
+ g_assert_not_reached();
+
+ case WAITING:
+ case WAITING_FOR_DISPATCH:
+ select_thread_wait ();
+ break;
+
+ case POLLING_DESCRIPTORS:
+ select_thread_poll_descriptors();
+ if (select_thread_state != POLLING_INTERRUPTED)
+ signal_main_thread();
+ select_thread_set_state (WAITING);
+ break;
+ case POLLING_INTERRUPTED:
+ /* Interrupted immediately */
+ select_thread_set_state (WAITING);
+ break;
+ case ITERATING_MAIN_LOOP:
+ select_thread_iterate_main_loop();
+ if (select_thread_state != ITERATING_INTERRUPTED)
+ signal_main_thread();
+ select_thread_set_state (WAITING_FOR_DISPATCH);
+ break;
+ case ITERATING_INTERRUPTED:
+ /* Interrupted immediately */
+ select_thread_set_state (WAITING);
+ break;
}
+ }
+}
+
+static void
+select_thread_start(void)
+{
+ g_return_if_fail (select_thread_state == BEFORE_START);
+
+ pipe (wakeup_pipe);
+ fcntl(wakeup_pipe[0], F_SETFL, O_NONBLOCK);
+
+ CFRunLoopSourceContext source_context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, got_fd_activity };
+ select_main_thread_source = CFRunLoopSourceCreate (NULL, 0, &source_context);
+
+ CFRunLoopAddSource (main_thread_run_loop, select_main_thread_source, kCFRunLoopCommonModes);
+
+ select_thread_state = WAITING;
+ select_thread_cond = g_cond_new ();
+
+ while (TRUE)
+ {
+ if (g_thread_create (select_thread_func, NULL, FALSE, NULL))
+ break;
- pthread_mutex_lock (&pollfd_mutex);
+ g_warning ("Failed to create select thread, sleeping and trying again");
+ sleep (1);
}
}
+static void
+interrupt_polling (void)
+{
+ g_return_if_fail (select_thread_state == POLLING_DESCRIPTORS);
+
+ if (wakeup_pipe[1])
+ {
+ char c = 'A';
+
+ write (wakeup_pipe[1], &c, 1);
+ }
+
+ main_thread_set_state (POLLING_INTERRUPTED);
+}
+
+static void
+interrupt_iteration (void)
+{
+ g_return_if_fail (select_thread_state == ITERATING_MAIN_LOOP);
+
+ g_main_context_wakeup (NULL);
+ main_thread_set_state (ITERATING_INTERRUPTED);
+}
+
static gint
poll_func (GPollFD *ufds, guint nfds, gint timeout_)
{
NSEvent *event;
NSDate *limit_date;
- gboolean poll_event_fd = FALSE;
- int n_active = 0;
+ int poll_event_fd = FALSE;
+ gboolean return_to_iterating = FALSE;
int i;
if (nfds > 1 || ufds[0].fd != -1)
{
- if (!select_thread) {
- /* Create source used for signalling the main thread */
- main_thread_run_loop = CFRunLoopGetCurrent ();
- CFRunLoopSourceContext source_context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, got_fd_activity };
- select_main_thread_source = CFRunLoopSourceCreate (NULL, 0, &source_context);
- CFRunLoopAddSource (main_thread_run_loop, select_main_thread_source, kCFRunLoopDefaultMode);
+ if (select_thread_state == BEFORE_START)
+ select_thread_start();
- pipe (wakeup_pipe);
- fcntl(wakeup_pipe[0], F_SETFL, O_NONBLOCK);
+ LOCK ();
- pthread_mutex_lock (&pollfd_mutex);
- pthread_create (&select_thread, NULL, select_thread_func, NULL);
- } else
- pthread_mutex_lock (&pollfd_mutex);
+ if (select_thread_state == ITERATING_MAIN_LOOP)
+ {
+ /* While we were in a blocking modal operation, someone recursed
+ * the main loop. gmain.c probably already spewed a warning
+ * because of unexpected reentrancy. We can't do much here, but
+ * wake the select thread up so we don't hang.
+ */
+ g_warning("Poll function called while were were iterating from a thread\n");
+ interrupt_iteration();
+ }
- while (!ready_for_poll)
- pthread_cond_wait (&ready_cond, &pollfd_mutex);
+ GDK_NOTE(MISC, g_print("Asking thread to poll on our behalf\n"));
+
+ while (select_thread_state != WAITING && select_thread_state != WAITING_FOR_DISPATCH)
+ main_thread_wait ();
+
+ if (select_thread_state == WAITING_FOR_DISPATCH)
+ {
+ /* If we were in a blocking modal operation, then we need to return
+ * continue with that when we are done
+ */
+ GDK_NOTE(MISC, g_print("Will return to thread iteration when done\n"));
+ return_to_iterating = TRUE;
+ }
/* We cheat and use the fake fd (if it's polled) for our pipe */
@@ -245,10 +500,8 @@ poll_func (GPollFD *ufds, guint nfds, gint timeout_)
pollfds[i].fd = wakeup_pipe[0];
pollfds[i].events = G_IO_IN;
- /* Start our thread */
- pthread_cond_signal (&ready_cond);
- pthread_cond_wait (&ready_cond, &pollfd_mutex);
- pthread_mutex_unlock (&pollfd_mutex);
+ main_thread_set_state (POLLING_DESCRIPTORS);
+ UNLOCK ();
}
if (timeout_ == -1)
@@ -258,18 +511,19 @@ poll_func (GPollFD *ufds, guint nfds, gint timeout_)
else
limit_date = [NSDate dateWithTimeIntervalSinceNow:timeout_/1000.0];
+ getting_events = TRUE;
event = [NSApp nextEventMatchingMask: NSAnyEventMask
untilDate: limit_date
inMode: NSDefaultRunLoopMode
dequeue: YES];
-
- if (event)
+ getting_events = FALSE;
+
+ if (nfds > 0)
{
- if ([event type] == NSApplicationDefined &&
- [event subtype] == GDK_QUARTZ_EVENT_SUBTYPE_EVENTLOOP)
- {
- pthread_mutex_lock (&pollfd_mutex);
+ LOCK ();
+ if (select_thread_state == WAITING) /* The poll completed */
+ {
for (i = 0; i < nfds; i++)
{
if (ufds[i].fd == -1)
@@ -281,58 +535,166 @@ poll_func (GPollFD *ufds, guint nfds, gint timeout_)
if (pollfds[i].revents)
{
ufds[i].revents = pollfds[i].revents;
- n_active ++;
}
}
+ }
+ else
+ {
+ interrupt_polling ();
+ }
- pthread_mutex_unlock (&pollfd_mutex);
- /* Try to get a Cocoa event too, if requested */
- if (poll_event_fd)
- event = [NSApp nextEventMatchingMask: NSAnyEventMask
- untilDate: [NSDate distantPast]
- inMode: NSDefaultRunLoopMode
- dequeue: YES];
- else
- event = NULL;
- }
- }
+ if (return_to_iterating)
+ {
+ while (select_thread_state != WAITING)
+ main_thread_wait ();
- /* There were no active fds, break out of the other thread's poll() */
- pthread_mutex_lock (&pollfd_mutex);
- if (select_fd_waiting && wakeup_pipe[1])
+ main_thread_set_state (ITERATING_MAIN_LOOP);
+ }
+
+ UNLOCK ();
+ }
+
+ if (event &&
+ [event type] == NSApplicationDefined &&
+ [event subtype] == GDK_QUARTZ_EVENT_SUBTYPE_EVENTLOOP)
{
- char c = 'A';
-
- write (wakeup_pipe[1], &c, 1);
+ /* Just used to wake us up; if an event and a FD arrived at the same
+ * time, could have come from a previous iteration, in which case
+ * we'll wake up spuriously, which is a little inefficient, but harmless.
+ */
+ event = NULL;
}
- pthread_mutex_unlock (&pollfd_mutex);
if (event)
{
- for (i = 0; i < nfds; i++)
- {
- if (ufds[i].fd == -1)
- {
- ufds[i].revents = G_IO_IN;
- break;
- }
- }
-
+ LOCK ();
+
if (!current_events)
current_events = g_queue_new ();
g_queue_push_head (current_events, [event retain]);
- n_active ++;
+ UNLOCK ();
+ }
+
+ /* We count on the fact that GMain only cares about >= 0 (no error) vs
+ * -1 (error), and doesn't do anything about the -1 case except warn.
+ */
+ return 0;
+}
+
+static void
+_gdk_quartz_begin_iterating_in_thread (void)
+{
+ modal_operation_count++;
+ if (modal_operation_count == 1)
+ {
+ GDK_NOTE (MISC, g_print("Starting processing GLib main loop from a thread\n"));
+
+ if (select_thread_state == BEFORE_START)
+ select_thread_start();
+
+ if (!g_main_context_acquire (NULL))
+ {
+ /* Ugh, someone is iterating the main thread from another thread. (This is
+ * pathological unless gdk_threads_init() was called.) We'll just wait until
+ * they are done and hope that it doesn't take long.
+ */
+ LOCK ();
+ while (!g_main_context_wait (NULL, select_thread_cond, select_thread_mutex))
+ ;
+ }
+ else
+ LOCK ();
+
+ if (select_thread_state == POLLING_DESCRIPTORS)
+ interrupt_polling();
+
+ while (select_thread_state != WAITING && select_thread_state != WAITING_FOR_DISPATCH)
+ main_thread_wait ();
+
+ main_thread_set_state (ITERATING_MAIN_LOOP);
+
+ UNLOCK ();
+ }
+}
+
+static void
+_gdk_quartz_end_iterating_in_thread (void)
+{
+ g_return_if_fail(modal_operation_count > 0);
+
+ modal_operation_count--;
+ if (modal_operation_count == 0)
+ {
+ GDK_NOTE (MISC, g_print("Stopping processing GLib main loop from a thread\n"));
+
+ LOCK();
+
+ if (select_thread_state == ITERATING_MAIN_LOOP)
+ {
+ GDK_NOTE(MISC, g_print("Context is iterating, wake it up!\n"));
+ interrupt_iteration();
+ }
+
+ while (select_thread_state != WAITING && select_thread_state != WAITING_FOR_DISPATCH)
+ main_thread_wait ();
+
+ if (select_thread_state == WAITING_FOR_DISPATCH)
+ {
+ UNLOCK ();
+ g_main_context_dispatch (g_main_context_default ());
+ LOCK ();
+ main_thread_set_state (WAITING);
+ }
+
+ UNLOCK();
+
+ g_main_context_release (NULL);
}
+}
- return n_active;
+void
+run_loop_observer_callback (CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity,
+ void *info)
+{
+ if (getting_events) /* Activity we triggered */
+ return;
+
+ switch (activity)
+ {
+ case kCFRunLoopEntry:
+ _gdk_quartz_begin_iterating_in_thread();
+ GDK_NOTE(MISC, g_print ("Loop Entry: %d\n", modal_operation_count));
+ break;
+ case kCFRunLoopBeforeTimers:
+ GDK_NOTE(MISC, g_print ("Before Timers\n"));
+ break;
+ case kCFRunLoopBeforeSources:
+ GDK_NOTE(MISC, g_print ("Before Sources\n"));
+ break;
+ case kCFRunLoopBeforeWaiting:
+ GDK_NOTE(MISC, g_print ("Before Waiting\n"));
+ break;
+ case kCFRunLoopAfterWaiting:
+ GDK_NOTE(MISC, g_print ("After Waiting\n"));
+ break;
+ case kCFRunLoopExit:
+ GDK_NOTE(MISC, g_print ("Loop Exit: %d\n", modal_operation_count));
+ _gdk_quartz_end_iterating_in_thread();
+ break;
+ default:
+ GDK_NOTE(MISC, g_print ("Unexpected activity: %d\n", activity));
+ break;
+ }
}
void
_gdk_quartz_event_loop_init (void)
{
GSource *source;
+ CFRunLoopObserverRef observer;
event_poll_fd.events = G_IO_IN;
event_poll_fd.fd = -1;
@@ -343,9 +705,23 @@ _gdk_quartz_event_loop_init (void)
g_source_set_can_recurse (source, TRUE);
g_source_attach (source, NULL);
+ main_thread = g_thread_self ();
+ main_thread_run_loop = CFRunLoopGetCurrent ();
+
+ observer = CFRunLoopObserverCreate(NULL, /* default allocator */
+ kCFRunLoopEntry | kCFRunLoopExit,
+ // kCFRunLoopAllActivities,
+ true, /* repeats: not one-shot */
+ 0, /* order (priority) */
+ run_loop_observer_callback,
+ NULL);
+
+ CFRunLoopAddObserver(main_thread_run_loop, observer, kCFRunLoopCommonModes);
+
old_poll_func = g_main_context_get_poll_func (NULL);
g_main_context_set_poll_func (NULL, poll_func);
autorelease_pool = [[NSAutoreleasePool alloc] init];
+
+ select_thread_mutex = g_mutex_new ();
}
-
--
1.6.0
--=-XJ7NXCzldB2WxV4OEwkx--
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]