Index: configure.in
===================================================================
RCS file: /cvs/gnome/glib/configure.in,v
retrieving revision 1.485
diff -u -r1.485 configure.in
--- configure.in	6 Jan 2006 05:09:30 -0000	1.485
+++ configure.in	8 Jan 2006 21:50:42 -0000
@@ -789,9 +789,10 @@
 AC_C_BIGENDIAN
 
 # check for header files
-AC_CHECK_HEADERS([dirent.h float.h limits.h pwd.h sys/param.h sys/poll.h])
+AC_CHECK_HEADERS([dirent.h float.h limits.h pwd.h sys/param.h])
 AC_CHECK_HEADERS([sys/time.h sys/times.h sys/wait.h unistd.h values.h])
 AC_CHECK_HEADERS([sys/select.h sys/types.h stdint.h sched.h malloc.h])
+AC_CHECK_HEADERS([sys/poll.h sys/epoll.h])
 
 # Checks for libcharset
 jm_LANGINFO_CODESET
Index: glib/Makefile.am
===================================================================
RCS file: /cvs/gnome/glib/glib/Makefile.am,v
retrieving revision 1.139
diff -u -r1.139 Makefile.am
--- glib/Makefile.am	1 Dec 2005 16:34:19 -0000	1.139
+++ glib/Makefile.am	8 Jan 2006 21:50:42 -0000
@@ -97,6 +97,8 @@
 	gnode.c			\
 	goption.c		\
 	gpattern.c		\
+	gpoll.c			\
+	gpoll.h			\
 	gprimes.c		\
 	gqsort.c		\
 	gqueue.c		\
Index: glib/glib.symbols
===================================================================
RCS file: /cvs/gnome/glib/glib/glib.symbols,v
retrieving revision 1.55
diff -u -r1.55 glib.symbols
--- glib/glib.symbols	5 Jan 2006 06:21:49 -0000	1.55
+++ glib/glib.symbols	8 Jan 2006 21:50:43 -0000
@@ -715,6 +715,18 @@
 #endif
 #endif
 
+#if IN_HEADER(__G_POLL_H__)
+#if IN_FILE(__G_POLL_C__)
+g_pollset_add
+g_pollset_change_priority_cutoff
+g_pollset_new
+g_pollset_ref
+g_pollset_remove
+g_pollset_unref
+g_pollset_wait
+#endif
+#endif
+
 #if IN_HEADER(__G_PRIMES_H__)
 #if IN_FILE(__G_PRIMES_C__)
 g_spaced_primes_closest G_GNUC_CONST
Index: tests/.cvsignore
===================================================================
RCS file: /cvs/gnome/glib/tests/.cvsignore,v
retrieving revision 1.47
diff -u -r1.47 .cvsignore
--- tests/.cvsignore	27 Dec 2005 23:37:28 -0000	1.47
+++ tests/.cvsignore	8 Jan 2006 21:50:43 -0000
@@ -39,6 +39,7 @@
 node-test
 option-test
 printf-test
+poll-test
 queue-test
 qsort-test
 rand-test
Index: tests/Makefile.am
===================================================================
RCS file: /cvs/gnome/glib/tests/Makefile.am,v
retrieving revision 1.82
diff -u -r1.82 Makefile.am
--- tests/Makefile.am	20 Dec 2005 17:35:02 -0000	1.82
+++ tests/Makefile.am	8 Jan 2006 21:50:43 -0000
@@ -86,6 +86,7 @@
 	printf-test				\
 	queue-test				\
 	asyncqueue-test				\
+	poll-test				\
 	qsort-test				\
 	rand-test				\
 	relation-test				\
@@ -149,6 +150,7 @@
 printf_test_LDADD = $(progs_ldadd)
 queue_test_LDADD = $(progs_ldadd)
 asyncqueue_test_LDADD = $(thread_ldadd)
+poll_test_LDADD = $(progs_ldadd)
 qsort_test_LDADD = $(progs_ldadd)
 rand_test_LDADD = $(progs_ldadd)
 relation_test_LDADD = $(progs_ldadd)
