Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • select.c

    rf0781ba rd4927a7  
    11#include "owl.h"
    22
    3 static GMainLoop *loop = NULL;
    4 static GMainContext *main_context;
    53static 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) {
     4static int psa_active = 0;
     5static int loop_active = 0;
     6
     7int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
    118  return t1->time - t2->time;
     9}
     10
     11int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
     12  return t1 == t2;
    1213}
    1314
     
    4243}
    4344
    44 static gboolean owl_timer_prepare(GSource *source, int *timeout) {
     45void owl_select_process_timers(struct timespec *timeout)
     46{
     47  time_t now = time(NULL);
    4548  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? */
     49
    9450  while(*timers) {
    9551    owl_timer *t = (*timers)->data;
    9652    int remove = 0;
    9753
    98     if(t->time > now.tv_sec)
     54    if(t->time > now)
    9955      break;
    10056
    10157    /* Reschedule if appropriate */
    10258    if(t->interval > 0) {
    103       t->time = now.tv_sec + t->interval;
     59      t->time = now + t->interval;
    10460      *timers = g_list_remove(*timers, t);
    10561      *timers = g_list_insert_sorted(*timers, t,
     
    11571    }
    11672  }
    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)
     73
     74  if(*timers) {
     75    owl_timer *t = (*timers)->data;
     76    timeout->tv_sec = t->time - now;
     77    if (timeout->tv_sec > 60)
     78      timeout->tv_sec = 60;
     79  } else {
     80    timeout->tv_sec = 60;
     81  }
     82
     83  timeout->tv_nsec = 0;
     84}
     85
     86static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
    12987{
    13088  int i, len;
     
    13593  for(i = 0; i < len; i++) {
    13694    d = owl_list_get_element(dl, i);
    137     if (d->fd == fd && d->valid) return d;
     95    if (d->fd == fd) return d;
    13896  }
    13997  return NULL;
     
    154112  }
    155113  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);
    164114}
    165115
     
    175125        d->needs_gc = 1;
    176126      else {
    177         owl_select_invalidate_io_dispatch(d);
    178127        owl_list_remove_element(dl, elt);
    179128        if (d->destroy)
     
    185134}
    186135
    187 static void owl_select_io_dispatch_gc(void)
     136void owl_select_io_dispatch_gc(void)
    188137{
    189138  int i;
     
    203152}
    204153
    205 /* Each FD may have at most one valid dispatcher.
     154/* Each FD may have at most one dispatcher.
    206155 * If a new dispatch is added for an FD, the old one is removed.
    207156 * mode determines what types of events are watched for, and may be any combination of:
     
    212161  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
    213162  owl_list *dl = owl_global_get_io_dispatch_list(&g);
    214   owl_io_dispatch *other;
    215163
    216164  d->fd = fd;
    217   d->valid = true;
    218165  d->needs_gc = 0;
    219166  d->mode = mode;
     
    222169  d->data = data;
    223170
    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);
     171  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
    239172  owl_list_append_element(dl, d);
    240173
     
    242175}
    243176
    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);
     177int owl_select_prepare_io_dispatch_fd_sets(fd_set *rfds, fd_set *wfds, fd_set *efds) {
     178  int i, len, max_fd;
     179  owl_io_dispatch *d;
     180  owl_list *dl = owl_global_get_io_dispatch_list(&g);
     181
     182  max_fd = 0;
    274183  len = owl_list_get_size(dl);
    275184  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) {
     185    d = owl_list_get_element(dl, i);
     186    if (d->mode & (OWL_IO_READ | OWL_IO_WRITE | OWL_IO_EXCEPT)) {
     187      if (max_fd < d->fd) max_fd = d->fd;
     188      if (d->mode & OWL_IO_READ) FD_SET(d->fd, rfds);
     189      if (d->mode & OWL_IO_WRITE) FD_SET(d->fd, wfds);
     190      if (d->mode & OWL_IO_EXCEPT) FD_SET(d->fd, efds);
     191    }
     192  }
     193  return max_fd + 1;
     194}
     195
     196void owl_select_io_dispatch(const fd_set *rfds, const fd_set *wfds, const fd_set *efds, const int max_fd)
     197{
     198  int i, len;
     199  owl_io_dispatch *d;
     200  owl_list *dl = owl_global_get_io_dispatch_list(&g);
     201
     202  dispatch_active = 1;
     203  len = owl_list_get_size(dl);
     204  for (i = 0; i < len; i++) {
     205    d = owl_list_get_element(dl, i);
     206    if (d->fd < max_fd && d->callback != NULL &&
     207        ((d->mode & OWL_IO_READ && FD_ISSET(d->fd, rfds)) ||
     208         (d->mode & OWL_IO_WRITE && FD_ISSET(d->fd, wfds)) ||
     209         (d->mode & OWL_IO_EXCEPT && FD_ISSET(d->fd, efds)))) {
    279210      d->callback(d, d->data);
    280211    }
     
    282213  dispatch_active = 0;
    283214  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 };
     215}
    294216
    295217int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
    296218{
    297   const owl_io_dispatch *d = owl_select_find_valid_io_dispatch_by_fd(fd);
     219  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
    298220  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
    299221    /* Don't mess with non-perl dispatch functions from here. */
    300222    return 1;
    301223  }
    302   /* Also remove any invalidated perl dispatch functions that may have
    303    * stuck around. */
    304   owl_select_remove_perl_io_dispatch(fd);
    305224  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
    306225  return 0;
    307226}
    308227
    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 
    324228int owl_select_remove_perl_io_dispatch(int fd)
    325229{
    326   owl_io_dispatch *d = owl_select_find_perl_io_dispatch(fd);
    327   if (d != NULL) {
     230  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
     231  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
    328232    /* Only remove perl io dispatchers from here. */
    329233    owl_select_remove_io_dispatch(d);
     
    333237}
    334238
    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);
     239int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
     240{
     241  aim_conn_t *cur;
     242  aim_session_t *sess;
     243  int max_fd;
     244
     245  max_fd = 0;
     246  sess = owl_global_get_aimsess(&g);
     247  for (cur = sess->connlist; cur; cur = cur->next) {
     248    if (cur->fd != -1) {
     249      FD_SET(cur->fd, rfds);
     250      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
     251        /* Yes, we're checking writable sockets here. Without it, AIM
     252           login is really slow. */
     253        FD_SET(cur->fd, wfds);
     254      }
     255     
     256      if (cur->fd > max_fd)
     257        max_fd = cur->fd;
     258    }
     259  }
     260  return max_fd;
     261}
     262
     263void owl_process_input_char(owl_input j)
     264{
     265  int ret;
     266
     267  owl_global_set_lastinputtime(&g, time(NULL));
     268  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
     269  if (ret!=0 && ret!=1) {
     270    owl_function_makemsg("Unable to handle keypress");
     271  }
     272}
     273
     274void owl_select_mask_signals(sigset_t *oldmask) {
     275  sigset_t set;
     276
     277  sigemptyset(&set);
     278  sigaddset(&set, SIGWINCH);
     279  sigaddset(&set, SIGALRM);
     280  sigaddset(&set, SIGPIPE);
     281  sigaddset(&set, SIGTERM);
     282  sigaddset(&set, SIGHUP);
     283  sigaddset(&set, SIGINT);
     284  sigprocmask(SIG_BLOCK, &set, oldmask);
     285}
     286
     287void owl_select_handle_intr(sigset_t *restore)
     288{
     289  owl_input in;
     290
     291  owl_global_unset_interrupted(&g);
     292
     293  sigprocmask(SIG_SETMASK, restore, NULL);
     294
     295  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
     296  owl_process_input_char(in);
     297}
     298
     299owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
     300{
     301  owl_ps_action *a = g_new(owl_ps_action, 1);
     302  owl_list *psa_list = owl_global_get_psa_list(&g);
     303  a->needs_gc = 0;
     304  a->callback = cb;
     305  a->destroy = destroy;
     306  a->data = data;
     307  owl_list_append_element(psa_list, a);
     308  return a;
     309}
     310
     311void owl_select_psa_gc(void)
     312{
     313  int i;
     314  owl_list *psa_list;
     315  owl_ps_action *a;
     316
     317  psa_list = owl_global_get_psa_list(&g);
     318  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
     319    a = owl_list_get_element(psa_list, i);
     320    if (a->needs_gc) {
     321      owl_list_remove_element(psa_list, i);
     322      if (a->destroy) {
     323        a->destroy(a);
     324      }
     325      g_free(a);
     326    }
     327  }
     328}
     329
     330void owl_select_remove_pre_select_action(owl_ps_action *a)
     331{
     332  a->needs_gc = 1;
     333  if (!psa_active)
     334    owl_select_psa_gc();
     335}
     336
     337int owl_select_do_pre_select_actions(void)
     338{
     339  int i, len, ret;
     340  owl_list *psa_list;
     341
     342  psa_active = 1;
     343  ret = 0;
     344  psa_list = owl_global_get_psa_list(&g);
     345  len = owl_list_get_size(psa_list);
     346  for (i = 0; i < len; i++) {
     347    owl_ps_action *a = owl_list_get_element(psa_list, i);
     348    if (a->callback != NULL && a->callback(a, a->data)) {
     349      ret = 1;
     350    }
     351  }
     352  psa_active = 0;
     353  owl_select_psa_gc();
     354  return ret;
     355}
     356
     357void owl_select(void)
     358{
     359  int i, max_fd, max_fd2, aim_done, ret;
     360  fd_set r;
     361  fd_set w;
     362  fd_set e;
     363  fd_set aim_rfds, aim_wfds;
     364  struct timespec timeout;
     365  sigset_t mask;
     366
     367  owl_select_process_timers(&timeout);
     368
     369  owl_select_mask_signals(&mask);
     370
     371  if(owl_global_is_interrupted(&g)) {
     372    owl_select_handle_intr(&mask);
     373    return;
     374  }
     375  FD_ZERO(&r);
     376  FD_ZERO(&w);
     377  FD_ZERO(&e);
     378
     379  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
     380
     381  /* AIM HACK:
     382   *
     383   *  The problem - I'm not sure where to hook into the owl/faim
     384   *  interface to keep track of when the AIM socket(s) open and
     385   *  close. In particular, the bosconn thing throws me off. So,
     386   *  rather than register particular dispatchers for AIM, I look up
     387   *  the relevant FDs and add them to select's watch lists, then
     388   *  check for them individually before moving on to the other
     389   *  dispatchers. --asedeno
     390   */
     391  aim_done = 1;
     392  FD_ZERO(&aim_rfds);
     393  FD_ZERO(&aim_wfds);
     394  if (owl_global_is_doaimevents(&g)) {
     395    aim_done = 0;
     396    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
     397    if (max_fd < max_fd2) max_fd = max_fd2;
     398    for(i = 0; i <= max_fd2; i++) {
     399      if (FD_ISSET(i, &aim_rfds)) {
     400        FD_SET(i, &r);
     401        FD_SET(i, &e);
     402      }
     403      if (FD_ISSET(i, &aim_wfds)) {
     404        FD_SET(i, &w);
     405        FD_SET(i, &e);
     406      }
     407    }
     408  }
     409  /* END AIM HACK */
     410
     411  if (owl_select_do_pre_select_actions()) {
     412    timeout.tv_sec = 0;
     413    timeout.tv_nsec = 0;
     414  }
     415
     416  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
     417
     418  if(ret < 0 && errno == EINTR) {
     419    if(owl_global_is_interrupted(&g)) {
     420      owl_select_handle_intr(NULL);
     421    }
     422    sigprocmask(SIG_SETMASK, &mask, NULL);
     423    return;
     424  }
     425
     426  sigprocmask(SIG_SETMASK, &mask, NULL);
     427
     428  if(ret > 0) {
     429    /* AIM HACK: process all AIM events at once. */
     430    for(i = 0; !aim_done && i <= max_fd; i++) {
     431      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
     432        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
     433          owl_process_aim();
     434          aim_done = 1;
     435        }
     436      }
     437    }
     438    owl_select_io_dispatch(&r, &w, &e, max_fd);
     439  }
    342440}
    343441
    344442void owl_select_run_loop(void)
    345443{
    346   main_context = g_main_context_default();
    347   loop = g_main_loop_new(main_context, FALSE);
    348   g_main_loop_run(loop);
     444  loop_active = 1;
     445  while (loop_active) {
     446    owl_select();
     447  }
    349448}
    350449
    351450void owl_select_quit_loop(void)
    352451{
    353   if (loop) {
    354     g_main_loop_quit(loop);
    355     g_main_loop_unref(loop);
    356     loop = NULL;
    357   }
    358 }
    359 
    360 typedef struct _owl_task { /*noproto*/
    361   void (*cb)(void *);
    362   void *cbdata;
    363   void (*destroy_cbdata)(void *);
    364 } owl_task;
    365 
    366 static gboolean _run_task(gpointer data)
    367 {
    368   owl_task *t = data;
    369   if (t->cb)
    370     t->cb(t->cbdata);
    371   return FALSE;
    372 }
    373 
    374 static void _destroy_task(void *data)
    375 {
    376   owl_task *t = data;
    377   if (t->destroy_cbdata)
    378     t->destroy_cbdata(t->cbdata);
    379   g_free(t);
    380 }
    381 
    382 void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*), GMainContext *context)
    383 {
    384   GSource *source = g_idle_source_new();
    385   owl_task *t = g_new0(owl_task, 1);
    386   t->cb = cb;
    387   t->cbdata = cbdata;
    388   t->destroy_cbdata = destroy_cbdata;
    389   g_source_set_priority(source, G_PRIORITY_DEFAULT);
    390   g_source_set_callback(source, _run_task, t, _destroy_task);
    391   g_source_attach(source, context);
    392   g_source_unref(source);
    393 }
     452  loop_active = 0;
     453}
Note: See TracChangeset for help on using the changeset viewer.