Index: select.c
===================================================================
--- select.c	(revision fb961524492fcc80a144e70d4b31e3f46146dbb8)
+++ select.c	(revision 33b6431b480426ea3c8334c4c6de3f19ad477071)
@@ -1,15 +1,13 @@
 #include "owl.h"
-#include <sys/stat.h>
-
+
+static GMainLoop *loop = NULL;
+static GMainContext *context;
 static int dispatch_active = 0;
-static int psa_active = 0;
-static int loop_active = 0;
-
-int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
+
+static GSource *owl_timer_source;
+static GSource *owl_io_dispatch_source;
+
+static int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
   return t1->time - t2->time;
-}
-
-int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
-  return t1 == t2;
 }
 
@@ -44,19 +42,64 @@
 }
 
-void owl_select_process_timers(struct timespec *timeout)
-{
-  time_t now = time(NULL);
-  GList **timers = owl_global_get_timerlist(&g);
-
+static gboolean owl_timer_prepare(GSource *source, int *timeout) {
+  GList **timers = owl_global_get_timerlist(&g);
+  GTimeVal now;
+
+  /* TODO: In the far /far/ future, g_source_get_time is what the cool
+   * kids use to get system monotonic time. */
+  g_source_get_current_time(source, &now);
+
+  /* FIXME: bother with millisecond accuracy now that we can? */
+  if (*timers) {
+    owl_timer *t = (*timers)->data;
+    *timeout = t->time - now.tv_sec;
+    if (*timeout <= 0) {
+      *timeout = 0;
+      return TRUE;
+    }
+    if (*timeout > 60 * 1000)
+      *timeout = 60 * 1000;
+  } else {
+    *timeout = 60 * 1000;
+  }
+  return FALSE;
+}
+
+static gboolean owl_timer_check(GSource *source) {
+  GList **timers = owl_global_get_timerlist(&g);
+  GTimeVal now;
+
+  /* TODO: In the far /far/ future, g_source_get_time is what the cool
+   * kids use to get system monotonic time. */
+  g_source_get_current_time(source, &now);
+
+  /* FIXME: bother with millisecond accuracy now that we can? */
+  if (*timers) {
+    owl_timer *t = (*timers)->data;
+    return t->time >= now.tv_sec;
+  }
+  return FALSE;
+}
+
+
+static gboolean owl_timer_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
+  GList **timers = owl_global_get_timerlist(&g);
+  GTimeVal now;
+
+  /* TODO: In the far /far/ future, g_source_get_time is what the cool
+   * kids use to get system monotonic time. */
+  g_source_get_current_time(source, &now);
+
+  /* FIXME: bother with millisecond accuracy now that we can? */
   while(*timers) {
     owl_timer *t = (*timers)->data;
     int remove = 0;
 
-    if(t->time > now)
+    if(t->time > now.tv_sec)
       break;
 
     /* Reschedule if appropriate */
     if(t->interval > 0) {
-      t->time = now + t->interval;
+      t->time = now.tv_sec + t->interval;
       *timers = g_list_remove(*timers, t);
       *timers = g_list_insert_sorted(*timers, t,
@@ -72,18 +115,16 @@
     }
   }
-
-  if(*timers) {
-    owl_timer *t = (*timers)->data;
-    timeout->tv_sec = t->time - now;
-    if (timeout->tv_sec > 60)
-      timeout->tv_sec = 60;
-  } else {
-    timeout->tv_sec = 60;
-  }
-
-  timeout->tv_nsec = 0;
-}
-
-static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
+  return TRUE;
+}
+
+static GSourceFuncs owl_timer_funcs = {
+  owl_timer_prepare,
+  owl_timer_check,
+  owl_timer_dispatch,
+  NULL
+};
+
+/* Returns the valid owl_io_dispatch for a given file descriptor. */
+static owl_io_dispatch *owl_select_find_valid_io_dispatch_by_fd(const int fd)
 {
   int i, len;
@@ -94,5 +135,5 @@
   for(i = 0; i < len; i++) {
     d = owl_list_get_element(dl, i);
-    if (d->fd == fd) return d;
+    if (d->fd == fd && d->valid) return d;
   }
   return NULL;
@@ -113,4 +154,12 @@
   }
   return -1;
+}
+
+static void owl_select_invalidate_io_dispatch(owl_io_dispatch *d)
+{
+  if (d == NULL || !d->valid)
+    return;
+  d->valid = false;
+  g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
 }
 
@@ -126,4 +175,5 @@
         d->needs_gc = 1;
       else {
+	owl_select_invalidate_io_dispatch(d);
         owl_list_remove_element(dl, elt);
         if (d->destroy)
@@ -135,5 +185,5 @@
 }
 
-void owl_select_io_dispatch_gc(void)
+static void owl_select_io_dispatch_gc(void)
 {
   int i;
@@ -153,5 +203,5 @@
 }
 
