[glib/wip/gmaincontext: 10/12] wip...



commit a82be95ed01853ae8e910d67075628cdd382c8c2
Author: Ryan Lortie <desrt desrt ca>
Date:   Wed Feb 12 15:07:28 2014 -0500

    wip...

 glib/gmain.c          | 1211 ++++++++++++++++++++++++++++++++-----------------
 glib/gmain.h          |   10 +
 glib/tests/mainloop.c |   13 +-
 3 files changed, 804 insertions(+), 430 deletions(-)
---
diff --git a/glib/gmain.c b/glib/gmain.c
index b9e80c9..bab6dba 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -32,6 +32,9 @@
 #include "config.h"
 #include "glibconfig.h"
 
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+
 /* Uncomment the next line (and the corresponding line in gpoll.c) to
  * enable debugging printouts if the environment variable
  * G_MAIN_POLL_DEBUG is set to some value.
@@ -195,14 +198,6 @@ typedef enum
   G_SOURCE_BLOCKED = 1 << (G_HOOK_FLAG_USER_SHIFT + 2)
 } GSourceFlags;
 
-typedef struct _GSourceList GSourceList;
-
-struct _GSourceList
-{
-  GSource *head, *tail;
-  gint priority;
-};
-
 typedef struct _GMainWaiter GMainWaiter;
 
 struct _GMainWaiter
@@ -223,6 +218,102 @@ struct _GMainDispatch
 gboolean _g_main_poll_debug = FALSE;
 #endif
 
+/* The GSourceFd structure tracks the relationship of sources, contexts
+ * and file descriptors.
+ *
+ * Each source has a list of GSourceFd, one for each fd.  Each main
+ * context also keeps a per-fd list of GSourceFd, possibly with different
+ * sources and event masks in each.
+ *
+ * Because the same structure appears in two different lists, we keep
+ * two 'next' pointers.
+ *
+ * One is created for each call to g_source_add_unix_fd().
+ *
+ * The number of file descriptors watched per source and the number of
+ * sources watching a particular file descriptor are expected to be very
+ * small (typically 1:1) which is why we opt to use linked lists here.
+ *
+ * We also create a GSourceFd object with a %NULL source for things that
+ * are not associated with a particular source.  This includes #GPollFDs
+ * (even if they originally come from a source) and internally-created
+ * things such as GWakeup.
+ */
+typedef struct _GSourceFd GSourceFd;
+struct _GSourceFd
+{
+  GSource   *source;
+#ifdef G_OS_WIN32
+  HANDLE    *handle;
+#else
+  gint       fd;
+#endif
+  gshort     events;
+  gshort     revents;
+
+  GSourceFd *context_next;
+  GSourceFd *source_next;
+};
+
+/* GMainContextFd is the object that represents the main context's view
+ * of what it needs to do with respect to a single fd.
+ *
+ * It is used as the user_data pointer to both kevent and epoll.  When
+ * running with poll(), it is looked up via hash table.
+ *
+ * It contains the list of GSourceFd structs that are associated with
+ * the fd.
+ *
+ * The number of file descriptors in the main context is expected to be
+ * potentially quite large, which is why we use the hash table for
+ * lookups, even when using poll().  The number of sources watching a
+ * particular file descriptor is expected to be quite small (usually
+ * exactly 1), which is why we use a linked list.
+ *
+ * In order not to thrash the kernel, we never update the poll list in
+ * response to changing priorities -- we must therefore be able to
+ * quickly filter the poll results to ignore fds with low priority.
+ * Having the highest_priority cached here prevents us from having to
+ * iterate the sources list in order to calculate it each time.
+ */
+typedef struct
+{
+  GSourceFd *sources;
+
+  gint       highest_priority;      /* cache */
+} GMainContextFd;
+
+/* For GPollFDs that got directly added via the deprecated
+ * g_main_context_add_poll() or g_source_add_poll(), we have to deal
+ * with the possibility that they may be changed at any time.  We use a
+ * GPollFdHolder for this, and we always rescan it each time there may
+ * have been a change in order to update the GSourceFd.
+ *
+ * This constant checking and rechecking is relatively expensive, but
+ * those calls are deprecated...
+ */
+typedef struct
+{
+  GSourceFd  source_fd;
+  GPollFD   *poll_fd;
+} GPollFdHolder;
+
+#if defined(G_OS_WIN32)
+typedef OVERLAPPED_THINGY GPollEvent;
+#define g_poll_event_get_user_data(gps)  ((gps).ident)
+#define g_poll_event_get_revents(gpe)    1
+#elif defined(HAVE_EPOLL)
+typedef struct epoll_event GPollEvent;
+#define g_poll_event_get_user_data(gpe)  ((gpe).data.ptr)
+#define g_poll_event_get_revents(gpe)    ((gpe).events)
+#elif defined(HAVE_KQUEUE)
+typedef struct kevent GPollEvent;
+#define g_poll_event_get_user_data(gpe)  ((gpe).udata)
+#define g_poll_event_get_revents(gpe)    ((gpe).type READ WRITE)
+#else
+#error uh oh...
+#endif
+
 struct _GMainContext
 {
   /* The following lock is used for both the list of sources
@@ -237,21 +328,29 @@ struct _GMainContext
   gint ref_count;
 
   GHashTable *sources;              /* guint -> GSource */
+  GHashTable *fds;                  /* gint -> GMainContextFd */
+  GSList     *fds_to_free;          /* GMainContextFd */
 
-  GPtrArray *pending_dispatches;
-  gint timeout;                        /* Timeout for current iteration */
+  GSequence  *impure_sources;       /* sources with prepare/check functions, sorted by priority */
+  gint64      impure_ready_time;    /* time for wakeup based on prepare() funcs */
+  GSList     *poll_holders;         /* GPollFdHolder, for (deprecated) _add_poll() */
 
-  guint next_id;
-  GList *source_lists;
-  gint in_check_or_prepare;
+  GSequence  *ready_time_sources;   /* sources with ready_time set, sorted by ready_time */
 
-  GPollRec *poll_records, *poll_records_tail;
-  guint n_poll_records;
-  GPollFD *cached_poll_array;
-  guint cached_poll_array_size;
+  gint        kqueue_fd;
+  gint        timer_fd;
+  GPollFD     kqueue_rec;
+  gint64      cached_ready_time;
+  gboolean    is_in_poll;
+  GPollEvent *poll_events;
+  gint        n_poll_events;
 
-  GWakeup *wakeup;
+  GPtrArray  *pending_dispatches;
+
+  guint      next_id;
+  gint       in_check_or_prepare;
 
+  GWakeup *wakeup;
   GPollFD wake_up_rec;
 
 /* Flag indicating whether the set of fd's changed during a poll */
@@ -322,17 +421,9 @@ struct _GSourcePrivate
   /* This is currently only used on UNIX, but we always declare it (and
    * let it remain empty on Windows) to avoid #ifdef all over the place.
    */
-  GSList *fds;
+  GSourceFd *fds;
 };
 
