Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • select.c

    rfb96152 r33b6431b  
    11#include "owl.h"
    2 #include <sys/stat.h>
    3 
     2
     3static GMainLoop *loop = NULL;
     4static GMainContext *context;
    45static int dispatch_active = 0;
    5 static int psa_active = 0;
    6 static int loop_active = 0;
    7 
    8 int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
     6
     7static GSource *owl_timer_source;
     8static GSource *owl_io_dispatch_source;
     9
     10static int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
    911  return t1->time - t2->time;
    10 }
    11 
    12 int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
    13   return t1 == t2;
    1412}
    1513
     
    4442}
    4543
    46 void owl_select_process_timers(struct timespec *timeout)
    47 {
    48   time_t now = time(NULL);
    49   GList **timers = owl_global_get_timerlist(&g);
    50 
     44static gboolean owl_timer_prepare(GSource *source, int *timeout) {
     45  GList **timers = owl_global_get_timerlist(&g);
     46  GTimeVal now;
     47
     48  /* TODO: In the far /far/ future, g_source_get_time is what the cool
     49   * kids use to get system monotonic time. */
     50  g_source_get_current_time(source, &now);
     51
     52  /* FIXME: bother with millisecond accuracy now that we can? */
     53  if (*timers) {
     54    owl_timer *t = (*timers)->data;
     55    *timeout = t->time - now.tv_sec;
     56    if (*timeout <= 0) {
     57      *timeout = 0;
     58      return TRUE;
     59    }
     60    if (*timeout > 60 * 1000)
     61      *timeout = 60 * 1000;
     62  } else {
     63    *timeout = 60 * 1000;
     64  }
     65  return FALSE;
     66}
     67
     68static gboolean owl_timer_check(GSource *source) {
     69  GList **timers = owl_global_get_timerlist(&g);
     70  GTimeVal now;
     71
     72  /* TODO: In the far /far/ future, g_source_get_time is what the cool
     73   * kids use to get system monotonic time. */
     74  g_source_get_current_time(source, &now);
     75
     76  /* FIXME: bother with millisecond accuracy now that we can? */
     77  if (*timers) {
     78    owl_timer *t = (*timers)->data;
     79    return t->time >= now.tv_sec;
     80  }
     81  return FALSE;
     82}
     83
     84
     85static gboolean owl_timer_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
     86  GList **timers = owl_global_get_timerlist(&g);
     87  GTimeVal now;
     88
     89  /* TODO: In the far /far/ future, g_source_get_time is what the cool
     90   * kids use to get system monotonic time. */
     91  g_source_get_current_time(source, &now);
     92
     93  /* FIXME: bother with millisecond accuracy now that we can? */
    5194  while(*timers) {
    5295    owl_timer *t = (*timers)->data;
    5396    int remove = 0;
    5497
    55     if(t->time > now)
     98    if(t->time > now.tv_sec)
    5699      break;
    57100
    58101    /* Reschedule if appropriate */
    59102    if(t->interval > 0) {
    60       t->time = now + t->interval;
     103      t->time = now.tv_sec + t->interval;
    61104      *timers = g_list_remove(*timers, t);
    62105      *timers = g_list_insert_sorted(*timers, t,
     
    72115    }
    73116  }
    74 
    75   if(*timers) {
    76     owl_timer *t = (*timers)->data;
    77     timeout->tv_sec = t->time - now;
    78     if (timeout->tv_sec > 60)
    79       timeout->tv_sec = 60;
    80   } else {
    81     timeout->tv_sec = 60;
    82   }
    83 
    84   timeout->tv_nsec = 0;
    85 }
    86 
    87 static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
     117  return TRUE;
     118}
     119
     120static GSourceFuncs owl_timer_funcs = {
     121  owl_timer_prepare,
     122  owl_timer_check,
     123  owl_timer_dispatch,
     124  NULL
     125};
     126
     127/* Returns the valid owl_io_dispatch for a given file descriptor. */
     128static owl_io_dispatch *owl_select_find_valid_io_dispatch_by_fd(const int fd)
    88129{
    89130  int i, len;
     
    94135  for(i = 0; i < len; i++) {
    95136    d = owl_list_get_element(dl, i);
    96     if (d->fd == fd) return d;
     137    if (d->fd == fd && d->valid) return d;
    97138  }
    98139  return NULL;
     
    113154  }
    114155  return -1;
     156}
     157
     158static void owl_select_invalidate_io_dispatch(owl_io_dispatch *d)
     159{
     160  if (d == NULL || !d->valid)
     161    return;
     162  d->valid = false;
     163  g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
    115164}
    116165
     
    126175        d->needs_gc = 1;
    127176      else {
     177        owl_select_invalidate_io_dispatch(d);
    128178        owl_list_remove_element(dl, elt);
    129179        if (d->destroy)
     
    135185}
    136186
    137 void owl_select_io_dispatch_gc(void)
     187static void owl_select_io_dispatch_gc(void)
    138188{
    139189  int i;
     
    153203}
    154204
    155 /* Each FD may have at most one dispatcher.
     205/* Each FD may have at most one valid dispatcher.
    156206 * If a new dispatch is added for an FD, the old one is removed.
    157207 * mode determines what types of events are watched for, and may be any combination of:
     
    162212  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
    163213  owl_list *dl = owl_global_get_io_dispatch_list(&g);
     214  owl_io_dispatch *other;
    164215
    165216  d->fd = fd;
     217  d->valid = true;
    166218  d->needs_gc = 0;
    167219  d->mode = mode;
     
    170222  d->data = data;
    171223
    172   owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
     224  /* TODO: Allow changing fd and mode in the middle? Probably don't care... */
     225  d->pollfd.fd = fd;
     226  d->pollfd.events = 0;
     227  if (d->mode & OWL_IO_READ)
     228    d->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
     229  if (d->mode & OWL_IO_WRITE)
     230    d->pollfd.events |= G_IO_OUT | G_IO_ERR;
     231  if (d->mode & OWL_IO_EXCEPT)
     232    d->pollfd.events |= G_IO_PRI | G_IO_ERR;
     233  g_source_add_poll(owl_io_dispatch_source, &d->pollfd);
     234
     235
     236  other = owl_select_find_valid_io_dispatch_by_fd(fd);
     237  if (other)
     238    owl_select_invalidate_io_dispatch(other);
    173239  owl_list_append_element(dl, d);
    174240
     
    176242}
    177243
    178 int owl_select_prepare_io_dispatch_fd_sets(fd_set *rfds, fd_set *wfds, fd_set *efds) {
    179   int i, len, max_fd;
    180   owl_io_dispatch *d;
    181   owl_list *dl = owl_global_get_io_dispatch_list(&g);
    182 
    183   max_fd = 0;
     244static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
     245  *timeout = -1;
     246  return FALSE;
     247}
     248
     249static gboolean owl_io_dispatch_check(GSource *source) {
     250  int i, len;
     251  const owl_list *dl;
     252
     253  dl = owl_global_get_io_dispatch_list(&g);
     254  len = owl_list_get_size(dl);
     255  for(i = 0; i < len; i++) {
     256    owl_io_dispatch *d = owl_list_get_element(dl, i);
     257    if (!d->valid) continue;
     258    if (d->pollfd.revents & G_IO_NVAL) {
     259      owl_function_debugmsg("Pruning defunct dispatch on fd %d.", d->fd);
     260      owl_select_invalidate_io_dispatch(d);
     261    }
     262    if (d->pollfd.revents & d->pollfd.events)
     263      return TRUE;
     264  }
     265  return FALSE;
     266}
     267
     268static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
     269  int i, len;
     270  const owl_list *dl;
     271
     272  dispatch_active = 1;
     273  dl = owl_global_get_io_dispatch_list(&g);
    184274  len = owl_list_get_size(dl);
    185275  for (i = 0; i < len; i++) {
    186     d = owl_list_get_element(dl, i);
    187     if (d->mode & (OWL_IO_READ | OWL_IO_WRITE | OWL_IO_EXCEPT)) {
    188       if (max_fd < d->fd) max_fd = d->fd;
    189       if (d->mode & OWL_IO_READ) FD_SET(d->fd, rfds);
    190       if (d->mode & OWL_IO_WRITE) FD_SET(d->fd, wfds);
    191       if (d->mode & OWL_IO_EXCEPT) FD_SET(d->fd, efds);
    192     }
    193   }
    194   return max_fd + 1;
    195 }
    196 
    197 void owl_select_io_dispatch(const fd_set *rfds, const fd_set *wfds, const fd_set *efds, const int max_fd)
    198 {
    199   int i, len;
    200   owl_io_dispatch *d;
    201   owl_list *dl = owl_global_get_io_dispatch_list(&g);
    202 
    203   dispatch_active = 1;
    204   len = owl_list_get_size(dl);
    205   for (i = 0; i < len; i++) {
    206     d = owl_list_get_element(dl, i);
    207     if (d->fd < max_fd && d->callback != NULL &&
    208         ((d->mode & OWL_IO_READ && FD_ISSET(d->fd, rfds)) ||
    209          (d->mode & OWL_IO_WRITE && FD_ISSET(d->fd, wfds)) ||
    210          (d->mode & OWL_IO_EXCEPT && FD_ISSET(d->fd, efds)))) {
     276    owl_io_dispatch *d = owl_list_get_element(dl, i);
     277    if (!d->valid) continue;
     278    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
    211279      d->callback(d, d->data);
    212280    }
     
    214282  dispatch_active = 0;
    215283  owl_select_io_dispatch_gc();
    216 }
     284
     285  return TRUE;
     286}
     287
     288static GSourceFuncs owl_io_dispatch_funcs = {
     289  owl_io_dispatch_prepare,
     290  owl_io_dispatch_check,
     291  owl_io_dispatch_dispatch,
     292  NULL
     293};
    217294
    218295int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
    219296{
    220   const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
     297  const owl_io_dispatch *d = owl_select_find_valid_io_dispatch_by_fd(fd);
    221298  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
    222299    /* Don't mess with non-perl dispatch functions from here. */
    223300    return 1;
    224301  }
     302  /* Also remove any invalidated perl dispatch functions that may have
     303   * stuck around. */
     304  owl_select_remove_perl_io_dispatch(fd);
    225305  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
    226306  return 0;
    227307}
    228308
     309static owl_io_dispatch *owl_select_find_perl_io_dispatch(int fd)
     310{
     311  int i, len;
     312  const owl_list *dl;
     313  owl_io_dispatch *d;
     314  dl = owl_global_get_io_dispatch_list(&g);
     315  len = owl_list_get_size(dl);
     316  for(i = 0; i < len; i++) {
     317    d = owl_list_get_element(dl, i);
     318    if (d->fd == fd && d->callback == owl_perlconfig_io_dispatch)
     319      return d;
     320  }
     321  return NULL;
     322}
     323
    229324int owl_select_remove_perl_io_dispatch(int fd)
    230325{
    231   const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
    232   if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
     326  owl_io_dispatch *d = owl_select_find_perl_io_dispatch(fd);
     327  if (d != NULL) {
    233328    /* Only remove perl io dispatchers from here. */
    234329    owl_select_remove_io_dispatch(d);
     
    238333}
    239334
    240 int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
    241 {
    242   aim_conn_t *cur;
    243   aim_session_t *sess;
    244   int max_fd;
    245 
    246   max_fd = 0;
    247   sess = owl_global_get_aimsess(&g);
    248   for (cur = sess->connlist; cur; cur = cur->next) {
    249     if (cur->fd != -1) {
    250       FD_SET(cur->fd, rfds);
    251       if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
    252         /* Yes, we're checking writable sockets here. Without it, AIM
    253            login is really slow. */
    254         FD_SET(cur->fd, wfds);
    255       }
    256      
    257       if (cur->fd > max_fd)
    258         max_fd = cur->fd;
    259     }
    260   }
    261   return max_fd;
    262 }
    263 
    264 void owl_process_input_char(owl_input j)
    265 {
    266   int ret;
    267 
    268   owl_global_set_lastinputtime(&g, time(NULL));
    269   ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
    270   if (ret!=0 && ret!=1) {
    271     owl_function_makemsg("Unable to handle keypress");
    272   }
    273 }
    274 
    275 void owl_select_mask_signals(sigset_t *oldmask) {
    276   sigset_t set;
    277 
    278   sigemptyset(&set);
    279   sigaddset(&set, SIGWINCH);
    280   sigaddset(&set, SIGALRM);
    281   sigaddset(&set, SIGPIPE);
    282   sigaddset(&set, SIGTERM);
    283   sigaddset(&set, SIGHUP);
    284   sigaddset(&set, SIGINT);
    285   sigprocmask(SIG_BLOCK, &set, oldmask);
    286 }
    287 
    288 void owl_select_handle_intr(sigset_t *restore)
    289 {
    290   owl_input in;
    291 
    292   owl_global_unset_interrupted(&g);
    293 
    294   sigprocmask(SIG_SETMASK, restore, NULL);
    295 
    296   in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
    297   owl_process_input_char(in);
    298 }
    299 
    300 owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
    301 {
    302   owl_ps_action *a = g_new(owl_ps_action, 1);
    303   owl_list *psa_list = owl_global_get_psa_list(&g);
    304   a->needs_gc = 0;
    305   a->callback = cb;
    306   a->destroy = destroy;
    307   a->data = data;
    308   owl_list_append_element(psa_list, a);
    309   return a;
    310 }
    311 
    312 void owl_select_psa_gc(void)
    313 {
    314   int i;
    315   owl_list *psa_list;
    316   owl_ps_action *a;
    317 
    318   psa_list = owl_global_get_psa_list(&g);
    319   for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
    320     a = owl_list_get_element(psa_list, i);
    321     if (a->needs_gc) {
    322       owl_list_remove_element(psa_list, i);
    323       if (a->destroy) {
    324         a->destroy(a);
    325       }
    326       g_free(a);
    327     }
    328   }
    329 }
    330 
    331 void owl_select_remove_pre_select_action(owl_ps_action *a)
    332 {
    333   a->needs_gc = 1;
    334   if (!psa_active)
    335     owl_select_psa_gc();
    336 }
    337 
    338 int owl_select_do_pre_select_actions(void)
    339 {
    340   int i, len, ret;
    341   owl_list *psa_list;
    342 
    343   psa_active = 1;
    344   ret = 0;
    345   psa_list = owl_global_get_psa_list(&g);
    346   len = owl_list_get_size(psa_list);
    347   for (i = 0; i < len; i++) {
    348     owl_ps_action *a = owl_list_get_element(psa_list, i);
    349     if (a->callback != NULL && a->callback(a, a->data)) {
    350       ret = 1;
    351     }
    352   }
    353   psa_active = 0;
    354   owl_select_psa_gc();
    355   return ret;
    356 }
    357 
    358 static void owl_select_prune_bad_fds(void) {
    359   owl_list *dl = owl_global_get_io_dispatch_list(&g);
    360   int len, i;
    361   struct stat st;
    362   owl_io_dispatch *d;
    363 
    364   len = owl_list_get_size(dl);
    365   for (i = 0; i < len; i++) {
    366     d = owl_list_get_element(dl, i);
    367     if (fstat(d->fd, &st) < 0 && errno == EBADF) {
    368       owl_function_debugmsg("Pruning defunct dispatch on fd %d.", d->fd);
    369       d->needs_gc = 1;
    370     }
    371   }
    372   owl_select_io_dispatch_gc();
    373 }
    374 
    375 void owl_select(void)
    376 {
    377   int i, max_fd, max_fd2, aim_done, ret;
    378   fd_set r;
    379   fd_set w;
    380   fd_set e;
    381   fd_set aim_rfds, aim_wfds;
    382   struct timespec timeout;
    383   sigset_t mask;
    384 
    385   owl_select_process_timers(&timeout);
    386 
    387   owl_select_mask_signals(&mask);
    388 
    389   if(owl_global_is_interrupted(&g)) {
    390     owl_select_handle_intr(&mask);
    391     return;
    392   }
    393   FD_ZERO(&r);
    394   FD_ZERO(&w);
    395   FD_ZERO(&e);
    396 
    397   max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
    398 
    399   /* AIM HACK:
    400    *
    401    *  The problem - I'm not sure where to hook into the owl/faim
    402    *  interface to keep track of when the AIM socket(s) open and
    403    *  close. In particular, the bosconn thing throws me off. So,
    404    *  rather than register particular dispatchers for AIM, I look up
    405    *  the relevant FDs and add them to select's watch lists, then
    406    *  check for them individually before moving on to the other
    407    *  dispatchers. --asedeno
    408    */
    409   aim_done = 1;
    410   FD_ZERO(&aim_rfds);
    411   FD_ZERO(&aim_wfds);
    412   if (owl_global_is_doaimevents(&g)) {
    413     aim_done = 0;
    414     max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
    415     if (max_fd < max_fd2) max_fd = max_fd2;
    416     for(i = 0; i <= max_fd2; i++) {
    417       if (FD_ISSET(i, &aim_rfds)) {
    418         FD_SET(i, &r);
    419         FD_SET(i, &e);
    420       }
    421       if (FD_ISSET(i, &aim_wfds)) {
    422         FD_SET(i, &w);
    423         FD_SET(i, &e);
    424       }
    425     }
    426   }
    427   /* END AIM HACK */
    428 
    429   if (owl_select_do_pre_select_actions()) {
    430     timeout.tv_sec = 0;
    431     timeout.tv_nsec = 0;
    432   }
    433 
    434   ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
    435 
    436   if(ret < 0) {
    437     if (errno == EINTR) {
    438       if(owl_global_is_interrupted(&g)) {
    439         owl_select_handle_intr(NULL);
    440       }
    441     } else if (errno == EBADF) {
    442       /* Perl must have closed an fd on us without removing it first. */
    443       owl_select_prune_bad_fds();
    444     }
    445     sigprocmask(SIG_SETMASK, &mask, NULL);
    446     return;
    447   }
    448 
    449   sigprocmask(SIG_SETMASK, &mask, NULL);
    450 
    451   if(ret > 0) {
    452     /* AIM HACK: process all AIM events at once. */
    453     for(i = 0; !aim_done && i <= max_fd; i++) {
    454       if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
    455         if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
    456           owl_process_aim();
    457           aim_done = 1;
    458         }
    459       }
    460     }
    461     owl_select_io_dispatch(&r, &w, &e, max_fd);
    462   }
     335void owl_select_init(void)
     336{
     337  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
     338  g_source_attach(owl_timer_source, NULL);
     339
     340  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
     341  g_source_attach(owl_io_dispatch_source, NULL);
    463342}
    464343
    465344void owl_select_run_loop(void)
    466345{
    467   loop_active = 1;
    468   while (loop_active) {
    469     owl_select();
    470   }
     346  context = g_main_context_default();
     347  loop = g_main_loop_new(context, FALSE);
     348  g_main_loop_run(loop);
    471349}
    472350
    473351void owl_select_quit_loop(void)
    474352{
    475   loop_active = 0;
    476 }
     353  if (loop) {
     354    g_main_loop_quit(loop);
     355    loop = NULL;
     356  }
     357}
     358
     359typedef struct _owl_task { /*noproto*/
     360  void (*cb)(void *);
     361  void *cbdata;
     362  void (*destroy_cbdata)(void *);
     363} owl_task;
     364
     365static gboolean _run_task(gpointer data)
     366{
     367  owl_task *t = data;
     368  if (t->cb)
     369    t->cb(t->cbdata);
     370  return FALSE;
     371}
     372
     373static void _destroy_task(void *data)
     374{
     375  owl_task *t = data;
     376  if (t->destroy_cbdata)
     377    t->destroy_cbdata(t->cbdata);
     378  g_free(t);
     379}
     380
     381void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*))
     382{
     383  GSource *source = g_idle_source_new();
     384  owl_task *t = g_new0(owl_task, 1);
     385  t->cb = cb;
     386  t->cbdata = cbdata;
     387  t->destroy_cbdata = destroy_cbdata;
     388  g_source_set_priority(source, G_PRIORITY_DEFAULT);
     389  g_source_set_callback(source, _run_task, t, _destroy_task);
     390  g_source_attach(source, context);
     391  g_source_unref(source);
     392}
Note: See TracChangeset for help on using the changeset viewer.