-/* Each FD may have at most one dispatcher.
+/* Each FD may have at most one valid dispatcher.
  * If a new dispatch is added for an FD, the old one is removed.
  * mode determines what types of events are watched for, and may be any combination of:
@@ -162,6 +212,8 @@
   owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
   owl_list *dl = owl_global_get_io_dispatch_list(&g);
+  owl_io_dispatch *other;
 
   d->fd = fd;
+  d->valid = true;
   d->needs_gc = 0;
   d->mode = mode;
@@ -170,5 +222,19 @@
   d->data = data;
 
-  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
+  /* TODO: Allow changing fd and mode in the middle? Probably don't care... */
+  d->pollfd.fd = fd;
+  d->pollfd.events = 0;
+  if (d->mode & OWL_IO_READ)
+    d->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
+  if (d->mode & OWL_IO_WRITE)
+    d->pollfd.events |= G_IO_OUT | G_IO_ERR;
+  if (d->mode & OWL_IO_EXCEPT)
+    d->pollfd.events |= G_IO_PRI | G_IO_ERR;
+  g_source_add_poll(owl_io_dispatch_source, &d->pollfd);
+
+
+  other = owl_select_find_valid_io_dispatch_by_fd(fd);
+  if (other)
+    owl_select_invalidate_io_dispatch(other);
   owl_list_append_element(dl, d);
 
@@ -176,37 +242,39 @@
 }
 
-int owl_select_prepare_io_dispatch_fd_sets(fd_set *rfds, fd_set *wfds, fd_set *efds) {
-  int i, len, max_fd;
-  owl_io_dispatch *d;
-  owl_list *dl = owl_global_get_io_dispatch_list(&g);
-
-  max_fd = 0;
+static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
+  *timeout = -1;
+  return FALSE;
+}
+
+static gboolean owl_io_dispatch_check(GSource *source) {
+  int i, len;
+  const owl_list *dl;
+
+  dl = owl_global_get_io_dispatch_list(&g);
+  len = owl_list_get_size(dl);
+  for(i = 0; i < len; i++) {
+    owl_io_dispatch *d = owl_list_get_element(dl, i);
+    if (!d->valid) continue;
+    if (d->pollfd.revents & G_IO_NVAL) {
+      owl_function_debugmsg("Pruning defunct dispatch on fd %d.", d->fd);
+      owl_select_invalidate_io_dispatch(d);
+    }
+    if (d->pollfd.revents & d->pollfd.events)
+      return TRUE;
+  }
+  return FALSE;
+}
+
+static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
+  int i, len;
+  const owl_list *dl;
+
+  dispatch_active = 1;
+  dl = owl_global_get_io_dispatch_list(&g);
   len = owl_list_get_size(dl);
   for (i = 0; i < len; i++) {
-    d = owl_list_get_element(dl, i);
-    if (d->mode & (OWL_IO_READ | OWL_IO_WRITE | OWL_IO_EXCEPT)) {
-      if (max_fd < d->fd) max_fd = d->fd;
-      if (d->mode & OWL_IO_READ) FD_SET(d->fd, rfds);
-      if (d->mode & OWL_IO_WRITE) FD_SET(d->fd, wfds);
-      if (d->mode & OWL_IO_EXCEPT) FD_SET(d->fd, efds);
-    }
-  }
-  return max_fd + 1;
-}
-
-void owl_select_io_dispatch(const fd_set *rfds, const fd_set *wfds, const fd_set *efds, const int max_fd)
-{
-  int i, len;
-  owl_io_dispatch *d;
-  owl_list *dl = owl_global_get_io_dispatch_list(&g);
-
-  dispatch_active = 1;
-  len = owl_list_get_size(dl);
-  for (i = 0; i < len; i++) {
-    d = owl_list_get_element(dl, i);
-    if (d->fd < max_fd && d->callback != NULL &&
-        ((d->mode & OWL_IO_READ && FD_ISSET(d->fd, rfds)) ||
-         (d->mode & OWL_IO_WRITE && FD_ISSET(d->fd, wfds)) ||
-         (d->mode & OWL_IO_EXCEPT && FD_ISSET(d->fd, efds)))) {
+    owl_io_dispatch *d = owl_list_get_element(dl, i);
+    if (!d->valid) continue;
+    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
       d->callback(d, d->data);
     }
@@ -214,21 +282,48 @@
   dispatch_active = 0;
   owl_select_io_dispatch_gc();
-}
+
+  return TRUE;
+}
+
+static GSourceFuncs owl_io_dispatch_funcs = {
+  owl_io_dispatch_prepare,
+  owl_io_dispatch_check,
+  owl_io_dispatch_dispatch,
+  NULL
+};
 
 int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
 {
-  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
+  const owl_io_dispatch *d = owl_select_find_valid_io_dispatch_by_fd(fd);
   if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
     /* Don't mess with non-perl dispatch functions from here. */
     return 1;
   }