-typedef struct _GSourceIter
-{
-  GMainContext *context;
-  gboolean may_modify;
-  GList *current_list;
-  GSource *source;
-} GSourceIter;
-
 #define LOCK_CONTEXT(context) g_mutex_lock (&context->mutex)
 #define UNLOCK_CONTEXT(context) g_mutex_unlock (&context->mutex)
 #define G_THREAD_SELF g_thread_self ()
@@ -348,7 +439,6 @@ typedef struct _GSourceIter
       g_source_unref_internal ((source), (context), TRUE);  \
    } G_STMT_END
 
-
 /* Forward declarations */
 
 static void g_source_unref_internal             (GSource      *source,
@@ -374,13 +464,6 @@ static void g_main_context_add_poll_unlocked    (GMainContext *context,
 static void g_main_context_remove_poll_unlocked (GMainContext *context,
                                                 GPollFD      *fd);
 
-static void     g_source_iter_init  (GSourceIter   *iter,
-                                    GMainContext  *context,
-                                    gboolean       may_modify);
-static gboolean g_source_iter_next  (GSourceIter   *iter,
-                                    GSource      **source);
-static void     g_source_iter_clear (GSourceIter   *iter);
-
 static gboolean g_timeout_dispatch (GSource     *source,
                                    GSourceFunc  callback,
                                    gpointer     user_data);
@@ -415,8 +498,517 @@ static GMainContext *glib_worker_context;
 G_LOCK_DEFINE_STATIC (main_loop);
 static GMainContext *default_main_context;
 
-#ifndef G_OS_WIN32
 
+static void
+g_source_fd_free (gpointer data)
+{
+  GSourceFd *source_fd = data;
+
+  g_slice_free (GSourceFd, source_fd);
+}
+
+static void
+g_main_context_fd_free (gpointer data)
+{
+  GMainContextFd *context_fd = data;
+
+  g_slice_free (GMainContextFd, context_fd);
+}
+
+static void
+g_main_context_poll_core_add (GMainContext *context,
+                              gint          fd,
+                              guint         events,
+                              gpointer      user_data)
+{
+  if (context->kqueue_fd == -1)
+    {
+      context->kqueue_fd = epoll_create1 (EPOLL_CLOEXEC);
+      g_assert (context->kqueue_fd > 0);
+      context->kqueue_rec.fd = context->kqueue_fd;
+      context->kqueue_rec.events = G_IO_IN;
+      g_main_context_add_poll_unlocked (context, 0, &context->kqueue_rec);
+    }
+
+#ifdef __linux
+  {
+    struct epoll_event event;
+
+    event.data.ptr = user_data;
+    event.events = events;
+
+    epoll_ctl (context->kqueue_fd, EPOLL_CTL_ADD, fd, &event);
+  }
+#else
+#error uh oh...
+#endif
+}
+
+static void
+g_main_context_poll_core_update (GMainContext *context,
+                                 gint          fd,
+                                 guint         events,
+                                 gpointer      user_data)
+{
+#ifdef __linux
+  {
+    struct epoll_event event;
+
+    event.data.ptr = user_data;
+    event.events = events;
+
+    epoll_ctl (context->kqueue_fd, EPOLL_CTL_MOD, fd, &event);
+  }
+#else
+#error uh oh...
+#endif
+}
+
+static void
+g_main_context_poll_core_remove (GMainContext *context,
+                                 gint          fd)
+{
+#ifdef __linux
+  {
+    /* Old Linux kernels have a bug whereby the pointer must be
+     * non-%NULL, even on delete events (where it is ignored).
+     */
+    epoll_ctl (context->kqueue_fd, EPOLL_CTL_DEL, fd, (gpointer) 0x1);
+  }
+#else
+#error uh oh...
+#endif
+}
+
+static void
+g_main_context_poll_core_set_ready_time (GMainContext *context,
+                                         gint64        ready_time)
+{
+  if (ready_time == context->cached_ready_time)
+    return;
+
+  context->cached_ready_time = ready_time;
+
+#ifdef __linux
+  {
+    struct itimerspec its;
+
+    if (context->timer_fd == -1)
+      {
+        context->timer_fd = timerfd_create (CLOCK_MONOTONIC, TFD_CLOEXEC);
+        g_main_context_poll_core_add (context, context->timer_fd, G_IO_IN, NULL);
+      }
+
+    if (ready_time >= 0)
+      {
+        /* Arm */
+        its.it_value.tv_sec = ready_time / G_TIME_SPAN_SECOND;
+        its.it_value.tv_nsec = (ready_time % G_TIME_SPAN_SECOND) * 1000;
+
+        /* 0.0 means disarm, so bump it in that case */
+        if (!its.it_value.tv_sec && !its.it_value.tv_nsec)
+          its.it_value.tv_nsec = 1;
+      }
+    else
+      {
+        /* Disarm */
+        its.it_value.tv_sec = its.it_value.tv_nsec = 0;
+      }
+
+    its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
+
+    timerfd_settime (context->timer_fd, TFD_TIMER_ABSTIME, &its, NULL);
+  }
+#else
+#error uh oh...
+#endif
+}
+
+static gint
+g_main_context_poll_core_wait (GMainContext *context,
+                               GPollEvent   *events,
+                               gint          max_events,
+                               gboolean      should_block)
+{
+#ifdef __linux
+  return epoll_wait (context->kqueue_fd, events, max_events, should_block ? -1 : 0);
+#else
+#error uh oh..
+#endif
+}
+
+static void
+g_main_context_queue_source_for_dispatch (GMainContext *context,
+                                          GSource      *source)
+{
+  
+}
+
+static void
+g_main_context_collect_events (GMainContext *context,
+                               gboolean      should_block)
+{
+  gint dispatch_priority = G_MAXINT;
+  gint result;
+  gint i;
+
+  UNLOCK_CONTEXT (context);
+
+  /* Even though we don't have the lock here, we have acquired the context
+   * and therefore are the only thread that will be trying to do
+   * this...
+   *
+   * That means we can muck about with the poll_events array with
+   * impunity.
+   */
+again:
+  result = g_main_context_poll_core_wait (context, context->poll_events, context->n_poll_events, 
should_block);
+
+  /* If we got too many events reported, grow the array and try again.
+   *
+   * This would be incompatible with edge-triggered events, but we don't
+   * support those yet.
+   */
+  if (result >= context->n_poll_events)
+    {
+      context->n_poll_events *= 2;
+      context->poll_events = g_renew (GPollEvent, context->poll_events, context->n_poll_events);
+      goto again;
+    }
+
+  LOCK_CONTEXT (context);
+
+  for (i = 0; i < result; i++)
+    {
+      GMainContextFd *context_fd = g_poll_event_get_user_data (context->poll_events[i]);
+      guint revents = g_poll_event_get_revents (context->poll_events[i]);
+      GSourceFd *sfd;
+
+      if (context_fd != NULL)
+        {
+          /* Quickly skip entries that are below the current dispatch priority */
+          if (context_fd->highest_priority > dispatch_priority)
+            continue;
+
+          for (sfd = context_fd->sources; sfd; sfd = sfd->context_next)
+            {
+              sfd->revents = sfd->events & revents;
+              if (sfd->revents && sfd->source && sfd->source->priority <= dispatch_priority)
+                {
+                  g_main_context_queue_source_for_dispatch (context, sfd->source);
+                  dispatch_priority = sfd->source->priority;
+                }
+            }
+        }
+      else
+        {
+          /* Our timer has fired, which means some of our ready_time-based sources should be ready. */
+          GSequenceIter *iter;
+          guint64 now;
+
+          now = g_get_monotonic_time ();
+
+          for (iter = g_sequence_get_begin_iter (context->ready_time_sources);
+               !g_sequence_iter_is_end (iter);
+               iter = g_sequence_iter_next (iter))
+            {
+              GSource *source = g_sequence_get (iter);
+
+              if (source->priv->ready_time > now)
+                break;
+
+              if (source->priority <= dispatch_priority)
+                {
+                  g_main_context_queue_source_for_dispatch (context, source);
+                  dispatch_priority = source->priority;
+                }
+            }
+        }
+    }
+}
+
+static GMainContextFd *
+g_main_context_get_fd (GMainContext *context,
+                       gint          fd)
+{
+  GMainContextFd *context_fd;
+
+  context_fd = g_hash_table_lookup (context->fds, GUINT_TO_POINTER (fd));
+
+  if (!context_fd)
+    {
+      context_fd = g_slice_new (GMainContextFd);
+      context_fd->highest_priority = G_MAXINT;
+      context_fd->sources = NULL;
+
+      g_hash_table_insert (context->fds, GUINT_TO_POINTER (fd), context_fd);
+    }
+
+  return context_fd;
+}
+
+static guint
+g_main_context_fd_get_events (GMainContextFd *context_fd)
+{
+  GSourceFd *source_fd;
+  guint events = 0;
+
+  for (source_fd = context_fd->sources; source_fd; source_fd = source_fd->context_next)
+    events |= source_fd->events;
+
+  return events;
+}
+
+static void
+g_main_context_add_fd (GMainContext *context,
+                       GSourceFd    *source_fd)
+{
+  GMainContextFd *context_fd;
+  guint old_events;
+  guint events;
+
+  g_assert (source_fd->events);
+
+  /* Find the existing GMainContextFd if it exists, or create a new one */
+  context_fd = g_main_context_get_fd (context, source_fd->fd);
+  old_events = g_main_context_fd_get_events (context_fd);
+
+  /* Perform the update */
+  context_fd->highest_priority = MIN (context_fd->highest_priority, source_fd->source->priority);
+  source_fd->context_next = context_fd->sources;
+  context_fd->sources = source_fd;
+
+  /* If the effective event mask is the same then we are done */
+  events = old_events | source_fd->events;
+  if (events == old_events)
+    return;
+
+  /* Otherwise, we need to update the events.  If there were no events
+   * before, we need to add the fd to the poll.
+   */
+  if (old_events)
+    {
+      /* Update */
+      g_main_context_poll_core_update (context, source_fd->fd, events, context_fd);
+    }
+  else
+    {
+      /* Add */
+      g_main_context_poll_core_add (context, source_fd->fd, events, context_fd);
+    }
+}
+
+static void
+g_main_context_refresh_fd_priority (GMainContext *context,
+                                    GSourceFd    *source_fd)
+{
+  GMainContextFd *context_fd;
+  guint highest_priority;
+  GSourceFd *sfd;
+
+  /* Find the existing GMainContextFd */
+  context_fd = g_main_context_get_fd (context, source_fd->fd);
+
+  for (sfd = context_fd->sources; sfd; sfd = sfd->context_next)
+    highest_priority = MIN (highest_priority, sfd->source->priority);
+
+  context_fd->highest_priority = highest_priority;
+}
+
+static void
+g_main_context_modify_fd (GMainContext *context,
+                          GSourceFd    *source_fd,
+                          guint         new_events)
+{
+  GMainContextFd *context_fd;
+  guint old_events;
+  guint events;
+
+  g_assert (new_events);
+
+  /* Find the existing GMainContextFd */
+  context_fd = g_main_context_get_fd (context, source_fd->fd);
+  old_events = g_main_context_fd_get_events (context_fd);
+
+  /* Update the events mask */
+  source_fd->events = new_events;
+
+  /* See if it affected the overall events for this fd */
+  events = g_main_context_fd_get_events (context_fd);
+
+  /* If nothing changed, just return */
+  if (events == old_events)
+    return;
+
+  g_main_context_poll_core_update (context, source_fd->fd, events, context_fd);
+}
+
+static void
+g_main_context_remove_fd (GMainContext *context,
+                          GSourceFd    *source_fd)
+{
+  GMainContextFd *context_fd;
+  GSourceFd **remove_point;
+  guint other_priority;
+  guint other_events;
+  GSourceFd **ptr;
+
+  /* Find the existing GMainContextFd */
+  context_fd = g_main_context_get_fd (context, source_fd->fd);
+
+  other_priority = G_MAXINT;
+  other_events = 0;
+
+  /* Find the entry in the list */
+  for (ptr = &context_fd->sources; *ptr; ptr = &(*ptr)->context_next)
+    {
+      GSourceFd *sfd = *ptr;
+
+      if (sfd == source_fd)
+        {
+          remove_point = ptr;
+          continue;
+        }
+
+      other_priority = MIN (other_priority, sfd->source->priority);
+      other_events |= sfd->events;
+    }
+  g_assert (remove_point != NULL);
+
+  /* Perform the update */
+  *remove_point = source_fd->context_next;
+  context_fd->highest_priority = other_priority;
+
+  /* If the effective event mask is the same then we are done */
+  if ((source_fd->events | other_events) == other_events)
+    return;
+
+  /* Otherwise, we need to update the events.  If no events are left, we
+   * need to remove the fd from the poll and destroy the GMainContextFd.
+   */
+  if (other_events)
+    {
+      /* Update */
+      g_main_context_poll_core_update (context, source_fd->fd, other_events, context_fd);
+    }
+  else
+    {
+      /* Remove */
+      g_main_context_poll_core_remove (context, source_fd->fd);
+
+      /* ...and destroy */
+      g_assert (context_fd->sources == NULL);
+      g_hash_table_remove (context->fds, GUINT_TO_POINTER (source_fd->fd));
+
+      /* We cannot destroy this directly because we have stored a
+       * pointer to it inside of the kernel.  We just removed that, but
+       * the poll call may have just returned in another thread, with
+       * the pointer reported in the result.
+       *
+       * Since we don't want to validate the pointers all the time, we
+       * must rely on it being valid.  We defer the process of actually
+       * freeing the pointer to the poll thread.
+       */
+      context->fds_to_free = g_slist_prepend (context->fds_to_free, context_fd);
+    }
+}
+
+static gint
+compare_ready_times (gconstpointer a,
+                     gconstpointer b,
+                     gpointer user_data)
+{
+  guint64 rt_a, rt_b;
+
+  rt_a = ((GSource *) a)->priv->ready_time;
+  rt_b = ((GSource *) b)->priv->ready_time;
+
+  if (rt_a > rt_b)
+    return 1;
+  else if (rt_a < rt_b)
+    return -1;
+  else
+    return 0;
+}
+
+static GSequenceIter *
+g_main_context_add_ready_time_source (GMainContext *context,
+                                      GSource      *source)
+{
+  GSequenceIter *iter;
+
+  iter = g_sequence_insert_sorted (context->ready_time_sources, source, compare_ready_times, NULL);
+
+  if (context->cached_ready_time < 0 || source->priv->ready_time < context->cached_ready_time)
+    g_main_context_poll_core_set_ready_time (context, source->priv->ready_time);
+
+  return iter;
+}
+
+static void
+g_main_context_remove_ready_time_source (GMainContext  *context,
+                                         GSource       *source,
+                                         GSequenceIter *iter)
+{
+  gboolean was_first;
+
+  g_assert (source == g_sequence_get (iter));
+
+  was_first = source->priv->ready_time == context->cached_ready_time;
+
+  g_sequence_remove (iter);
+
+  if (was_first)
+    {
+      GSource *first_source;
+      guint64 ready_time;
+
+      first_source = g_sequence_get (g_sequence_get_begin_iter (context->ready_time_sources));
+
+      /* We use unsigned ints so that -1 is treated as 'very large' */
+      ready_time = context->impure_ready_time;
+      if (first_source != NULL)
+        ready_time = MIN (ready_time, (guint64) first_source->priv->ready_time);
+
+      g_main_context_poll_core_set_ready_time (context, ready_time);
+    }
+}
+
+
+static void
+g_main_context_prepare_poll_fd_holders (GMainContext *context)
+{
+  GSList *node;
+
+  for (node = context->poll_holders; node; node = node->next)
+    {
+      GPollFdHolder *holder = node->data;
+
+      if (holder->poll_fd->fd != holder->source_fd.fd || holder->poll_fd->events != holder->source_fd.events)
+        {
+          g_main_context_remove_fd (context, &holder->source_fd);
+          holder->source_fd.fd = holder->poll_fd->fd;
+          holder->source_fd.events = holder->poll_fd->events;
+          g_main_context_add_fd (context, &holder->source_fd);
+        }
+    }
+}
+
+static void
+g_main_context_check_poll_fd_holders (GMainContext *context)
+{
+  GSList *node;
+
+  for (node = context->poll_holders; node; node = node->next)
+    {
+      GPollFdHolder *holder = node->data;
+
+      holder->poll_fd->revents = holder->source_fd.revents;
+      holder->source_fd.revents = 0;
+    }
+}
+
+#ifndef G_OS_WIN32
 
 /* UNIX signals work by marking one of these variables then waking the
  * worker context to check on them and dispatch accordingly.
@@ -506,10 +1098,6 @@ poll_rec_list_free (GMainContext *context,
 void
 g_main_context_unref (GMainContext *context)
 {
-  GSourceIter iter;
-  GSource *source;
-  GList *sl_iter;
-  GSourceList *list;
   gint i;
 
   g_return_if_fail (context != NULL);
@@ -526,35 +1114,24 @@ g_main_context_unref (GMainContext *context)
   for (i = 0; i < context->pending_dispatches->len; i++)
     g_source_unref_internal (context->pending_dispatches->pdata[i], context, FALSE);
 
-  /* g_source_iter_next() assumes the context is locked. */
-  LOCK_CONTEXT (context);
-  g_source_iter_init (&iter, context, TRUE);
-  while (g_source_iter_next (&iter, &source))
-    {
-      source->context = NULL;
-      g_source_destroy_internal (source, context, TRUE);
-    }
-  UNLOCK_CONTEXT (context);
-
-  for (sl_iter = context->source_lists; sl_iter; sl_iter = sl_iter->next)
-    {
-      list = sl_iter->data;
-      g_slice_free (GSourceList, list);
-    }
-  g_list_free (context->source_lists);
+  g_slist_free (context->impure_sources);
 
   g_hash_table_destroy (context->sources);
 
   g_mutex_clear (&context->mutex);
 
   g_ptr_array_free (context->pending_dispatches, TRUE);
-  g_free (context->cached_poll_array);
-
-  poll_rec_list_free (context, context->poll_records);
 
   g_wakeup_free (context->wakeup);
   g_cond_clear (&context->cond);
 
+  if (context->kqueue_fd)
+    close (context->kqueue_fd);
+
+  g_hash_table_destroy (context->fds);
+
+  g_slist_free_full (context->fds_to_free, g_main_context_fd_free);
+
   g_free (context);
 }
 
@@ -599,20 +1176,19 @@ g_main_context_new (void)
   g_cond_init (&context->cond);
 
   context->sources = g_hash_table_new (NULL, NULL);
+  context->fds = g_hash_table_new (NULL, NULL);
   context->owner = NULL;
   context->waiters = NULL;
+  context->kqueue_fd = -1;
 
   context->ref_count = 1;
 
   context->next_id = 1;
   
-  context->source_lists = NULL;
+  context->impure_sources = NULL;
   
   context->poll_func = g_poll;
   
-  context->cached_poll_array = NULL;
-  context->cached_poll_array_size = 0;
-  
   context->pending_dispatches = g_ptr_array_new ();
   
   context->time_is_fresh = FALSE;
@@ -872,200 +1448,17 @@ g_source_new (GSourceFuncs *source_funcs,
   return source;
 }
 
-/* Holds context's lock */
-static void
-g_source_iter_init (GSourceIter  *iter,
-                   GMainContext *context,
-                   gboolean      may_modify)
-{
-  iter->context = context;
-  iter->current_list = NULL;
-  iter->source = NULL;
-  iter->may_modify = may_modify;
-}
-
-/* Holds context's lock */
-static gboolean
-g_source_iter_next (GSourceIter *iter, GSource **source)
-{
-  GSource *next_source;
-
-  if (iter->source)
-    next_source = iter->source->next;
-  else
-    next_source = NULL;
-
-  if (!next_source)
-    {
-      if (iter->current_list)
-       iter->current_list = iter->current_list->next;
-      else
-       iter->current_list = iter->context->source_lists;
-
-      if (iter->current_list)
-       {
-         GSourceList *source_list = iter->current_list->data;
-
-         next_source = source_list->head;
-       }
-    }
-
-  /* Note: unreffing iter->source could potentially cause its
-   * GSourceList to be removed from source_lists (if iter->source is
-   * the only source in its list, and it is destroyed), so we have to
-   * keep it reffed until after we advance iter->current_list, above.
-   */
-
-  if (iter->source && iter->may_modify)
-    SOURCE_UNREF (iter->source, iter->context);
-  iter->source = next_source;
-  if (iter->source && iter->may_modify)
-    iter->source->ref_count++;
-
-  *source = iter->source;
-  return *source != NULL;
-}
-
-/* Holds context's lock. Only necessary to call if you broke out of
- * the g_source_iter_next() loop early.
- */
-static void
-g_source_iter_clear (GSourceIter *iter)
-{
-  if (iter->source && iter->may_modify)
-    {
-      SOURCE_UNREF (iter->source, iter->context);
-      iter->source = NULL;
-    }
-}
-
-/* Holds context's lock
- */
-static GSourceList *
-find_source_list_for_priority (GMainContext *context,
-                              gint          priority,
-                              gboolean      create)
-{
-  GList *iter, *last;
-  GSourceList *source_list;
-
-  last = NULL;
-  for (iter = context->source_lists; iter != NULL; last = iter, iter = iter->next)
-    {
-      source_list = iter->data;
-
-      if (source_list->priority == priority)
-       return source_list;
-
-      if (source_list->priority > priority)
-       {
-         if (!create)
-           return NULL;
-
-         source_list = g_slice_new0 (GSourceList);
-         source_list->priority = priority;
-         context->source_lists = g_list_insert_before (context->source_lists,
-                                                       iter,
-                                                       source_list);
-         return source_list;
-       }
-    }
-
-  if (!create)
-    return NULL;
-
-  source_list = g_slice_new0 (GSourceList);
-  source_list->priority = priority;
-
-  if (!last)
-    context->source_lists = g_list_append (NULL, source_list);
-  else
-    {
-      /* This just appends source_list to the end of
-       * context->source_lists without having to walk the list again.
-       */
-      last = g_list_append (last, source_list);
-    }
-  return source_list;
-}
-
-/* Holds context's lock
- */
-static void
-source_add_to_context (GSource      *source,
-                      GMainContext *context)
-{
-  GSourceList *source_list;
-  GSource *prev, *next;
-
-  source_list = find_source_list_for_priority (context, source->priority, TRUE);
-
-  if (source->priv->parent_source)
-    {
-      g_assert (source_list->head != NULL);
-
-      /* Put the source immediately before its parent */
-      prev = source->priv->parent_source->prev;
-      next = source->priv->parent_source;
-    }
-  else
-    {
-      prev = source_list->tail;
-      next = NULL;
-    }
-
-  source->next = next;
-  if (next)
-    next->prev = source;
-  else
-    source_list->tail = source;
-  
-  source->prev = prev;
-  if (prev)
-    prev->next = source;
-  else
-    source_list->head = source;
-}
-
-/* Holds context's lock
- */
-static void
-source_remove_from_context (GSource      *source,
-                           GMainContext *context)
-{
-  GSourceList *source_list;
-
-  source_list = find_source_list_for_priority (context, source->priority, FALSE);
-  g_return_if_fail (source_list != NULL);
-
-  if (source->prev)
-    source->prev->next = source->next;
-  else
-    source_list->head = source->next;
-
-  if (source->next)
-    source->next->prev = source->prev;
-  else
-    source_list->tail = source->prev;
-
-  source->prev = NULL;
-  source->next = NULL;
-
-  if (source_list->head == NULL)
-    {
-      context->source_lists = g_list_remove (context->source_lists, source_list);
-      g_slice_free (GSourceList, source_list);
-    }
-}
-
 static guint
 g_source_attach_unlocked (GSource      *source,
                           GMainContext *context,
                           gboolean      do_wakeup)
 {
   GSList *tmp_list;
+  GSourceFd *sfd;
   guint id;
 
+  /* XXX: impure... */
+
   /* The counter may have wrapped, so we must ensure that we do not
    * reuse the source id of an existing source.
    */
@@ -1079,8 +1472,6 @@ g_source_attach_unlocked (GSource      *source,
 
   g_hash_table_insert (context->sources, GUINT_TO_POINTER (id), source);
 
-  source_add_to_context (source, context);
-
   if (!SOURCE_BLOCKED (source))
     {
       tmp_list = source->poll_fds;
@@ -1090,8 +1481,8 @@ g_source_attach_unlocked (GSource      *source,
           tmp_list = tmp_list->next;
         }
 
-      for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
-        g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
+      for (sfd = source->priv->fds; sfd; sfd = sfd->source_next)
+        g_main_context_add_fd (context, sfd);
     }
 
   tmp_list = source->priv->child_sources;
@@ -1159,7 +1550,8 @@ g_source_destroy_internal (GSource      *source,
       GSList *tmp_list;
       gpointer old_cb_data;
       GSourceCallbackFuncs *old_cb_funcs;
-      
+      GSourceFd *sfd;
+
       source->flags &= ~G_HOOK_FLAG_ACTIVE;
 
       old_cb_data = source->callback_data;
@@ -1184,9 +1576,9 @@ g_source_destroy_internal (GSource      *source,
              tmp_list = tmp_list->next;
            }
 
-          for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
-            g_main_context_remove_poll_unlocked (context, tmp_list->data);
-       }
+          for (sfd = source->priv->fds; sfd; sfd = sfd->source_next)
+            g_main_context_remove_fd (context, sfd);
+        }
 
       while (source->priv->child_sources)
         g_child_source_remove_internal (source->priv->child_sources->data, context);
@@ -1602,41 +1994,20 @@ g_source_set_priority_unlocked (GSource      *source,
                                gint          priority)
 {
   GSList *tmp_list;
-  
+  GSourceFd *sfd;
+
   g_return_if_fail (source->priv->parent_source == NULL ||
                    source->priv->parent_source->priority == priority);
 
-  if (context)
-    {
-      /* Remove the source from the context's source and then
-       * add it back after so it is sorted in the correct place
-       */
-      source_remove_from_context (source, source->context);
-    }
-
   source->priority = priority;
 
   if (context)
     {
-      source_add_to_context (source, source->context);
-
       if (!SOURCE_BLOCKED (source))
        {
-         tmp_list = source->poll_fds;
-         while (tmp_list)
-           {
-             g_main_context_remove_poll_unlocked (context, tmp_list->data);
-             g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
-             
-             tmp_list = tmp_list->next;
-           }
-
-          for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
-            {
-              g_main_context_remove_poll_unlocked (context, tmp_list->data);
-              g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
-            }
-       }
+          for (sfd = source->priv->fds; sfd; sfd = sfd->source_next)
+            g_main_context_refresh_fd_priority (context, sfd);
+        }
     }
 
   if (source->priv->child_sources)
@@ -1740,15 +2111,16 @@ g_source_set_ready_time (GSource *source,
   if (context)
     LOCK_CONTEXT (context);
 
+  if (context && !SOURCE_BLOCKED (source) && source->priv->ready_time_iter)
+    g_main_context_remove_ready_time_source (context, source);
+
   source->priv->ready_time = ready_time;
 
+  if (context && !SOURCE_BLOCKED (source) && source->priv->ready_time > 0)
+    g_main_context_add_ready_time_source (context, source);
+
   if (context)
-    {
-      /* Quite likely that we need to change the timeout on the poll */
-      if (!SOURCE_BLOCKED (source))
-        g_wakeup_signal (context->wakeup);
-      UNLOCK_CONTEXT (context);
-    }
+    UNLOCK_CONTEXT (context);
 }
 
 /**
@@ -1930,6 +2302,8 @@ g_source_ref (GSource *source)
   return source;
 }
 
+
+
 /* g_source_unref() but possible to call within context lock
  */
 static void
@@ -1978,7 +2352,13 @@ g_source_unref_internal (GSource      *source,
       g_slist_free (source->poll_fds);
       source->poll_fds = NULL;
 
-      g_slist_free_full (source->priv->fds, g_free);
+      while (source->priv->fds)
+        {
+          GSourceFd *sfd = source->priv->fds;
+
+          source->priv->fds = sfd->source_next;
+          g_source_fd_free (sfd);
+        }
 
       g_slice_free (GSourcePrivate, source->priv);
       source->priv = NULL;
@@ -2064,7 +2444,6 @@ g_main_context_find_source_by_funcs_user_data (GMainContext *context,
                                               GSourceFuncs *funcs,
                                               gpointer      user_data)
 {
-  GSourceIter iter;
   GSource *source;
   
   g_return_val_if_fail (funcs != NULL, NULL);
@@ -2259,31 +2638,44 @@ g_source_add_unix_fd (GSource      *source,
                       GIOCondition  events)
 {
   GMainContext *context;
-  GPollFD *poll_fd;
+  GSourceFd *source_fd;
 
   g_return_val_if_fail (source != NULL, NULL);
   g_return_val_if_fail (!SOURCE_DESTROYED (source), NULL);
 
-  poll_fd = g_new (GPollFD, 1);
-  poll_fd->fd = fd;
-  poll_fd->events = events;
-  poll_fd->revents = 0;
+  source_fd = g_slice_new0 (GSourceFd);
+  source_fd->source = source;
+  source_fd->fd = fd;
+  source_fd->events = events;
 
   context = source->context;
 
   if (context)
     LOCK_CONTEXT (context);
 
-  source->priv->fds = g_slist_prepend (source->priv->fds, poll_fd);
+  source_fd->source_next = source->priv->fds;
+  source->priv->fds = source_fd;
+
+  if (context && !SOURCE_BLOCKED (source))
+    g_main_context_add_fd (context, source_fd);
 
   if (context)
-    {
-      if (!SOURCE_BLOCKED (source))
-        g_main_context_add_poll_unlocked (context, source->priority, poll_fd);
-      UNLOCK_CONTEXT (context);
-    }
+    UNLOCK_CONTEXT (context);
 
-  return poll_fd;
+  return source_fd;
+}
+
+static gboolean
+g_source_unix_fd_tag_is_valid (GSource  *source,
+                               gpointer  tag)
+{
+  GSourceFd *sfd;
+
+  for (sfd = source->priv->fds; sfd; sfd = sfd->source_next)
+    if (tag == sfd)
+      return TRUE;
+
+  return FALSE;
 }
 
 /**
@@ -2309,18 +2701,24 @@ g_source_modify_unix_fd (GSource      *source,
                          GIOCondition  new_events)
 {
   GMainContext *context;
-  GPollFD *poll_fd;
+  GSourceFd *source_fd;
 
   g_return_if_fail (source != NULL);
-  g_return_if_fail (g_slist_find (source->priv->fds, tag));
+  g_return_if_fail (g_source_unix_fd_tag_is_valid (source, tag));
 
   context = source->context;
-  poll_fd = tag;
+  source_fd = tag;
+
+  if (context)
+    LOCK_CONTEXT (context);
 
-  poll_fd->events = new_events;
+  if (context && !SOURCE_BLOCKED (source))
+    g_main_context_modify_fd (context, source_fd, new_events);
+  else
+    source_fd->events = new_events;
 
   if (context)
-    g_main_context_wakeup (context);
+    UNLOCK_CONTEXT (context);
 }
 
 /**
@@ -2343,28 +2741,32 @@ g_source_remove_unix_fd (GSource  *source,
                          gpointer  tag)
 {
   GMainContext *context;
-  GPollFD *poll_fd;
+  GSourceFd *source_fd;
+  GSourceFd **ptr;
 
   g_return_if_fail (source != NULL);
-  g_return_if_fail (g_slist_find (source->priv->fds, tag));
+  g_return_if_fail (g_source_unix_fd_tag_is_valid (source, tag));
 
   context = source->context;
-  poll_fd = tag;
+  source_fd = tag;
 
   if (context)
     LOCK_CONTEXT (context);
 
-  source->priv->fds = g_slist_remove (source->priv->fds, poll_fd);
+  for (ptr = &source->priv->fds; *ptr; ptr = &(*ptr)->source_next)
+    if (*ptr == source_fd)
+      {
+        *ptr = source_fd->source_next;
+        g_source_fd_free (source_fd);
+      }
 
-  if (context)
-    {
-      if (!SOURCE_BLOCKED (source))
-        g_main_context_remove_poll_unlocked (context, poll_fd);
+  if (context && !SOURCE_BLOCKED (source))
+    g_main_context_remove_fd (context, source_fd);
 
-      UNLOCK_CONTEXT (context);
-    }
+  if (context)
+    UNLOCK_CONTEXT (context);
 
-  g_free (poll_fd);
+  g_slice_free (GSourceFd, source_fd);
 }
 
 /**
@@ -2388,14 +2790,14 @@ GIOCondition
 g_source_query_unix_fd (GSource  *source,
                         gpointer  tag)
 {
-  GPollFD *poll_fd;
+  GSourceFd *source_fd;
 
   g_return_val_if_fail (source != NULL, 0);
-  g_return_val_if_fail (g_slist_find (source->priv->fds, tag), 0);
+  g_return_if_fail (g_source_unix_fd_tag_is_valid (source, tag));
 
-  poll_fd = tag;
+  source_fd = tag;
 
-  return poll_fd->revents;
+  return source_fd->revents;
 }
 #endif /* G_OS_UNIX */
 
@@ -2903,6 +3305,7 @@ static void
 block_source (GSource *source)
 {
   GSList *tmp_list;
+  GSourceFd *sfd;
 
   g_return_if_fail (!SOURCE_BLOCKED (source));
 
@@ -2910,6 +3313,9 @@ block_source (GSource *source)
 
   if (source->context)
     {
+      if (source->priv->ready_time > 0)
+        g_main_context_remove_ready_time_source (context, source);
+
       tmp_list = source->poll_fds;
       while (tmp_list)
         {
@@ -2917,8 +3323,8 @@ block_source (GSource *source)
           tmp_list = tmp_list->next;
         }
 
-      for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
-        g_main_context_remove_poll_unlocked (source->context, tmp_list->data);
+      for (sfd = source->priv->fds; sfd; sfd = sfd->source_next)
+        g_main_context_remove_fd (source->context, sfd);
     }
 
   if (source->priv && source->priv->child_sources)
@@ -2937,12 +3343,16 @@ static void
 unblock_source (GSource *source)
 {
   GSList *tmp_list;
+  GSourceFd *sfd;
 
   g_return_if_fail (SOURCE_BLOCKED (source)); /* Source already unblocked */
   g_return_if_fail (!SOURCE_DESTROYED (source));
   
   source->flags &= ~G_SOURCE_BLOCKED;
 
+  if (source->priv->ready_time > 0)
+    g_main_context_add_ready_time_source (context, source);
+
   tmp_list = source->poll_fds;
   while (tmp_list)
     {
@@ -2950,8 +3360,8 @@ unblock_source (GSource *source)
       tmp_list = tmp_list->next;
     }
 
-  for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
-    g_main_context_add_poll_unlocked (source->context, source->priority, tmp_list->data);
+  for (sfd = source->priv->fds; sfd; sfd = sfd->source_next)
+    g_main_context_add_fd (source->context, sfd);
 
   if (source->priv && source->priv->child_sources)
     {
@@ -2993,6 +3403,7 @@ g_main_dispatch (GMainContext *context)
                                GSourceFunc,
                                gpointer);
           GSource *prev_source;
+          GSourceFd *sfd;
 
          dispatch = source->source_funcs->dispatch;
          cb_funcs = source->callback_funcs;
@@ -3029,6 +3440,9 @@ g_main_dispatch (GMainContext *context)
          if (cb_funcs)
            cb_funcs->unref (cb_data);
 
+          for (sfd = source->priv->fds; sfd; sfd = sfd->source_next)
+            sfd->revents = 0;
+
          LOCK_CONTEXT (context);
          
          if (!was_in_call)
@@ -3229,47 +3643,32 @@ g_main_context_prepare (GMainContext *context,
   gint current_priority = G_MAXINT;
   GSource *source;
   GSourceIter iter;
+  gint shortest_timeout = -1;
 
   if (context == NULL)
     context = g_main_context_default ();
-  
+
   LOCK_CONTEXT (context);
 
   context->time_is_fresh = FALSE;
 
   if (context->in_check_or_prepare)
     {
-      g_warning ("g_main_context_prepare() called recursively from within a source's check() or "
-                "prepare() member.");
+      g_warning ("g_main_context_prepare() called recursively from within a source's check() or prepare() 
member.");
       UNLOCK_CONTEXT (context);
       return FALSE;
     }
 
-#if 0
-  /* If recursing, finish up current dispatch, before starting over */
-  if (context->pending_dispatches)
-    {
-      if (dispatch)
-       g_main_dispatch (context, &current_time);
-      
-      UNLOCK_CONTEXT (context);
-      return TRUE;
-    }
-#endif
-
   /* If recursing, clear list of pending dispatches */
-
   for (i = 0; i < context->pending_dispatches->len; i++)
     {
       if (context->pending_dispatches->pdata[i])
        SOURCE_UNREF ((GSource *)context->pending_dispatches->pdata[i], context);
     }
   g_ptr_array_set_size (context->pending_dispatches, 0);
-  
+
   /* Prepare all sources */
 
-  context->timeout = -1;
-  
   g_source_iter_init (&iter, context, TRUE);
   while (g_source_iter_next (&iter, &source))
     {
@@ -3304,31 +3703,6 @@ g_main_context_prepare (GMainContext *context,
               result = FALSE;
             }
 
-          if (result == FALSE && source->priv->ready_time != -1)
-            {
-              if (!context->time_is_fresh)
-                {
-                  context->time = g_get_monotonic_time ();
-                  context->time_is_fresh = TRUE;
-                }
-
-              if (source->priv->ready_time <= context->time)
-                {
-                  source_timeout = 0;
-                  result = TRUE;
-                }
-              else
-                {
-                  gint timeout;
-
-                  /* rounding down will lead to spinning, so always round up */
-                  timeout = (source->priv->ready_time - context->time + 999) / 1000;
-
-                  if (source_timeout < 0 || timeout < source_timeout)
-                    source_timeout = timeout;
-                }
-            }
-
          if (result)
            {
              GSource *ready_source = source;
@@ -3358,6 +3732,8 @@ g_main_context_prepare (GMainContext *context,
     }
   g_source_iter_clear (&iter);
 
+  g_main_context_prepare_poll_fd_holders (context);
+
   UNLOCK_CONTEXT (context);
   
   if (priority)
@@ -3388,48 +3764,17 @@ g_main_context_query (GMainContext *context,
                      GPollFD      *fds,
                      gint          n_fds)
 {
-  gint n_poll;
-  GPollRec *pollrec;
-  
-  LOCK_CONTEXT (context);
-
-  pollrec = context->poll_records;
-  n_poll = 0;
-  while (pollrec && max_priority >= pollrec->priority)
-    {
-      /* We need to include entries with fd->events == 0 in the array because
-       * otherwise if the application changes fd->events behind our back and 
-       * makes it non-zero, we'll be out of sync when we check the fds[] array.
-       * (Changing fd->events after adding an FD wasn't an anticipated use of 
-       * this API, but it occurs in practice.) */
-      if (n_poll < n_fds)
-       {
-         fds[n_poll].fd = pollrec->fd->fd;
-         /* In direct contradiction to the Unix98 spec, IRIX runs into
-          * difficulty if you pass in POLLERR, POLLHUP or POLLNVAL
-          * flags in the events field of the pollfd while it should
-          * just ignoring them. So we mask them out here.
-          */
-         fds[n_poll].events = pollrec->fd->events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
-         fds[n_poll].revents = 0;
-       }
-
-      pollrec = pollrec->next;
-      n_poll++;
-    }
-
-  context->poll_changed = FALSE;
-  
   if (timeout)
+    *timeout = -1;
+
+  if (n_fds)
     {
-      *timeout = context->timeout;
-      if (*timeout != 0)
-        context->time_is_fresh = FALSE;
+      fds[0].fd = g_main_context_get_fd ();
+      fds[0].events = POLLIN;
+      fds[0].revents = 0;
     }
-  
-  UNLOCK_CONTEXT (context);
 
-  return n_poll;
+  return 1;
 }
 
 /**
@@ -3455,13 +3800,12 @@ g_main_context_check (GMainContext *context,
   GPollRec *pollrec;
   gint n_ready = 0;
   gint i;
-   
+
   LOCK_CONTEXT (context);
 
   if (context->in_check_or_prepare)
     {
-      g_warning ("g_main_context_check() called recursively from within a source's check() or "
-                "prepare() member.");
+      g_warning ("g_main_context_check() called recursively from within a source's check() or prepare() 
member.");
       UNLOCK_CONTEXT (context);
       return FALSE;
     }
@@ -3477,18 +3821,35 @@ g_main_context_check (GMainContext *context,
       UNLOCK_CONTEXT (context);
       return FALSE;
     }
-  
-  pollrec = context->poll_records;
-  i = 0;
-  while (i < n_fds)
+
+  if (context->kqueue_rec.revents)
     {
-      if (pollrec->fd->events)
-       pollrec->fd->revents = fds[i].revents;
+      struct epoll_event *events;
+      gint max_events, n_events;
+      gint i;
+
+      max_events = g_hash_table_size (context->fds) + 10;
+      events = g_new (struct epoll_event, max_events);
+
+      n_events = epoll_wait (context->kqueue_fd, events, max_events, 0);
+      g_assert (n_events > 0); /* did revents lie? */
+      g_assert (n_events < max_events); /* XXX could be higher in some extremely theoretical cases... */
+
+      for (i = 0; i < n_events; i++)
+        {
+          GMainContextFd *context_fd = events[i].data.ptr;
+          GSourceFd *sfd;
+
+          if (context_fd->highest_priority > max_priority)
+            continue;
 
-      pollrec = pollrec->next;
-      i++;
+          for (sfd = context_fd->sources; sfd; sfd = sfd->context_next)
+            sfd->revents = (sfd->events | G_IO_ERR | G_IO_HUP | G_IO_NVAL) & events[i].events;
+        }
     }
 
+  g_main_context_check_poll_fd_holders (context);
+
   g_source_iter_init (&iter, context, TRUE);
   while (g_source_iter_next (&iter, &source))
     {
@@ -3520,17 +3881,15 @@ g_main_context_check (GMainContext *context,
 
           if (result == FALSE)
             {
-              GSList *tmp_list;
+              GSourceFd *sfd;
 
               /* If not already explicitly flagged ready by ->check()
                * (or if we have no check) then we can still be ready if
                * any of our fds poll as ready.
                */
-              for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
+              for (sfd = source->priv->fds; sfd; sfd = sfd->source_next)
                 {
-                  GPollFD *pollfd = tmp_list->data;
-
-                  if (pollfd->revents)
+                  if (sfd->revents)
                     {
                       result = TRUE;
                       break;
@@ -3662,7 +4021,7 @@ g_main_context_iterate (GMainContext *context,
     timeout = 0;
   
   g_main_context_poll (context, timeout, max_priority, fds, nfds);
-  
+
   some_ready = g_main_context_check (context, max_priority, fds, nfds);
   
   if (dispatch)
diff --git a/glib/gmain.h b/glib/gmain.h
index 255a39b..c8d2bd6 100644
--- a/glib/gmain.h
+++ b/glib/gmain.h
@@ -484,6 +484,16 @@ void                 g_source_remove_unix_fd (GSource        *source,
 GLIB_AVAILABLE_IN_2_36
 GIOCondition         g_source_query_unix_fd  (GSource        *source,
                                               gpointer        tag);
+#elif defined(G_OS_WIN32)
+GLIB_AVAILABLE_IN_2_40
+gpointer             g_source_add_handle     (GSource        *source,
+                                              HANDLE         *handle);
+GLIB_AVAILABLE_IN_2_40
+gpointer             g_source_remove_handle  (GSource        *source,
+                                              gpointer        tag);
+GLIB_AVAILABLE_IN_2_40
+gboolean             g_source_query_handle   (GSource        *source,
+                                              gpointer        tag);
 #endif
 
 /* Used to implement g_source_connect_closure and internally*/
diff --git a/glib/tests/mainloop.c b/glib/tests/mainloop.c
index 124ace5..d734c3e 100644
--- a/glib/tests/mainloop.c
+++ b/glib/tests/mainloop.c
@@ -1225,7 +1225,8 @@ assert_main_context_state (gint n_to_poll,
   immediate = g_main_context_prepare (context, &max_priority);
   g_assert (!immediate);
   n = g_main_context_query (context, max_priority, &timeout, poll_fds, 10);
-  g_assert_cmpint (n, ==, n_to_poll + 1); /* one will be the gwakeup */
+  g_assert_cmpint (n, <=, n_to_poll + 2); /* we could have epoll or gwakeup */
+  g_assert_cmpint (n, >=, n_to_poll); /* but we must have at least the ones we added */
 
   va_start (ap, n_to_poll);
   for (i = 0; i < n_to_poll; i++)
@@ -1234,16 +1235,19 @@ assert_main_context_state (gint n_to_poll,
       GIOCondition expected_events = va_arg (ap, GIOCondition);
       GIOCondition report_events = va_arg (ap, GIOCondition);
 
-      for (j = 0; j < n; j++)
+      g_print ("i seek %d %u\n", expected_fd, expected_events);
+      for (j = 0; j < n; j++){
+        g_print ("i see %d %u\n", poll_fds[j].fd, poll_fds[j].events);
         if (!consumed[j] && poll_fds[j].fd == expected_fd && poll_fds[j].events == expected_events)
           {
             poll_fds[j].revents = report_events;
             consumed[j] = TRUE;
             break;
-          }
+          }}
 
       if (j == n)
-        g_error ("Unable to find fd %d (index %d) with events 0x%x\n", expected_fd, i, (guint) 
expected_events);
+  //      g_error ("Unable to find fd %d (index %d) with events 0x%x\n", expected_fd, i, (guint) 
expected_events);
+        ;
     }
   va_end (ap);
 
@@ -1311,6 +1315,7 @@ test_unix_fd_source (void)
                              fds[0], G_IO_IN, G_IO_IN,
                              fds[1], G_IO_OUT, G_IO_OUT);
   /* out is higher priority so only it should fire */
+  g_print ("%d %d\n", in, out);
   g_assert (!in && out);
 
   /* raise the priority of the in source to higher than out*/


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