Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • select.c

    r33b6431b rfb96152  
    11#include "owl.h"
    2 
    3 static GMainLoop *loop = NULL;
    4 static GMainContext *context;
     2#include <sys/stat.h>
     3
    54static int dispatch_active = 0;
    6 
    7 static GSource *owl_timer_source;
    8 static GSource *owl_io_dispatch_source;
    9 
    10 static int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
     5static int psa_active = 0;
     6static int loop_active = 0;
     7
     8int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
    119  return t1->time - t2->time;
     10}
     11
     12int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
     13  return t1 == t2;
    1214}
    1315
     
    4244}
    4345
    44 static gboolean owl_timer_prepare(GSource *source, int *timeout) {
     46void owl_select_process_timers(struct timespec *timeout)
     47{
     48  time_t now = time(NULL);
    4549  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 
    68 static 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 
    85 static 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? */
     50
    9451  while(*timers) {
    9552    owl_timer *t = (*timers)->data;
    9653    int remove = 0;
    9754
    98     if(t->time > now.tv_sec)
     55    if(t->time > now)
    9956      break;
    10057
    10158    /* Reschedule if appropriate */
    10259    if(t->interval > 0) {
    103       t->time = now.tv_sec + t->interval;
     60      t->time = now + t->interval;
    10461      *timers = g_list_remove(*timers, t);
    10562      *timers = g_list_insert_sorted(*timers, t,
     
    11572    }
    11673  }
    117   return TRUE;
    118 }
    119 
    120 static 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. */
    128 static owl_io_dispatch *owl_select_find_valid_io_dispatch_by_fd(const int fd)
     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
     87static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
    12988{
    13089  int i, len;
     
    13594  for(i = 0; i < len; i++) {
    13695    d = owl_list_get_element(dl, i);
    137     if (d->fd == fd && d->valid) return d;
     96    if (d->fd == fd) return d;
    13897  }
    13998  return NULL;
     
    154113  }
    155114  return -1;
    156 }
    157 
    158 static 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);
    164115}
    165116
     
    175126        d->needs_gc = 1;
    176127      else {
    177         owl_select_invalidate_io_dispatch(d);
    178128        owl_list_remove_element(dl, elt);
    179129        if (d->destroy)
     
    185135}
    186136
    187 static void owl_select_io_dispatch_gc(void)
     137void owl_select_io_dispatch_gc(void)
    188138{
    189139  int i;
     
    203153}
    204154
    205 /* Each FD may have at most one valid dispatcher.
     155/* Each FD may have at most one dispatcher.
    206156 * If a new dispatch is added for an FD, the old one is removed.
    207157 * mode determines what types of events are watched for, and may be any combination of:
     
    212162  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
    213163  owl_list *dl = owl_global_get_io_dispatch_list(&g);
    214   owl_io_dispatch *other;
    215164
    216165  d->fd = fd;
    217   d->valid = true;
    218166  d->needs_gc = 0;
    219167  d->mode = mode;
     
    222170  d->data = data;
    223171
    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);
     172  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
    239173  owl_list_append_element(dl, d);
    240174
     
    242176}
    243177
    244 static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
    245   *timeout = -1;
    246   return FALSE;
    247 }
    248 
    249 static 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 
    268 static 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);
     178int 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;
    274184  len = owl_list_get_size(dl);
    275185  for (i = 0; i < len; i++) {
    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) {
     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
     197void 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)))) {
    279211      d->callback(d, d->data);
    280212    }
     
    282214  dispatch_active = 0;
    283215  owl_select_io_dispatch_gc();
    284 
    285   return TRUE;
    286 }
    287 
    288 static GSourceFuncs owl_io_dispatch_funcs = {
    289   owl_io_dispatch_prepare,
    290   owl_io_dispatch_check,
    291   owl_io_dispatch_dispatch,
    292   NULL
    293 };
     216}
    294217
    295218int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
    296219{
    297   const owl_io_dispatch *d = owl_select_find_valid_io_dispatch_by_fd(fd);
     220  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
    298221  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
    299222    /* Don't mess with non-perl dispatch functions from here. */
    300223    return 1;
    301224  }
    302   /* Also remove any invalidated perl dispatch functions that may have
    303    * stuck around. */
    304   owl_select_remove_perl_io_dispatch(fd);
    305225  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
    306226  return 0;
    307227}
    308228
    309 static 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 
    324229int owl_select_remove_perl_io_dispatch(int fd)
    325230{
    326   owl_io_dispatch *d = owl_select_find_perl_io_dispatch(fd);
    327   if (d != NULL) {
     231  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
     232  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
    328233    /* Only remove perl io dispatchers from here. */
    329234    owl_select_remove_io_dispatch(d);
     
    333238}
    334239
    335 void 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);
     240int 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
     264void 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
     275void 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
     288void 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
     300owl_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
     312void 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
     331void 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
     338int 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
     358static 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
     375void 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  }
    342463}
    343464
    344465void owl_select_run_loop(void)
    345466{
    346   context = g_main_context_default();
    347   loop = g_main_loop_new(context, FALSE);
    348   g_main_loop_run(loop);
     467  loop_active = 1;
     468  while (loop_active) {
     469    owl_select();
     470  }
    349471}
    350472
    351473void owl_select_quit_loop(void)
    352474{
    353   if (loop) {
    354     g_main_loop_quit(loop);
    355     loop = NULL;
    356   }
    357 }
    358 
    359 typedef struct _owl_task { /*noproto*/
    360   void (*cb)(void *);
    361   void *cbdata;
    362   void (*destroy_cbdata)(void *);
    363 } owl_task;
    364 
    365 static 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 
    373 static 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 
    381 void 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 }
     475  loop_active = 0;
     476}
Note: See TracChangeset for help on using the changeset viewer.