+  /* Also remove any invalidated perl dispatch functions that may have
+   * stuck around. */
+  owl_select_remove_perl_io_dispatch(fd);
   owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
   return 0;
 }
 
+static owl_io_dispatch *owl_select_find_perl_io_dispatch(int fd)
+{
+  int i, len;
+  const owl_list *dl;
+  owl_io_dispatch *d;
+  dl = owl_global_get_io_dispatch_list(&g);
+  len = owl_list_get_size(dl);
+  for(i = 0; i < len; i++) {
+    d = owl_list_get_element(dl, i);
+    if (d->fd == fd && d->callback == owl_perlconfig_io_dispatch)
+      return d;
+  }
+  return NULL;
+}
+
 int owl_select_remove_perl_io_dispatch(int fd)
 {
-  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
-  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
+  owl_io_dispatch *d = owl_select_find_perl_io_dispatch(fd);
+  if (d != NULL) {
     /* Only remove perl io dispatchers from here. */
     owl_select_remove_io_dispatch(d);
@@ -238,239 +333,60 @@
 }
 
-int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
-{
-  aim_conn_t *cur;
-  aim_session_t *sess;
-  int max_fd;
-
-  max_fd = 0;
-  sess = owl_global_get_aimsess(&g);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->fd != -1) {
-      FD_SET(cur->fd, rfds);
-      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
-        /* Yes, we're checking writable sockets here. Without it, AIM
-           login is really slow. */
-        FD_SET(cur->fd, wfds);
-      }
-      
-      if (cur->fd > max_fd)
-        max_fd = cur->fd;
-    }
-  }
-  return max_fd;
-}
-
-void owl_process_input_char(owl_input j)
-{
-  int ret;
-
-  owl_global_set_lastinputtime(&g, time(NULL));
-  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
-  if (ret!=0 && ret!=1) {
-    owl_function_makemsg("Unable to handle keypress");
-  }
-}
-
-void owl_select_mask_signals(sigset_t *oldmask) {
-  sigset_t set;
-
-  sigemptyset(&set);
-  sigaddset(&set, SIGWINCH);
-  sigaddset(&set, SIGALRM);
-  sigaddset(&set, SIGPIPE);
-  sigaddset(&set, SIGTERM);
-  sigaddset(&set, SIGHUP);
-  sigaddset(&set, SIGINT);
-  sigprocmask(SIG_BLOCK, &set, oldmask);
-}
-
-void owl_select_handle_intr(sigset_t *restore)
-{
-  owl_input in;
-
-  owl_global_unset_interrupted(&g);
-
-  sigprocmask(SIG_SETMASK, restore, NULL);
-
-  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
-  owl_process_input_char(in);
-}
-
-owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
-{
-  owl_ps_action *a = g_new(owl_ps_action, 1);
-  owl_list *psa_list = owl_global_get_psa_list(&g);
-  a->needs_gc = 0;
-  a->callback = cb;
-  a->destroy = destroy;
-  a->data = data;
-  owl_list_append_element(psa_list, a);
-  return a;
-}
-
-void owl_select_psa_gc(void)
-{
-  int i;
-  owl_list *psa_list;
-  owl_ps_action *a;
-
-  psa_list = owl_global_get_psa_list(&g);
-  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
-    a = owl_list_get_element(psa_list, i);
-    if (a->needs_gc) {
-      owl_list_remove_element(psa_list, i);
-      if (a->destroy) {
-        a->destroy(a);
-      }
-      g_free(a);
-    }
-  }
-}
-
-void owl_select_remove_pre_select_action(owl_ps_action *a)
-{
-  a->needs_gc = 1;
-  if (!psa_active)
-    owl_select_psa_gc();
-}
-
-int owl_select_do_pre_select_actions(void)
-{
-  int i, len, ret;
-  owl_list *psa_list;
-
-  psa_active = 1;
-  ret = 0;
-  psa_list = owl_global_get_psa_list(&g);
-  len = owl_list_get_size(psa_list);
-  for (i = 0; i < len; i++) {
-    owl_ps_action *a = owl_list_get_element(psa_list, i);
-    if (a->callback != NULL && a->callback(a, a->data)) {
-      ret = 1;
-    }
-  }
-  psa_active = 0;
-  owl_select_psa_gc();
-  return ret;
-}
-
-static void owl_select_prune_bad_fds(void) {
-  owl_list *dl = owl_global_get_io_dispatch_list(&g);
-  int len, i;
-  struct stat st;
-  owl_io_dispatch *d;
-
-  len = owl_list_get_size(dl);
-  for (i = 0; i < len; i++) {
-    d = owl_list_get_element(dl, i);
-    if (fstat(d->fd, &st) < 0 && errno == EBADF) {
-      owl_function_debugmsg("Pruning defunct dispatch on fd %d.", d->fd);
-      d->needs_gc = 1;
-    }
-  }
-  owl_select_io_dispatch_gc();
-}
-
-void owl_select(void)
-{
-  int i, max_fd, max_fd2, aim_done, ret;
-  fd_set r;
-  fd_set w;
-  fd_set e;
-  fd_set aim_rfds, aim_wfds;
-  struct timespec timeout;
-  sigset_t mask;
-
-  owl_select_process_timers(&timeout);
-
-  owl_select_mask_signals(&mask);
-
-  if(owl_global_is_interrupted(&g)) {
-    owl_select_handle_intr(&mask);
-    return;
-  }
-  FD_ZERO(&r);
-  FD_ZERO(&w);
-  FD_ZERO(&e);
-
-  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
-
-  /* AIM HACK: 
-   *
-   *  The problem - I'm not sure where to hook into the owl/faim
-   *  interface to keep track of when the AIM socket(s) open and
-   *  close. In particular, the bosconn thing throws me off. So,
-   *  rather than register particular dispatchers for AIM, I look up
-   *  the relevant FDs and add them to select's watch lists, then
-   *  check for them individually before moving on to the other
-   *  dispatchers. --asedeno
-   */
-  aim_done = 1;
-  FD_ZERO(&aim_rfds);
-  FD_ZERO(&aim_wfds);
-  if (owl_global_is_doaimevents(&g)) {
-    aim_done = 0;
-    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
-    if (max_fd < max_fd2) max_fd = max_fd2;
-    for(i = 0; i <= max_fd2; i++) {
-      if (FD_ISSET(i, &aim_rfds)) {
-        FD_SET(i, &r);
-        FD_SET(i, &e);
-      }
-      if (FD_ISSET(i, &aim_wfds)) {
-        FD_SET(i, &w);
-        FD_SET(i, &e);
-      }
-    }
-  }
-  /* END AIM HACK */
-
-  if (owl_select_do_pre_select_actions()) {
-    timeout.tv_sec = 0;
-    timeout.tv_nsec = 0;
-  }
-
-  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
-
-  if(ret < 0) {
-    if (errno == EINTR) {
-      if(owl_global_is_interrupted(&g)) {
-        owl_select_handle_intr(NULL);
-      }
-    } else if (errno == EBADF) {
-      /* Perl must have closed an fd on us without removing it first. */
-      owl_select_prune_bad_fds();
-    }
-    sigprocmask(SIG_SETMASK, &mask, NULL);
-    return;
-  }
-
-  sigprocmask(SIG_SETMASK, &mask, NULL);
-
-  if(ret > 0) {
-    /* AIM HACK: process all AIM events at once. */
-    for(i = 0; !aim_done && i <= max_fd; i++) {
-      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
-        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
-          owl_process_aim();
-          aim_done = 1;
-        }
-      }
-    }
-    owl_select_io_dispatch(&r, &w, &e, max_fd);
-  }
+void owl_select_init(void)
+{
+  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
+  g_source_attach(owl_timer_source, NULL);
+
+  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
+  g_source_attach(owl_io_dispatch_source, NULL);
 }
 
 void owl_select_run_loop(void)
 {
-  loop_active = 1;
-  while (loop_active) {
-    owl_select();
-  }
+  context = g_main_context_default();
+  loop = g_main_loop_new(context, FALSE);
+  g_main_loop_run(loop);
 }
 
 void owl_select_quit_loop(void)
 {
-  loop_active = 0;
-}
+  if (loop) {
+    g_main_loop_quit(loop);
+    loop = NULL;
+  }
+}
+
+typedef struct _owl_task { /*noproto*/
+  void (*cb)(void *);
+  void *cbdata;
+  void (*destroy_cbdata)(void *);
+} owl_task;
+
+static gboolean _run_task(gpointer data)
+{
+  owl_task *t = data;
+  if (t->cb)
+    t->cb(t->cbdata);
+  return FALSE;
+}
+
+static void _destroy_task(void *data)
+{
+  owl_task *t = data;
+  if (t->destroy_cbdata)
+    t->destroy_cbdata(t->cbdata);
+  g_free(t);
+}
+
+void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*))
+{
+  GSource *source = g_idle_source_new();
+  owl_task *t = g_new0(owl_task, 1);
+  t->cb = cb;
+  t->cbdata = cbdata;
+  t->destroy_cbdata = destroy_cbdata;
+  g_source_set_priority(source, G_PRIORITY_DEFAULT);
+  g_source_set_callback(source, _run_task, t, _destroy_task);
+  g_source_attach(source, context);
+  g_source_unref(source);
+}