Index: glib/gpoll.c
===================================================================
RCS file: glib/gpoll.c
diff -N glib/gpoll.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ glib/gpoll.c	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,1490 @@
+/* gpoll.c: Descriptor poll implementation
+ * Copyright 2005 Mikhail Zabaluev
+ *
+ * This code borrows from gmain.c
+ * Copyright 1998 Owen Taylor
+ *
+ * GLib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GLib; see the file COPYING.LIB.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "glib.h"
+#include "gpoll.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <errno.h>
+#include <string.h>
+
+#if defined(HAVE_SYS_EPOLL_H)
+
+#include <sys/epoll.h>
+
+#elif defined(G_OS_WIN32)
+
+#define STRICT
+#include <windows.h>
+
+#else /* !HAVE_SYS_EPOLL_H && !G_OS_WIN32 */
+
+#ifdef GLIB_HAVE_SYS_POLL_H
+
+#  include <sys/poll.h>
+#  undef events	/* AIX 4.1.5 & 4.3.2 define this for SVR3,4 compatibility */
+#  undef revents /* AIX 4.1.5 & 4.3.2 define this for SVR3,4 compatibility */
+
+/* The poll() emulation on OS/X doesn't handle fds=NULL, nfds=0,
+ * so we prefer our own select-based implementation there.
+ */
+#  ifdef _POLL_EMUL_H_
+#  undef HAVE_POLL
+#  endif
+   
+#endif /* GLIB_HAVE_SYS_POLL_H */
+
+#ifndef HAVE_POLL
+
+/* Code for the select-based implementation comes from the GNU C Library.
+ * Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
+ */
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif /* HAVE_SYS_SELECT_H */
+
+#ifdef G_OS_BEOS
+#undef NO_FD_SET
+#endif /* G_OS_BEOS */
+
+#ifndef NO_FD_SET
+#  define SELECT_MASK fd_set
+#else /* !NO_FD_SET */
+#  ifndef _AIX
+typedef long fd_mask;
+#  endif /* _AIX */
+#  ifdef _IBMR2
+#    define SELECT_MASK void
+#  else /* !_IBMR2 */
+#    define SELECT_MASK int
+#  endif /* !_IBMR2 */
+#endif /* !NO_FD_SET */
+
+#endif /* !HAVE_POLL */
+#endif /* !HAVE_SYS_EPOLL_H && !G_OS_WIN32 */
+
+#include "galias.h"
+
+#ifdef HAVE_SYS_EPOLL_H
+
+/*
+ * An epoll-based implementation
+ */
+
+typedef struct _GPriorityPoll GPriorityPoll;
+typedef struct _GPollRec GPollRec;
+
+struct _GPollSet {
+  /* Reference count */
+  gint ref_count;
+  /* List of GPriorityPoll buckets, sorted by priority */
+  GSList *priority_polls;
+  /* Map of file descriptors to their GPollRec structures */
+  GHashTable *fd_map;
+  /* Descriptor for the master epoll set containing priority epoll fds */
+  gint master_fd;
+  /* Maximum value of priority to include in the poll */
+  gint priority_cutoff;
+  /* Number of priority levels currently polled */
+  gint polled_priorities_count;
+  /* Buffer for descriptor events */
+  struct epoll_event *event_buf;
+  /* Number of elements allocated at event_buf */
+  gsize event_buf_size;
+  /* Buffer for priority poll descriptor events */
+  struct epoll_event *priority_event_buf;
+  /* Number of elements allocated at priority_event_buf */
+  gsize priority_event_buf_size;
+  /* Flag to lock out set modifications from notify handlers */
+  gboolean in_notify;
+};
+
+struct _GPriorityPoll {
+  /* Priority of descriptors in this epoll set */
+  gint priority;
+  /* Epoll descriptor for this priority */
+  gint epoll_fd;
+  /* Number of descriptors currently in the epoll set */
+  gint count;
+};
+
+struct _GPollRec {
+  GPollFD pollfd;
+  GPriorityPoll *bucket;
+  gpointer data;
+};
+
+static inline void g_pollset_disable_master_epoll (GPollSet *pollset);
+
+static void
+destroy_poll_rec (gpointer rec)
+{
+  g_slice_free (GPollRec, rec);
+}
+
+static gint
+priority_poll_match (gconstpointer bucket,
+                     gconstpointer priority)
+{
+  return ((GPriorityPoll *)bucket)->priority - GPOINTER_TO_INT(priority);
+}
+
+static gint
+priority_poll_compare (gconstpointer bucket1,
+                       gconstpointer bucket2)
+{
+  return ((GPriorityPoll *)bucket1)->priority
+	- ((GPriorityPoll *)bucket2)->priority;
+}
+
+static inline guint32
+g_epoll_events_from_io_flags (gushort iocond)
+{
+  /* The following condition is statically evaluated */
+  if (EPOLLIN == G_IO_IN
+      && EPOLLOUT == G_IO_OUT
+      && EPOLLPRI == G_IO_PRI
+      && EPOLLERR == G_IO_ERR
+      && EPOLLHUP == G_IO_HUP)
+    return iocond & (EPOLLIN|EPOLLOUT|EPOLLPRI|EPOLLERR|EPOLLHUP);
+  else
+    {
+      /* Slower piecemeal variant */
+      guint32 events = 0;
+      if (iocond & G_IO_IN)
+	events |= EPOLLIN;
+      if (iocond & G_IO_OUT)
+	events |= EPOLLOUT;
+      if (iocond & G_IO_PRI)
+	events |= EPOLLPRI;
+      if (iocond & G_IO_ERR)
+	events |= EPOLLERR;
+      if (iocond & G_IO_HUP)
+	events |= EPOLLHUP;
+      return events;
+    }
+}
+
+static inline gushort
+g_io_flags_from_epoll_events (guint32 events)
+{
+  /* The following condition is statically evaluated */
+  if (EPOLLIN == G_IO_IN
+      && EPOLLOUT == G_IO_OUT
+      && EPOLLPRI == G_IO_PRI
+      && EPOLLERR == G_IO_ERR
+      && EPOLLHUP == G_IO_HUP)
+    return (gushort)(events & (EPOLLIN|EPOLLOUT|EPOLLPRI|EPOLLERR|EPOLLHUP));
+  else
+    {
+      /* Slower piecemeal variant */
+      gushort iocond = 0;
+      if (events & EPOLLIN)
+	iocond |= G_IO_IN;
+      if (events & EPOLLOUT)
+	iocond |= G_IO_OUT;
+      if (events & EPOLLPRI)
+	iocond |= G_IO_PRI;
+      if (events & EPOLLERR)
+	iocond |= G_IO_ERR;
+      if (events & EPOLLHUP)
+	iocond |= G_IO_HUP;
+      return iocond;
+    }
+}
+
+/**
+ * g_pollset_new:
+ * 
+ * Creates a new #GPollSet object with the reference count of one.
+ * 
+ * Return value: the new #GPollSet
+ **/
+GPollSet *
+g_pollset_new (void)
+{
+  GPollSet *pollset = g_new0 (GPollSet, 1);
+  pollset->ref_count = 1;
+  pollset->priority_polls = NULL;
+  pollset->fd_map = g_hash_table_new_full (NULL,
+                                           NULL,
+                                           NULL,
+                                           destroy_poll_rec);
+  pollset->master_fd = -1;
+  pollset->priority_cutoff = G_MAXINT;
+  pollset->polled_priorities_count = 0;
+  pollset->event_buf = NULL;
+  pollset->event_buf_size = 0;
+  pollset->priority_event_buf = NULL;
+  pollset->priority_event_buf_size = 0;
+  pollset->in_notify = FALSE;
+  return pollset;
+}
+
+static void
+g_pollset_free (GPollSet *pollset)
+{
+  GSList *item;
+
+  g_free (pollset->priority_event_buf);
+  g_free (pollset->event_buf);
+
+  g_hash_table_destroy (pollset->fd_map);
+
+  item = pollset->priority_polls;
+  while (item != NULL)
+    {
+      GPriorityPoll *bucket = (GPriorityPoll *)item->data;
+      close (bucket->epoll_fd);
+      g_slice_free (GPriorityPoll, bucket);
+      item = g_slist_next (item);
+    }
+  g_slist_free (pollset->priority_polls);
+
+  if (pollset->master_fd >= 0)
+    g_pollset_disable_master_epoll (pollset);
+
+  g_free (pollset);
+}
+
+static gboolean
+g_pollset_register_priority_poll (GPollSet *pollset, GPriorityPoll *bucket)
+{
+  struct epoll_event evt;
+  gint rc;
+
+  evt.events = EPOLLIN;
+  evt.data.ptr = bucket;
+  rc = epoll_ctl (pollset->master_fd,
+                  EPOLL_CTL_ADD,
+                  bucket->epoll_fd,
+                  &evt);
+  if (rc != 0)
+    {
+      g_warning ("cascaded EPOLL_CTL_ADD failed: %s", g_strerror (errno));
+      return FALSE;
+    }
+  ++pollset->polled_priorities_count;
+  return TRUE;
+}
+
+static gboolean
+g_pollset_unregister_priority_poll (GPollSet *pollset, GPriorityPoll *bucket)
+{
+  gint rc;
+
+  rc = epoll_ctl (pollset->master_fd,
+                  EPOLL_CTL_DEL,
+                  bucket->epoll_fd,
+                  NULL);
+  if (rc != 0)
+    {
+      g_warning ("cascaded EPOLL_CTL_DEL failed: %s", g_strerror (errno));
+      return FALSE;
+    }
+  --pollset->polled_priorities_count;
+  return TRUE;
+}
+
+static gboolean
+g_pollset_enable_master_epoll (GPollSet *pollset)
+{
+  GSList* item;
+
+  pollset->master_fd = epoll_create (g_slist_length (pollset->priority_polls) + 1);
+  if (pollset->master_fd < 0)
+    {
+      g_warning ("epoll_create failed: %s", g_strerror (errno));
+      return FALSE;
+    }
+
+  item = pollset->priority_polls;
+  g_return_val_if_fail (item != NULL, FALSE);
+  do
+    {
+      GPriorityPoll *bucket = (GPriorityPoll *)item->data;
+      if (bucket->priority <= pollset->priority_cutoff)
+	if (!g_pollset_register_priority_poll (pollset, bucket))
+	  return FALSE;
+      item = g_slist_next (item);
+    }
+  while (item != NULL);
+
+  return TRUE;
+}
+
+static inline void
+g_pollset_disable_master_epoll (GPollSet *pollset)
+{
+  close (pollset->master_fd);
+  pollset->master_fd = -1;
+}
+
+static GPriorityPoll *
+g_pollset_add_priority_poll (GPollSet *pollset,
+                             gint      priority)
+{
+  GPriorityPoll *bucket;
+  bucket = g_slice_new0 (GPriorityPoll);
+  bucket->priority = priority;
+  bucket->count = 0;
+  bucket->epoll_fd = epoll_create (1);
+  if (bucket->epoll_fd < 0)
+    {
+      g_warning ("epoll_create failed: %s", g_strerror (errno));
+      goto fail;
+    }
+
+  if (pollset->priority_polls != NULL)
+    {
+      /* There are going to be multiple poll levels.
+       * This always means that the master epoll shall be established,
+       * even if there is only one priority poll within the current cutoff.
+       * The reason is, it's simpler and less costly to change
+       * the priority cutoff if the master epoll stays up all the time.
+       */
+      if (pollset->master_fd < 0)
+	if (!g_pollset_enable_master_epoll (pollset))
+	  goto fail;
+    }
+
+  if (priority <= pollset->priority_cutoff && pollset->master_fd >= 0)
+    {
+      /* Register the new priority poll with the master epoll.
+       */
+      if (!g_pollset_register_priority_poll (pollset, bucket))
+	goto fail;
+    }
+
+  pollset->priority_polls = g_slist_insert_sorted (pollset->priority_polls,
+                                                   bucket,
+                                                   priority_poll_compare);
+
+  return bucket;
+
+fail:
+  if (bucket->epoll_fd >= 0)
+    close (bucket->epoll_fd);
+  g_slice_free (GPriorityPoll, bucket);
+  return NULL;
+}
+
+static void
+g_pollset_remove_priority_poll (GPollSet      *pollset,
+                                GPriorityPoll *bucket)
+{
+  if (bucket->priority <= pollset->priority_cutoff && pollset->master_fd >= 0)
+    g_pollset_unregister_priority_poll (pollset, bucket);
+
+  pollset->priority_polls = g_slist_remove (pollset->priority_polls, bucket);
+
+  close (bucket->epoll_fd);
+
+  g_slice_free (GPriorityPoll, bucket);
+
+  if (pollset->priority_polls == NULL || pollset->priority_polls->next == NULL)
+    if (pollset->master_fd >= 0)
+      g_pollset_disable_master_epoll (pollset);
+}
+
+/**
+ * g_pollset_add:
+ * @pollset: a #GPollSet
+ * @pollfd: a #GPollFD structure holding information about a file
+ *          descriptor to watch.
+ * @priority: the priority for this file descriptor
+ * @data: user-provided value to associate with this file descriptor
+ * 
+ * Adds a file descriptor to the set of file descriptors pollable with
+ * @pollset. When I/O events are retrieved for this descriptor by
+ * g_pollset_wait(), the supplied notify function is called with
+ * a copy of the @pollfd structure having its @revents field updated
+ * with #GIOCondition flags, and the @data value.
+ * 
+ * If the poll set already contains the same descriptor, the earlier
+ * registration is replaced.
+ * 
+ * Return value: %TRUE on success, %FALSE if an error occurred.
+ **/
+gboolean
+g_pollset_add (GPollSet      *pollset,
+               const GPollFD *pollfd,
+               gint           priority,
+               gpointer       data)
+{
+  struct epoll_event evt;
+  GSList *item;
+  GPriorityPoll *bucket;
+  GPollRec *rec;
+  gint rc;
+
+  g_return_val_if_fail (!pollset->in_notify, FALSE);
+
+  /* Make sure the descriptor isn't duplicated */
+  g_pollset_remove (pollset, pollfd->fd);
+
+  /* Find or create a bucket for the specified priority */
+  item = g_slist_find_custom (pollset->priority_polls,
+                              GINT_TO_POINTER (priority),
+                              priority_poll_match);
+  if (G_LIKELY (item != NULL))
+    bucket = (GPriorityPoll *)item->data;
+  else
+    {
+      bucket = g_pollset_add_priority_poll (pollset, priority);
+      if (bucket == NULL)
+	return FALSE;
+    }
+
+  rec = g_slice_new0 (GPollRec);
+  rec->pollfd = *pollfd;
+  rec->bucket = bucket;
+  rec->data = data;
+
+  /* Add the descriptor and its record to the bucket's epoll set */
+  evt.events = g_epoll_events_from_io_flags (pollfd->events);
+  evt.data.ptr = rec;
+  rc = epoll_ctl (bucket->epoll_fd, EPOLL_CTL_ADD, pollfd->fd, &evt);
+  if (G_UNLIKELY (rc != 0))
+    {
+      g_warning ("EPOLL_CTL_ADD failed: %s", g_strerror (errno));
+      g_slice_free (GPollRec, rec);
+      return FALSE;
+    }
+
+  /* Register the descriptor in data structures */
+  g_hash_table_insert (pollset->fd_map, GINT_TO_POINTER (pollfd->fd), rec);
+  ++bucket->count;
+
+  return TRUE;
+}
+
+/**
+ * g_pollset_remove:
+ * @pollset: a #GPollSet
+ * @fd: a file descriptor to remove
+ * 
+ * Removes file descriptor from the set of file descriptors
+ * pollable with @pollset.
+ * 
+ * Return value: %TRUE on success, %FALSE if the descriptor is not in the set
+ *               or an error has occurred.
+ **/
+gboolean
+g_pollset_remove (GPollSet *pollset,
+                  gint      fd)
+{
+  GPriorityPoll *bucket;
+  GPollRec *rec;
+  gint rc;
+
+  g_return_val_if_fail (!pollset->in_notify, FALSE);
+
+  rec = (GPollRec *)g_hash_table_lookup (pollset->fd_map,
+                                         GINT_TO_POINTER (fd));
+  if (rec == NULL)
+    return FALSE;
+
+  bucket = rec->bucket;
+
+  rc = epoll_ctl (bucket->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
+  if (G_UNLIKELY (rc != 0))
+    {
+      g_warning ("EPOLL_CTL_DEL failed: %s", g_strerror (errno));
+      return FALSE;
+    }
+
+  g_hash_table_remove (pollset->fd_map, GINT_TO_POINTER (fd));
+
+  g_assert (bucket->count > 0);
+  --bucket->count;
+
+  if (bucket->count == 0)
+    g_pollset_remove_priority_poll (pollset, bucket);
+
+  return TRUE;
+}
+
+/**
+ * g_pollset_change_priority_cutoff:
+ * @pollset: a #GPollSet
+ * @new_cutoff: new priority cutoff value
+ * 
+ * Sets the maximum priority for descriptors of this set queried with
+ * g_pollset_wait(). The default is to query all descriptors in the set
+ * (i.e. the default value is %G_MAXINT).
+ * 
+ * Return value: the old cutoff value
+ **/
+gint
+g_pollset_change_priority_cutoff (GPollSet *pollset,
+                                  gint      new_cutoff)
+{
+  GSList *item = pollset->priority_polls;
+  gint old_cutoff = pollset->priority_cutoff;
+  if (new_cutoff < old_cutoff)
+    {
+      while (item != NULL
+		&& ((GPriorityPoll *)item->data)->priority <= new_cutoff)
+	{
+	  item = g_slist_next (item);
+	}
+      while (item != NULL
+		&& ((GPriorityPoll *)item->data)->priority <= old_cutoff)
+	{
+	  g_pollset_unregister_priority_poll (pollset,
+	                                      (GPriorityPoll *)item->data);
+	  item = g_slist_next (item);
+        }
+    }
+  else if (new_cutoff > old_cutoff)
+    {
+      while (item != NULL
+		&& ((GPriorityPoll *)item->data)->priority <= old_cutoff)
+	{
+	  item = g_slist_next (item);
+	}
+      while (item != NULL
+		&& ((GPriorityPoll *)item->data)->priority <= new_cutoff)
+	{
+	  g_pollset_register_priority_poll (pollset,
+	                                    (GPriorityPoll *)item->data);
+	  item = g_slist_next (item);
+        }
+    }
+  pollset->priority_cutoff = new_cutoff;
+  return old_cutoff;
+}
+
+static gboolean
+g_pollset_wait_priority (GPollSet       *pollset,
+                         GPriorityPoll  *bucket,
+                         gint            timeout,
+                         GPollNotifyFunc notify,
+                         gpointer        user_data)
+{
+  struct epoll_event *events;
+  gint n_events;
+  gint i;
+
+  g_return_val_if_fail (bucket->count > 0, FALSE);
+  if (pollset->event_buf_size < bucket->count)
+    {
+      g_free (pollset->event_buf);
+      pollset->event_buf = g_new (struct epoll_event,
+                                  bucket->count);
+      pollset->event_buf_size = bucket->count;
+    }
+  events = pollset->event_buf;
+  n_events = epoll_wait (bucket->epoll_fd,
+                         events,
+                         bucket->count,
+                         timeout);
+  if (G_UNLIKELY (n_events < 0))
+    {
+      if (errno == EINTR)
+	return TRUE;
+      else
+	{
+	  g_warning ("single-priority epoll_wait failed: %s", g_strerror (errno));
+	  return FALSE;
+	}
+    }
+
+  for (i = 0; i < n_events; i++)
+    {
+      GPollRec *rec = (GPollRec *)events[i].data.ptr;
+      rec->pollfd.revents = g_io_flags_from_epoll_events (events[i].events);
+      (*notify) (&rec->pollfd, rec->data, user_data);
+    }
+
+  return TRUE;
+}
+
+/**
+ * g_pollset_wait:
+ * @pollset: a #GPollSet
+ * @timeout: timeout in milliseconds; -1 means wait indefinitely
+ * @notify: function to call for the members that have had activity reported
+ * @user_data: user-defined data to pass to @notify
+ * 
+ * Waits for events on the file descriptors in this poll set. When events are
+ * reported, calls the function passed as @notify with #GPollFD structure
+ * and data parameters filled from the corresponding set member.
+ * FIXME: specify a consistent behavior on an empty or effectively empty set.
+ * 
+ * Return value: %TRUE if events have been reported, or the timeout expired,
+ *               or the wait has been interrupted by a signal. In case of an
+ *               error, %FALSE is returned.
+ **/
+gboolean
+g_pollset_wait (GPollSet       *pollset,
+                gint            timeout,
+                GPollNotifyFunc notify,
+                gpointer        user_data)
+{
+  GPriorityPoll *bucket;
+  struct epoll_event *events;
+  gint n_events;
+  gint i;
+  gboolean pollres;
+
+  g_return_val_if_fail (!pollset->in_notify, FALSE);
+
+  /* TODO: is it OK to return TRUE for an empty set? */
+  if (pollset->priority_polls == NULL)
+    return FALSE;
+
+  if (pollset->priority_polls->next == NULL)
+    {
+      /* Wait on the single available bucket */
+
+      bucket = (GPriorityPoll *)pollset->priority_polls->data;
+
+      /* TODO: is it OK to return TRUE if all registered polls
+       * have been cut off ? */
+      if (bucket->priority > pollset->priority_cutoff)
+	return FALSE;
+
+      pollset->in_notify = TRUE;
+      pollres = g_pollset_wait_priority (pollset,
+                                         bucket,
+                                         timeout,
+                                         notify,
+                                         user_data);
+      pollset->in_notify = FALSE;
+
+      return pollres;
+    }
+
+  g_return_val_if_fail (pollset->master_fd >= 0, FALSE);
+
+  /* TODO: is it OK to return TRUE for an empty set? */
+  if (pollset->polled_priorities_count == 0)
+    return FALSE;
+
+  if (pollset->priority_event_buf_size < pollset->polled_priorities_count)
+    {
+      g_free (pollset->priority_event_buf);
+      pollset->priority_event_buf = g_new (struct epoll_event,
+                                           pollset->polled_priorities_count);
+      pollset->priority_event_buf_size = pollset->polled_priorities_count;
+    }
+
+  events = pollset->priority_event_buf;
+  n_events = epoll_wait (pollset->master_fd,
+                         events,
+                         pollset->polled_priorities_count,
+                         timeout);
+  if (G_UNLIKELY (n_events < 0))
+    {
+      if (errno == EINTR)
+	return TRUE;
+      else
+	{
+	  g_warning ("master epoll_wait failed: %s", g_strerror (errno));
+	  return FALSE;
+	}
+    }
+
+  /* Query all priority polls that have reported activity */
+  pollset->in_notify = TRUE;
+  for (i = 0; i < n_events; i++)
+    {
+      pollres = g_pollset_wait_priority (pollset,
+                                         (GPriorityPoll *)events[i].data.ptr,
+                                         0,
+                                         notify,
+                                         user_data);
+      if (G_UNLIKELY (!pollres))
+	{
+	  pollset->in_notify = FALSE;
+	  return FALSE;
+	}
+    }
+  pollset->in_notify = FALSE;
+
+  return TRUE;
+}
+
+#elif defined(G_OS_WIN32) /* && !HAVE_SYS_EPOLL_H */
+
+typedef struct _GPollRec GPollRec;
+
+struct _GPollRec
+{
+  GPollFD pollfd;
+  gint priority;
+  gpointer data;
+};
+
+struct _GPollSet
+{
+  gint ref_count;
+  guint n_poll_records;
+  guint n_watched_records;
+  gint priority_cutoff;
+  gboolean cache_valid;
+  GPollRec msg_rec;
+  GPollRec poll_records[MAXIMUM_WAIT_OBJECTS];
+  HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+};
+
+GPollSet *
+g_pollset_new (void)
+{
+  GPollSet *pollset;
+  pollset = g_new0 (GPollSet, 1);
+  pollset->ref_count = 1;
+  pollset->n_poll_records = 0;
+  pollset->n_watched_records = 0;
+  pollset->priority_cutoff = G_MAXINT;
+  pollset->cache_valid = FALSE;
+  pollset->msg_rec.pollfd.fd = G_WIN32_MSG_HANDLE;
+  pollset->msg_rec.pollfd.events = 0;
+  return pollset;
+}
+
+static inline void
+g_pollset_free (GPollSet *pollset)
+{
+  g_free (pollset);
+}
+
+gboolean
+g_pollset_add (GPollSet      *pollset,
+               const GPollFD *pollfd,
+               gint           priority,
+               gpointer       data)
+{
+  GPollRec *poll_records;
+  guint n_fds;
+  guint i;
+
+  /* Make sure the descriptor isn't duplicated */
+  g_pollset_remove (pollset, pollfd->fd);
+
+  if (!pollfd->events)
+    return TRUE;
+
+  if (pollfd->fd == G_WIN32_MSG_HANDLE)
+    {
+      /* Only G_IO_IN is meaningful here, and priority is ignored */
+      pollset->msg_rec.pollfd.events = pollfd->events;
+      pollset->msg_rec.data = data;
+      return TRUE;
+    }
+
+  poll_records = pollset->poll_records;
+  n_fds = pollset->n_poll_records;
+
+  if (G_UNLIKELY (n_fds >= MAXIMUM_WAIT_OBJECTS))
+    {
+      g_warning ("Too many handles to wait for");
+      return FALSE;
+    }
+
+  for (i = 0; i < n_fds && priority >= poll_records[i].priority; i++);
+
+  memmove (&poll_records[i + 1],
+           &poll_records[i],
+           (n_fds - i) * sizeof(GPollRec));
+
+  poll_records[i].pollfd.fd = pollfd->fd;
+  poll_records[i].pollfd.events = pollfd->events;
+  /* poll_records[i].pollfd.revents = 0; */
+  poll_records.priority = priority;
+  poll_records.data = data;
+
+  ++pollset->poll_records;
+  if (priority <= pollset->priority_cutoff)
+    {
+      ++pollset->n_watched_records;
+      pollset->cache_valid = FALSE;
+    }
+
+  g_return_val_if_fail (pollset->n_watched_records <= pollset->n_poll_records,
+                        FALSE);
+  return TRUE;
+}
+
+gboolean
+g_pollset_remove (GPollSet *pollset,
+                  gint      fd)
+{
+  GPollRec *poll_records = pollset->poll_records;
+  guint n_fds = pollset->n_poll_records;
+  guint i;
+
+  if (fd == G_WIN32_MSG_HANDLE)
+    {
+      pollset->msg_rec.pollfd.events = 0;
+      return TRUE;
+    }
+
+  /* This is rather inefficient, but since we can't have more than 64 handles
+   * per set, we don't care */
+  for (i = 0; i < n_fds; i++)
+    {
+      if ((HANDLE) poll_records[i].pollfd.fd == (HANDLE) fd)
+	{
+	  memcpy (&poll_records[i],
+	          &poll_records[i + 1],
+	          (n_fds - i - 1) * sizeof(GPollRec));
+	  if (i < pollset->n_watched_records)
+	    {
+	      --pollset->n_watched_records;
+	      pollset->cache_valid = FALSE;
+	    }
+	  --pollset->n_poll_records;
+	  g_return_val_if_fail (pollset->n_watched_records <= pollset->n_poll_records,
+	                        FALSE);
+	  return TRUE;
+	}
+    }
+
+  return FALSE;
+}
+
+static void
+g_pollset_update_cache (GPollSet *pollset)
+{
+  GPollRec *poll_records = pollset->poll_records;
+  HANDLE *handles = pollset->handles;
+  guint n_fds = pollset->n_watched_records;
+  guint i;
+
+  for (i = 0; i < n_fds; i++)
+    handles[i] = (HANDLE) poll_records[i].pollfd.fd;
+
+  pollset->cache_valid = TRUE;
+}
+
+gboolean
+g_pollset_wait (GPollSet       *pollset,
+                gint            timeout,
+                GPollNotifyFunc notify,
+                gpointer        user_data)
+{
+  guint nhandles;
+  DWORD ready;
+  MSG msg;
+
+  if (!pollset->cache_valid)
+    g_pollset_update_cache (pollset);
+
+  if (timeout == -1)
+    timeout = INFINITE;
+
+  nhandles = pollset->n_watched_records;
+  if (pollset->msg_rec.pollfd.events & G_IO_IN)
+    {
+      /* Waiting for messages, and maybe events
+       * -> First PeekMessage
+       */
+#ifdef G_MAIN_POLL_DEBUG
+      g_print ("PeekMessage\n");
+#endif
+      if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
+	ready = WAIT_OBJECT_0 + nhandles;
+      else
+	{
+	  if (nhandles == 0)
+	    {
+	      /* Waiting just for messages */
+	      if (timeout == INFINITE)
+		{
+		  /* Infinite timeout
+		   * -> WaitMessage
+		   */
+#ifdef G_MAIN_POLL_DEBUG
+		  g_print ("WaitMessage\n");
+#endif
+		  if (!WaitMessage ())
+		    {
+		      gchar *emsg = g_win32_error_message (GetLastError ());
+		      g_warning ("WaitMessage() failed: %s", emsg);
+		      g_free (emsg);
+		      ready = WAIT_FAILED;
+		    }
+		  else
+		    ready = WAIT_OBJECT_0;
+		}
+	      else if (timeout == 0)
+		{
+		  /* Waiting just for messages, zero timeout.
+		   * If we got here, there was no message
+		   */
+		  ready = WAIT_TIMEOUT;
+		}
+	      else
+		{
+		  /* Waiting just for messages, some timeout
+		   * -> Set a timer, wait for message,
+		   * kill timer, use PeekMessage
+		   */
+		  UINT_PTR timer = SetTimer (NULL, 0, timeout, NULL);
+		  if (timer == 0)
+		    {
+		      gchar *emsg = g_win32_error_message (GetLastError ());
+		      g_warning ("SetTimer() failed: %s", emsg);
+		      g_free (emsg);
+		      ready = WAIT_FAILED;
+		    }
+		  else
+		    {
+#ifdef G_MAIN_POLL_DEBUG
+		      g_print ("WaitMessage\n");
+#endif
+		      WaitMessage ();
+		      KillTimer (NULL, timer);
+#ifdef G_MAIN_POLL_DEBUG
+		      g_print ("PeekMessage\n");
+#endif
+		      if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)
+			  && (msg.message != WM_TIMER || msg.hwnd != NULL))
+			ready = WAIT_OBJECT_0;
+		      else
+			ready = WAIT_TIMEOUT;
+		    }
+		}
+	    }
+	  else
+	    {
+	      /* Wait for either message or event
+	       * -> Use MsgWaitForMultipleObjects
+	       */
+#ifdef G_MAIN_POLL_DEBUG
+	      g_print ("MsgWaitForMultipleObjects(%d, %d)\n", nhandles, timeout);
+#endif
+	      ready = MsgWaitForMultipleObjects (nhandles,
+	                                         pollset->handles,
+	                                         FALSE,
+						 timeout,
+						 QS_ALLINPUT);
+	      if (ready == WAIT_FAILED)
+		{
+		  gchar *emsg = g_win32_error_message (GetLastError ());
+		  g_warning ("MsgWaitForMultipleObjects() failed: %s", emsg);
+		  g_free (emsg);
+		}
+	    }
+	}
+    }
+  else
+    {
+      /* Wait for just events
+       * -> Use WaitForMultipleObjects
+       */
+#ifdef G_MAIN_POLL_DEBUG
+      g_print ("WaitForMultipleObjects(%d, %d)\n", nhandles, timeout);
+#endif
+      ready = WaitForMultipleObjects (nhandles,
+                                      pollset->handles,
+                                      FALSE,
+                                      timeout);
+      if (ready == WAIT_FAILED)
+	{
+	  gchar *emsg = g_win32_error_message (GetLastError ());
+	  g_warning ("WaitForMultipleObjects() failed: %s", emsg);
+	  g_free (emsg);
+	}
+    }
+
+  if (ready == WAIT_FAILED)
+    return FALSE;
+  else if (ready == WAIT_TIMEOUT)
+    return TRUE;
+  else if (ready == WAIT_OBJECT_0 + nhandles)
+    {
+      g_return_val_if_fail (pollset->msg_rec.events & G_IO_IN, TRUE);
+      pollset->msg_rec.revents = G_IO_IN;
+      (*notify) (&pollset->msg_rec.pollfd, pollset->msg_rec.data, user_data);
+    }
+  else if (ready < WAIT_OBJECT_0 + nhandles && ready >= WAIT_OBJECT_0)
+    {
+      GPollRec *pollrec = &pollset->poll_records[ready - WAIT_OBJECT_0];
+      pollrec->revents = pollrec->events;
+      (*notify) (&pollrec->pollfd, pollrec->data, user_data);
+    }
+  else if (ready >= WAIT_ABANDONED_0 && ready < WAIT_ABANDONED_0 + nhandles)
+    {
+      GPollRec *pollrec = &pollset->poll_records[ready - WAIT_ABANDONED_0];
+      pollrec->revents = G_IO_HUP;
+      (*notify) (&pollrec->pollfd, pollrec->data, user_data);
+    }
+  else
+    g_return_val_if_reached (FALSE);
+
+  return TRUE;
+}
+
+#else /* !G_OS_WIN32 && !HAVE_SYS_EPOLL_H */
+
+/* 
+ * An implementation based on poll or select
+ */
+
+/* SunOS has poll, but doesn't provide a prototype. */
+#  if defined (sun) && !defined (__SVR4)
+struct pollfd {
+  gint fd;
+  gushort events;
+  gushort revents;
+};
+extern gint poll (struct pollfd *ufds, guint nfsd, gint timeout);
+#  endif  /* !sun */
+
+typedef struct _GPollRec GPollRec;
+
+struct _GPollSet
+{
+  gint ref_count;
+  GPollRec *poll_records;
+  guint n_watched_records;
+  gint priority_cutoff;
+#ifdef HAVE_POLL
+  struct pollfd *cached_poll_array;
+  guint cached_poll_array_size;
+#endif
+  gboolean cache_valid;
+  gboolean in_notify;
+};
+
+struct _GPollRec
+{
+  GPollFD pollfd;
+  GPollRec *next;
+  gint priority;
+  gpointer data;
+};
+
+GPollSet *
+g_pollset_new (void)
+{
+  GPollSet *pollset;
+  pollset = g_new0 (GPollSet, 1);
+  pollset->ref_count = 1;
+  pollset->poll_records = NULL;
+  pollset->n_watched_records = 0;
+  pollset->priority_cutoff = G_MAXINT;
+#ifdef HAVE_POLL
+  pollset->cached_poll_array = NULL;
+  pollset->cached_poll_array_size = 0;
+#endif
+  pollset->cache_valid = FALSE;
+  pollset->in_notify = FALSE;
+  return pollset;
+}
+
+static void
+g_pollset_free (GPollSet *pollset)
+{
+#ifdef HAVE_POLL
+  g_free (pollset->cached_poll_array);
+#endif
+  g_slice_free_chain (GPollRec, pollset->poll_records, next);
+  g_free (pollset);
+}
+
+static GPollRec *
+g_pollset_remove_rec (GPollSet *pollset,
+                      GPollRec *pollrec,
+                      GPollRec *lastrec)
+{
+  GPollRec *nextrec = pollrec->next;
+  if (lastrec != NULL)
+    lastrec->next = nextrec;
+  else
+    pollset->poll_records = nextrec;
+
+  if (pollrec->priority <= pollset->priority_cutoff)
+    {
+      --pollset->n_watched_records;
+      pollset->cache_valid = FALSE;
+    }
+
+  g_slice_free (GPollRec, pollrec);
+
+  return nextrec;
+}
+
+gboolean
+g_pollset_add (GPollSet      *pollset,
+               const GPollFD *pollfd,
+               gint           priority,
+               gpointer       data)
+{
+  GPollRec *lastrec;
+  GPollRec *pollrec;
+  GPollRec *newrec;
+  gboolean dupe_found = FALSE;
+
+  g_return_val_if_fail (!pollset->in_notify, FALSE);
+
+#if !defined(HAVE_POLL) && defined(FD_SETSIZE)
+  if (G_UNLIKELY (pollfd->fd >= FD_SETSIZE))
+    {
+      g_warning ("Descriptor %d is out of FD_SETSIZE (%d)",
+                 pollfd->fd, (gint)FD_SETSIZE);
+      return FALSE;
+    }
+#endif
+
+  newrec = g_slice_new (GPollRec);
+  newrec->pollfd.fd = pollfd->fd;
+  newrec->pollfd.events = pollfd->events;
+  /* newrec->pollfd.revents = 0; */
+  newrec->priority = priority;
+  newrec->data = data;
+
+  /* Find the place for the new record in the record list,
+   * all the while removing a duplicate record for the same file descriptor
+   * if such is found */
+  lastrec = NULL;
+  pollrec = pollset->poll_records;
+  while (pollrec != NULL && priority >= pollrec->priority)
+    {
+      if (pollrec->pollfd.fd == pollfd->fd)
+	{
+	  pollrec = g_pollset_remove_rec (pollset, pollrec, lastrec);
+	  dupe_found = TRUE;
+	}
+      else
+	{
+	  lastrec = pollrec;
+	  pollrec = pollrec->next;
+	}
+    }
+
+  /* Insert the new record into the record list */
+  if (lastrec != NULL)
+    lastrec->next = newrec;
+  else
+    pollset->poll_records = newrec;
+  newrec->next = pollrec;
+
+  if (!dupe_found)
+    {
+      /* Proceed down the rest of the record list
+       * to remove the duplicate descriptor record if it's there */
+      lastrec = newrec;
+      while (pollrec != NULL)
+	{
+	  if (pollrec->pollfd.fd == pollfd->fd)
+	    {
+	      /* pollrec = */
+	      g_pollset_remove_rec (pollset, pollrec, lastrec);
+	      /* dupe_found = TRUE; */
+	      break;
+	    }
+	  lastrec = pollrec;
+	  pollrec = pollrec->next;
+	}
+    }
+
+  if (priority <= pollset->priority_cutoff)
+    {
+      ++pollset->n_watched_records;
+      pollset->cache_valid = FALSE;
+    }
+
+  return TRUE;
+}
+
+gboolean
+g_pollset_remove (GPollSet *pollset,
+                  gint      fd)
+{
+  GPollRec *lastrec;
+  GPollRec *pollrec;
+
+  g_return_val_if_fail (!pollset->in_notify, FALSE);
+
+  lastrec = NULL;
+  pollrec = pollset->poll_records;
+  while (pollrec)
+    {
+      if (pollrec->pollfd.fd == fd)
+	{
+	  g_pollset_remove_rec(pollset, pollrec, lastrec);
+	  return TRUE;
+	}
+      lastrec = pollrec;
+      pollrec = pollrec->next;
+    }
+
+  return FALSE;
+}
+
+gint
+g_pollset_change_priority_cutoff (GPollSet *pollset,
+                                  gint      new_cutoff)
+{
+  GPollRec *pollrec;
+  guint nrecords;
+  gint old_cutoff = pollset->priority_cutoff;
+  if (new_cutoff != old_cutoff)
+    {
+      nrecords = 0;
+      pollrec = pollset->poll_records;
+      while (pollrec)
+	{
+	  if (pollrec->priority > new_cutoff)
+	    break;
+	  ++nrecords;
+	  pollrec = pollrec->next;
+	}
+
+      if (nrecords > pollset->n_watched_records)
+	pollset->cache_valid = FALSE;
+
+      pollset->n_watched_records = nrecords;
+      pollset->priority_cutoff = new_cutoff;
+    }
+  return old_cutoff;
+}
+
+#ifdef HAVE_POLL
+
+static void
+g_pollset_update_cache (GPollSet *pollset)
+{
+  GPollRec *pollrec;
+  struct pollfd *fds;
+  guint n_fds;
+  guint i;
+
+  if (pollset->cached_poll_array_size < pollset->n_watched_records)
+    {
+      g_free (pollset->cached_poll_array);
+      pollset->cached_poll_array = g_new (struct pollfd,
+                                          pollset->n_watched_records);
+      pollset->cached_poll_array_size = pollset->n_watched_records;
+    }
+
+  fds = pollset->cached_poll_array;
+  n_fds = pollset->n_watched_records;
+  pollrec = pollset->poll_records;
+  for (i = 0; i < n_fds; i++)
+    {
+      fds[i].fd = pollrec->pollfd.fd;
+      fds[i].events = pollrec->pollfd.events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
+      /* fds[i].revents = 0; */
+      pollrec = pollrec->next;
+    }
+
+  pollset->cache_valid = TRUE;
+}
+
+#endif /* HAVE_POLL */
+
+gboolean
+g_pollset_wait (GPollSet       *pollset,
+                gint            timeout,
+                GPollNotifyFunc notify,
+                gpointer        user_data)
+{
+
+#ifdef HAVE_POLL
+
+  GPollRec *pollrec;
+  struct pollfd *fds;
+  guint n_fds;
+  guint i;
+  gint changed;
+  gint notified;
+
+  g_return_val_if_fail (!pollset->in_notify, FALSE);
+
+  if (!pollset->cache_valid)
+    g_pollset_update_cache (pollset);
+
+  fds = pollset->cached_poll_array;
+  n_fds = pollset->n_watched_records;
+
+  g_return_val_if_fail (n_fds <= pollset->cached_poll_array_size, FALSE);
+
+  changed = poll (fds, n_fds, timeout);
+  if (changed == 0)
+    return TRUE;
+  if (G_UNLIKELY (changed < 0))
+    {
+      if (errno == EINTR)
+        return TRUE;
+      g_warning ("poll failed: %s", g_strerror(errno));
+      return FALSE;
+    }
+
+  pollset->in_notify = TRUE;
+  notified = 0;
+  pollrec = pollset->poll_records;
+  for (i = 0; i < n_fds; i++)
+    {
+      if (fds[i].revents != 0)
+	{
+	  (*notify) ((const GPollFD *)&fds[i], pollrec->data, user_data);
+	  ++notified;
+	  if (notified == changed)
+	    break;
+	}
+      pollrec = pollrec->next;
+    }
+  pollset->in_notify = FALSE;
+
+#else /* !HAVE_POLL */
+
+  SELECT_MASK rset;
+  SELECT_MASK wset;
+  SELECT_MASK xset;
+  GPollRec *pollrec;
+  guint n_fds;
+  guint i;
+  int maxfd;
+  gint changed;
+
+  FD_ZERO (&rset);
+  FD_ZERO (&wset);
+  FD_ZERO (&xset);
+
+  maxfd = 0;
+  n_fds = pollset->n_watched_records;
+  pollrec = pollset->poll_records;
+  for (i = 0; i < n_fds; i++)
+    {
+      gint fd = pollrec->pollfd.fd;
+      gushort events = pollrec->pollfd.events;
+      if (events & G_IO_IN)
+        FD_SET (fd, &rset);
+      if (events & G_IO_OUT)
+        FD_SET (fd, &wset);
+      if (events & G_IO_PRI)
+        FD_SET (fd, &xset);
+      if (fd > maxfd && (events & (G_IO_IN|G_IO_OUT|G_IO_PRI)))
+	maxfd = fd;
+      pollrec = pollrec->next;
+    }
+
+  if (timeout < 0)
+    changed = select (maxfd + 1, &rset, &wset, &xset, NULL);
+  else
+    {
+      struct timeval tv;
+      tv.tv_sec = timeout / 1000;
+      tv.tv_usec = (timeout % 1000) * 1000;
+      changed = select (maxfd + 1, &rset, &wset, &xset, &tv);
+      if (changed == 0)
+	return TRUE;
+    }
+
+  if (G_UNLIKELY (changed < 0))
+    {
+      if (errno == EINTR)
+        return TRUE;
+      g_warning ("select failed: %s", g_strerror(errno));
+      return FALSE;
+    }
+
+  pollset->in_notify = TRUE;
+  pollrec = pollset->poll_records;
+  for (i = 0; i < n_fds; i++)
+    {
+      gint fd = pollrec->pollfd.fd;
+      gushort events = 0;
+      if (FD_ISSET (fd, &rset))
+	events |= G_IO_IN;
+      if (FD_ISSET (fd, &wset))
+	events |= G_IO_OUT;
+      if (FD_ISSET (fd, &xset))
+	events |= G_IO_PRI;
+
+      if (events != 0)
+	{
+	  pollrec->pollfd.revents = events;
+	  (*notify) (&pollrec->pollfd, pollrec->data, user_data);
+	}
+
+      pollrec = pollrec->next;
+    }
+  pollset->in_notify = FALSE;
+
+#endif /* !HAVE_POLL */
+
+  return TRUE;
+}
+
+#endif /* !G_OS_WIN32 && !HAVE_SYS_EPOLL_H */
+
+/*
+ * Common refcounting functions
+ */
+
+/**
+ * g_pollset_ref:
+ * @pollset: a #GPollSet
+ * 
+ * Increases the reference count on a #GPollSet object by one.
+ * This function is MT-safe.
+ * 
+ * Return value: the @pollset
+ **/
+GPollSet*
+g_pollset_ref (GPollSet *pollset)
+{
+  g_return_val_if_fail (pollset != NULL, NULL);
+  g_return_val_if_fail (pollset->ref_count > 0, NULL);
+
+  g_atomic_int_inc (&pollset->ref_count);
+
+  return pollset;
+}
+
+/**
+ * g_pollset_unref:
+ * @pollset: a #GPollSet
+ * 
+ * Decreases the reference count on a #GPollSet object by one.
+ * If the resulting count is zero, frees the object and all
+ * associated resources.
+ * This function is MT-safe.
+ **/
+void
+g_pollset_unref (GPollSet *pollset)
+{
+  g_return_if_fail (pollset != NULL);
+  g_return_if_fail (pollset->ref_count > 0);
+
+  if (g_atomic_int_dec_and_test (&pollset->ref_count))
+    g_pollset_free (pollset);
+}
+
+
+#define __G_POLL_C__
+#include "galiasdef.c"
Index: glib/gpoll.h
===================================================================
RCS file: glib/gpoll.h
diff -N glib/gpoll.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ glib/gpoll.h	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,48 @@
+/* gpoll.h - the GLib poll set
+ * Copyright (C) 2005 Mikhail Zabaluev
+ *
+ * GLib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with GLib; see the file COPYING.LIB.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __G_POLL_H__
+#define __G_POLL_H__
+
+G_BEGIN_DECLS
+
+typedef struct _GPollSet GPollSet;
+
+typedef void (*GPollNotifyFunc) (const GPollFD *fd,
+                                 gpointer fd_data,
+                                 gpointer user_data);
+
+GPollSet *g_pollset_new    (void);
+GPollSet *g_pollset_ref    (GPollSet       *pollset);
+void      g_pollset_unref  (GPollSet       *pollset);
+gboolean  g_pollset_add    (GPollSet       *pollset,
+                            const GPollFD  *pollfd,
+                            gint            priority,
+                            gpointer        data);
+gboolean  g_pollset_remove (GPollSet       *pollset,
+                            gint            fd);
+gint      g_pollset_change_priority_cutoff (GPollSet *pollset,
+                                            gint      new_cutoff);
+gboolean  g_pollset_wait   (GPollSet       *pollset,
+                            gint            timeout,
+                            GPollNotifyFunc notify,
+                            gpointer        user_data);
+
+G_END_DECLS
+
+#endif /* __G_POLL_H__ */
Index: tests/poll-test.c
===================================================================
RCS file: tests/poll-test.c
diff -N tests/poll-test.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/poll-test.c	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,337 @@
+#undef G_DISABLE_ASSERT
+#undef G_LOG_DOMAIN
+
+#include <glib.h>
+#include <gpoll.h>
+#include <config.h>
+
+#ifdef G_OS_UNIX
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+
+
+#define NPIPES 2
+#define NDESCS (NPIPES * 2)
+
+
+static gchar test_string[] = "some stuff fit for a pipe";
+
+#define TEST_STRING_LENGTH (G_N_ELEMENTS(test_string) - 1)
+
+
+static gint    pipes  [NDESCS];
+static gushort revents[NDESCS];
+
+static gpointer user_data_token = pipes;
+
+static void
+clear_revents (void)
+{
+  gint i;
+  for (i = 0; i < NDESCS; i++)
+    revents[i] = 0;
+}
+
+static void
+poll_event_notify (const GPollFD *fd,
+                   gpointer poll_data,
+                   gpointer user_data)
+{
+  g_assert (user_data == user_data_token);
+  *(gushort *)poll_data = fd->revents;
+}
+
+static void
+add_pipe_read (GPollSet *pollset, gint pipe_index, gint priority)
+{
+  gboolean added;
+  GPollFD pollfd;
+  pollfd.fd = pipes[pipe_index * 2];
+  pollfd.events = G_IO_IN;
+  added = g_pollset_add (pollset,
+                         &pollfd,
+                         priority,
+                         &revents[pipe_index * 2]);
+  if (!added)
+    {
+      g_warning ("g_pollset_add failed");
+      exit (1);
+    }
+}
+
+static void
+add_pipe_write (GPollSet *pollset, gint pipe_index, gint priority)
+{
+  gboolean added;
+  GPollFD pollfd;
+  pollfd.fd = pipes[pipe_index * 2 + 1];
+  pollfd.events = G_IO_OUT;
+  added = g_pollset_add (pollset,
+                         &pollfd,
+                         priority,
+                         &revents[pipe_index * 2 + 1]);
+  if (!added)
+    {
+      g_warning ("g_pollset_add failed");
+      exit (1);
+    }
+}
+
+static void
+remove_pipe_read (GPollSet *pollset, gint pipe_index)
+{
+  gboolean removed;
+  removed = g_pollset_remove (pollset, pipes[pipe_index * 2]);
+  if (!removed)
+    {
+      g_warning ("g_pollset_remove failed");
+      exit (1);
+    }
+}
+
+static void
+remove_pipe_write (GPollSet *pollset, gint pipe_index)
+{
+  gboolean removed;
+  removed = g_pollset_remove (pollset, pipes[pipe_index * 2 + 1]);
+  if (!removed)
+    {
+      g_warning ("g_pollset_remove failed");
+      exit (1);
+    }
+}
+
+static gboolean
+check_pipe_read (GPollSet *pollset, gint pipe_index)
+{
+  return ((revents[pipe_index * 2] & G_IO_IN) != 0);
+}
+
+static gboolean
+check_pipe_write (GPollSet *pollset, gint pipe_index)
+{
+  return ((revents[pipe_index * 2 + 1] & G_IO_OUT) != 0);
+}
+
+static void
+write_to_pipe (gint pipe_index)
+{
+  ssize_t bytes_written;
+  bytes_written = write (pipes[pipe_index * 2 + 1],
+                         test_string,
+                         TEST_STRING_LENGTH);
+  if (bytes_written < 0)
+    {
+      g_warning ("write failed: %s", g_strerror (errno));
+      exit (1);
+    }
+}
+
+static gint
+get_fd_level (void)
+{
+  gint fd_level = -1;
+#ifdef G_OS_UNIX
+  fd_level = dup (STDOUT_FILENO);
+  if (fd_level < 0) 
+    {
+      g_warning ("Descriptor dup failed: %s", g_strerror (errno));
+      exit (1);
+    }
+  close (fd_level);
+#endif
+  return fd_level;
+}
+
+static void
+check_fd_level (gint fd_level_before)
+{
+#ifdef G_OS_UNIX
+  if (get_fd_level () != fd_level_before)
+    {
+      g_warning ("Descriptor leak detected");
+      exit (1);
+    }
+#endif
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  GPollSet *pollset;
+  gint fd_level_before;
+  gint old_cutoff;
+  gint i;
+  gint j;
+
+  for (i = 0; i < NPIPES; i++)
+    {
+      if (pipe (pipes + i * 2) != 0)
+	{
+	  g_warning ("Cannot create pipe: %s", g_strerror (errno));
+	  return 1;
+	}
+    }
+
+  fd_level_before = get_fd_level();
+
+  pollset = g_pollset_new ();
+  g_assert (pollset != NULL);
+
+  add_pipe_write (pollset, 0, G_PRIORITY_DEFAULT);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 0));
+
+  add_pipe_write (pollset, 1, G_PRIORITY_HIGH);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 0));
+  g_assert (check_pipe_write (pollset, 1));
+
+  add_pipe_read (pollset, 0, G_PRIORITY_DEFAULT);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 0));
+  g_assert (check_pipe_write (pollset, 1));
+  g_assert (!check_pipe_read (pollset, 0));
+
+  write_to_pipe (0);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 0));
+  g_assert (check_pipe_write (pollset, 1));
+  g_assert (check_pipe_read  (pollset, 0));
+
+  old_cutoff = g_pollset_change_priority_cutoff (pollset, G_PRIORITY_HIGH);
+  g_assert (old_cutoff == G_MAXINT);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (!check_pipe_write (pollset, 0));
+  g_assert (check_pipe_write  (pollset, 1));
+  g_assert (!check_pipe_read  (pollset, 0));
+
+  add_pipe_read (pollset, 1, G_PRIORITY_LOW);
+  write_to_pipe (1);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (!check_pipe_write (pollset, 0));
+  g_assert (check_pipe_write  (pollset, 1));
+  g_assert (!check_pipe_read  (pollset, 0));
+  g_assert (!check_pipe_read  (pollset, 1));
+
+  old_cutoff = g_pollset_change_priority_cutoff (pollset, G_PRIORITY_DEFAULT);
+  g_assert (old_cutoff == G_PRIORITY_HIGH);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 0));
+  g_assert (check_pipe_write (pollset, 1));
+  g_assert (check_pipe_read  (pollset, 0));
+  g_assert (!check_pipe_read (pollset, 1));
+
+  old_cutoff = g_pollset_change_priority_cutoff (pollset, G_PRIORITY_LOW);
+  g_assert (old_cutoff == G_PRIORITY_DEFAULT);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 0));
+  g_assert (check_pipe_write (pollset, 1));
+  g_assert (check_pipe_read  (pollset, 0));
+  g_assert (check_pipe_read  (pollset, 1));
+
+  remove_pipe_write (pollset, 0);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (!check_pipe_write (pollset, 0));
+  g_assert (check_pipe_write  (pollset, 1));
+  g_assert (check_pipe_read   (pollset, 0));
+  g_assert (check_pipe_read   (pollset, 1));
+
+  old_cutoff = g_pollset_change_priority_cutoff (pollset, G_PRIORITY_DEFAULT);
+  g_assert (old_cutoff == G_PRIORITY_LOW);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 1));
+  g_assert (check_pipe_read  (pollset, 0));
+  g_assert (!check_pipe_read (pollset, 1));
+
+  remove_pipe_read (pollset, 1);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 1));
+  g_assert (check_pipe_read  (pollset, 0));
+
+  old_cutoff = g_pollset_change_priority_cutoff (pollset, G_PRIORITY_LOW);
+  g_assert (old_cutoff == G_PRIORITY_DEFAULT);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 1));
+  g_assert (check_pipe_read  (pollset, 0));
+  g_assert (!check_pipe_read (pollset, 1));
+
+  remove_pipe_write (pollset, 1);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 0, poll_event_notify, user_data_token));
+  g_assert (!check_pipe_write (pollset, 1));
+  g_assert (check_pipe_read   (pollset, 0));
+
+  g_pollset_unref (pollset);
+
+  check_fd_level (fd_level_before);
+
+  /* Check a few more cases not covered by the previous tests */
+
+  pollset = g_pollset_new ();
+  g_assert (pollset != NULL);
+
+  add_pipe_write (pollset, 0, G_PRIORITY_DEFAULT);
+  add_pipe_write (pollset, 1, G_PRIORITY_HIGH);
+
+  g_pollset_change_priority_cutoff (pollset, G_PRIORITY_DEFAULT);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 500, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 0));
+  g_assert (check_pipe_write (pollset, 1));
+
+  /* Should remove previously added descriptor with G_PRIORITY_HIGH */
+  add_pipe_write (pollset, 1, G_PRIORITY_LOW);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 1500, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write  (pollset, 0));
+  g_assert (!check_pipe_write (pollset, 1));
+
+  /* Should remove previously added descriptor with G_PRIORITY_LOW */
+  add_pipe_write (pollset, 1, G_PRIORITY_DEFAULT);
+
+  clear_revents ();
+  g_assert (g_pollset_wait (pollset, 1500, poll_event_notify, user_data_token));
+  g_assert (check_pipe_write (pollset, 0));
+  g_assert (check_pipe_write (pollset, 1));
+
+  /* Unref a set with more than single priority level */
+  g_pollset_unref (pollset);
+
+  check_fd_level (fd_level_before);
+
+  for (j = 0; j < NDESCS; j++)
+    close (pipes[j]);
+
+  return 0;
+}
