source: select.c @ 6b4033f

release-1.10release-1.8release-1.9
Last change on this file since 6b4033f was 2c79eae, checked in by David Benjamin <davidben@mit.edu>, 13 years ago
Start of GMainContext code Add GSources to feed owl_timer and owl_io_dispatch events into the main loop. Also add a hack so pre-select actions run at all. Glib's main loop has a hard priority system, so it prefers that prepare/check hooks return if the event would actually do anything. We probably want to replace every pre-select action with a dedicated GSource (or maybe a bunch of g_idle_add calls). Signals are also racy right now; glib uses select/poll instead of pselect/ppoll, so you can't actually listen for signals on the same thread. (In fact, the single-threaded version of g_child_watch_add doesn't actually work.) That's okay as what we were doing doesn't work when you add a second thread and thread safety is sort of the point of this work. The AIM hack need also be restored. Keep owl_select around for now as reference.
  • Property mode set to 100644
File size: 12.9 KB
RevLine 
[9c7a701]1#include "owl.h"
2
[2c79eae]3static GMainLoop *loop = NULL;
[1895c29]4static int dispatch_active = 0;
[4f2166b]5static int psa_active = 0;
[2c79eae]6
7static GSource *owl_timer_source;
8static GSource *owl_io_dispatch_source;
[1895c29]9
[ebb8498]10static int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
[ca1fc33a]11  return t1->time - t2->time;
[b7bb454]12}
13
[c6adf17]14owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
[b7bb454]15{
[96828e4]16  owl_timer *t = g_new(owl_timer, 1);
[58d1f8a]17  GList **timers = owl_global_get_timerlist(&g);
[b7bb454]18
[ca1fc33a]19  t->time = time(NULL) + after;
20  t->interval = interval;
21  t->callback = cb;
[c675b39]22  t->destroy = destroy;
[ca1fc33a]23  t->data = data;
[d4927a7]24  t->name = name ? g_strdup(name) : NULL;
[b7bb454]25
[58d1f8a]26  *timers = g_list_insert_sorted(*timers, t,
27                                 (GCompareFunc)_owl_select_timer_cmp);
[ca1fc33a]28  return t;
[b7bb454]29}
30
31void owl_select_remove_timer(owl_timer *t)
32{
[58d1f8a]33  GList **timers = owl_global_get_timerlist(&g);
34  if (t && g_list_find(*timers, t)) {
35    *timers = g_list_remove(*timers, t);
[c675b39]36    if(t->destroy) {
37      t->destroy(t);
38    }
[ddbbcffa]39    g_free(t->name);
40    g_free(t);
[ca1fc33a]41  }
[b7bb454]42}
43
[2c79eae]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) {
[58d1f8a]69  GList **timers = owl_global_get_timerlist(&g);
[2c79eae]70  GTimeVal now;
[b7bb454]71
[2c79eae]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? */
[58d1f8a]94  while(*timers) {
95    owl_timer *t = (*timers)->data;
[c675b39]96    int remove = 0;
[b7bb454]97
[2c79eae]98    if(t->time > now.tv_sec)
[ca1fc33a]99      break;
[b7bb454]100
[ca1fc33a]101    /* Reschedule if appropriate */
102    if(t->interval > 0) {
[2c79eae]103      t->time = now.tv_sec + t->interval;
[58d1f8a]104      *timers = g_list_remove(*timers, t);
105      *timers = g_list_insert_sorted(*timers, t,
106                                     (GCompareFunc)_owl_select_timer_cmp);
[ca1fc33a]107    } else {
[c675b39]108      remove = 1;
[ca1fc33a]109    }
[b7bb454]110
[ca1fc33a]111    /* Do the callback */
[c675b39]112    t->callback(t, t->data);
113    if(remove) {
114      owl_select_remove_timer(t);
115    }
[ca1fc33a]116  }
[2c79eae]117  return TRUE;
118}
[b7bb454]119
[2c79eae]120static GSourceFuncs owl_timer_funcs = {
121  owl_timer_prepare,
122  owl_timer_check,
123  owl_timer_dispatch,
124  NULL
125};
[b7bb454]126
127
[df0138f]128static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
129{
130  int i, len;
131  const owl_list *dl;
132  owl_io_dispatch *d;
133  dl = owl_global_get_io_dispatch_list(&g);
134  len = owl_list_get_size(dl);
135  for(i = 0; i < len; i++) {
136    d = owl_list_get_element(dl, i);
137    if (d->fd == fd) return d;
138  }
139  return NULL;
140}
141
142static int owl_select_find_io_dispatch(const owl_io_dispatch *in)
143{
144  int i, len;
145  const owl_list *dl;
146
147  if (in != NULL) {
148    dl = owl_global_get_io_dispatch_list(&g);
149    len = owl_list_get_size(dl);
150    for(i = 0; i < len; i++) {
151      const owl_io_dispatch *d = owl_list_get_element(dl, i);
152      if (d == in) return i;
153    }
154  }
155  return -1;
156}
157
158void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
159{
160  int elt;
161  if (in != NULL) {
162    elt = owl_select_find_io_dispatch(in);
163    if (elt != -1) {
164      owl_list *dl = owl_global_get_io_dispatch_list(&g);
165      owl_io_dispatch *d = owl_list_get_element(dl, elt);
166      if (dispatch_active)
167        d->needs_gc = 1;
168      else {
169        owl_list_remove_element(dl, elt);
170        if (d->destroy)
171          d->destroy(d);
[2c79eae]172        g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
[ddbbcffa]173        g_free(d);
[df0138f]174      }
175    }
176  }
177}
178
[ebb8498]179static void owl_select_io_dispatch_gc(void)
[df0138f]180{
181  int i;
182  owl_list *dl;
183
184  dl = owl_global_get_io_dispatch_list(&g);
185  /*
186   * Count down so we aren't set off by removing items from the list
187   * during the iteration.
188   */
189  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
190    owl_io_dispatch *d = owl_list_get_element(dl, i);
191    if(d->needs_gc) {
192      owl_select_remove_io_dispatch(d);
193    }
194  }
195}
196
197/* Each FD may have at most one dispatcher.
198 * If a new dispatch is added for an FD, the old one is removed.
199 * mode determines what types of events are watched for, and may be any combination of:
200 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
201 */
202const owl_io_dispatch *owl_select_add_io_dispatch(int fd, int mode, void (*cb)(const owl_io_dispatch *, void *), void (*destroy)(const owl_io_dispatch *), void *data)
203{
[96828e4]204  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
[df0138f]205  owl_list *dl = owl_global_get_io_dispatch_list(&g);
206
207  d->fd = fd;
208  d->needs_gc = 0;
209  d->mode = mode;
210  d->callback = cb;
211  d->destroy = destroy;
212  d->data = data;
213
[2c79eae]214  /* TODO: Allow changing fd and mode in the middle? Probably don't care... */
215  d->pollfd.fd = fd;
216  d->pollfd.events = 0;
217  if (d->mode & OWL_IO_READ)
218    d->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
219  if (d->mode & OWL_IO_WRITE)
220    d->pollfd.events |= G_IO_OUT | G_IO_ERR;
221  if (d->mode & OWL_IO_EXCEPT)
222    d->pollfd.events |= G_IO_PRI | G_IO_ERR;
223  g_source_add_poll(owl_io_dispatch_source, &d->pollfd);
224
225
[df0138f]226  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
227  owl_list_append_element(dl, d);
228
229  return d;
230}
231
[2c79eae]232static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
233  owl_select_do_pre_select_actions(); /* HACK */
234  *timeout = -1;
235  return FALSE;
236}
[df0138f]237
[2c79eae]238static gboolean owl_io_dispatch_check(GSource *source) {
239  int i, len;
240  const owl_list *dl;
241
242  dl = owl_global_get_io_dispatch_list(&g);
[df0138f]243  len = owl_list_get_size(dl);
[2c79eae]244  for(i = 0; i < len; i++) {
245    const owl_io_dispatch *d = owl_list_get_element(dl, i);
246    if (d->pollfd.revents & d->pollfd.events)
247      return TRUE;
[df0138f]248  }
[2c79eae]249  return FALSE;
[df0138f]250}
251
[2c79eae]252static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
[df0138f]253  int i, len;
[2c79eae]254  const owl_list *dl;
[df0138f]255
256  dispatch_active = 1;
[2c79eae]257  dl = owl_global_get_io_dispatch_list(&g);
[df0138f]258  len = owl_list_get_size(dl);
259  for (i = 0; i < len; i++) {
[2c79eae]260    owl_io_dispatch *d = owl_list_get_element(dl, i);
261    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
[df0138f]262      d->callback(d, d->data);
263    }
264  }
265  dispatch_active = 0;
266  owl_select_io_dispatch_gc();
[2c79eae]267
268  return TRUE;
[df0138f]269}
270
[2c79eae]271static GSourceFuncs owl_io_dispatch_funcs = {
272  owl_io_dispatch_prepare,
273  owl_io_dispatch_check,
274  owl_io_dispatch_dispatch,
275  NULL
276};
277
[df0138f]278int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
279{
280  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
281  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
282    /* Don't mess with non-perl dispatch functions from here. */
283    return 1;
284  }
285  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
286  return 0;
287}
288
289int owl_select_remove_perl_io_dispatch(int fd)
290{
291  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
292  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
293    /* Only remove perl io dispatchers from here. */
294    owl_select_remove_io_dispatch(d);
295    return 0;
296  }
297  return 1;
298}
299
[2f69081]300int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
[9c7a701]301{
302  aim_conn_t *cur;
303  aim_session_t *sess;
304  int max_fd;
305
306  max_fd = 0;
307  sess = owl_global_get_aimsess(&g);
[a409a9d]308  for (cur = sess->connlist; cur; cur = cur->next) {
[9c7a701]309    if (cur->fd != -1) {
[2f69081]310      FD_SET(cur->fd, rfds);
311      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
312        /* Yes, we're checking writable sockets here. Without it, AIM
313           login is really slow. */
314        FD_SET(cur->fd, wfds);
315      }
316     
[9c7a701]317      if (cur->fd > max_fd)
318        max_fd = cur->fd;
319    }
320  }
321  return max_fd;
322}
323
[5a35c708]324void owl_process_input_char(owl_input j)
325{
326  int ret;
327
328  owl_global_set_lastinputtime(&g, time(NULL));
329  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
330  if (ret!=0 && ret!=1) {
331    owl_function_makemsg("Unable to handle keypress");
332  }
333}
334
[40bda84]335void owl_select_mask_signals(sigset_t *oldmask) {
336  sigset_t set;
337
338  sigemptyset(&set);
[c3031f3]339  sigaddset(&set, SIGWINCH);
340  sigaddset(&set, SIGALRM);
341  sigaddset(&set, SIGPIPE);
342  sigaddset(&set, SIGTERM);
343  sigaddset(&set, SIGHUP);
[40bda84]344  sigaddset(&set, SIGINT);
345  sigprocmask(SIG_BLOCK, &set, oldmask);
346}
347
348void owl_select_handle_intr(sigset_t *restore)
[3a84694]349{
350  owl_input in;
[0cb6c26]351
352  owl_global_unset_interrupted(&g);
[40bda84]353
354  sigprocmask(SIG_SETMASK, restore, NULL);
[0cb6c26]355
[3a84694]356  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
357  owl_process_input_char(in);
358}
359
[4f2166b]360owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
361{
[96828e4]362  owl_ps_action *a = g_new(owl_ps_action, 1);
[4f2166b]363  owl_list *psa_list = owl_global_get_psa_list(&g);
364  a->needs_gc = 0;
365  a->callback = cb;
366  a->destroy = destroy;
367  a->data = data;
368  owl_list_append_element(psa_list, a);
369  return a;
370}
371
372void owl_select_psa_gc(void)
373{
374  int i;
375  owl_list *psa_list;
376  owl_ps_action *a;
377
378  psa_list = owl_global_get_psa_list(&g);
379  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
380    a = owl_list_get_element(psa_list, i);
381    if (a->needs_gc) {
382      owl_list_remove_element(psa_list, i);
383      if (a->destroy) {
384        a->destroy(a);
385      }
[ddbbcffa]386      g_free(a);
[4f2166b]387    }
388  }
389}
390
391void owl_select_remove_pre_select_action(owl_ps_action *a)
392{
393  a->needs_gc = 1;
394  if (!psa_active)
395    owl_select_psa_gc();
396}
397
398int owl_select_do_pre_select_actions(void)
399{
400  int i, len, ret;
401  owl_list *psa_list;
402
403  psa_active = 1;
404  ret = 0;
405  psa_list = owl_global_get_psa_list(&g);
406  len = owl_list_get_size(psa_list);
407  for (i = 0; i < len; i++) {
408    owl_ps_action *a = owl_list_get_element(psa_list, i);
409    if (a->callback != NULL && a->callback(a, a->data)) {
410      ret = 1;
411    }
412  }
413  psa_active = 0;
414  owl_select_psa_gc();
415  return ret;
416}
417
[2c79eae]418#if 0
419/* FIXME: Reimplement the AIM hack and handle AIM events. */
[c79a047]420void owl_select(void)
[9c7a701]421{
[df0138f]422  int i, max_fd, max_fd2, aim_done, ret;
[9c7a701]423  fd_set r;
[df0138f]424  fd_set w;
[9c7a701]425  fd_set e;
[2f69081]426  fd_set aim_rfds, aim_wfds;
[3a84694]427  struct timespec timeout;
[0cb6c26]428  sigset_t mask;
[9c7a701]429
[b7bb454]430  owl_select_process_timers(&timeout);
[9c7a701]431
[40bda84]432  owl_select_mask_signals(&mask);
433
[3a84694]434  if(owl_global_is_interrupted(&g)) {
[40bda84]435    owl_select_handle_intr(&mask);
[3a84694]436    return;
437  }
[df0138f]438  FD_ZERO(&r);
439  FD_ZERO(&w);
440  FD_ZERO(&e);
[3a84694]441
[6fc40a7]442  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
[9c7a701]443
444  /* AIM HACK:
445   *
446   *  The problem - I'm not sure where to hook into the owl/faim
447   *  interface to keep track of when the AIM socket(s) open and
448   *  close. In particular, the bosconn thing throws me off. So,
449   *  rather than register particular dispatchers for AIM, I look up
450   *  the relevant FDs and add them to select's watch lists, then
451   *  check for them individually before moving on to the other
452   *  dispatchers. --asedeno
453   */
454  aim_done = 1;
[2f69081]455  FD_ZERO(&aim_rfds);
456  FD_ZERO(&aim_wfds);
[9c7a701]457  if (owl_global_is_doaimevents(&g)) {
458    aim_done = 0;
[df0138f]459    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
460    if (max_fd < max_fd2) max_fd = max_fd2;
461    for(i = 0; i <= max_fd2; i++) {
[2f69081]462      if (FD_ISSET(i, &aim_rfds)) {
[9c7a701]463        FD_SET(i, &r);
464        FD_SET(i, &e);
465      }
[df0138f]466      if (FD_ISSET(i, &aim_wfds)) {
467        FD_SET(i, &w);
468        FD_SET(i, &e);
469      }
[9c7a701]470    }
471  }
472  /* END AIM HACK */
[2f69081]473
[4f2166b]474  if (owl_select_do_pre_select_actions()) {
475    timeout.tv_sec = 0;
476    timeout.tv_nsec = 0;
477  }
478
[df0138f]479  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
[3a84694]480
481  if(ret < 0 && errno == EINTR) {
482    if(owl_global_is_interrupted(&g)) {
[40bda84]483      owl_select_handle_intr(NULL);
[3a84694]484    }
[40bda84]485    sigprocmask(SIG_SETMASK, &mask, NULL);
[3a84694]486    return;
487  }
488
[40bda84]489  sigprocmask(SIG_SETMASK, &mask, NULL);
[3a84694]490
491  if(ret > 0) {
[7ca5d3e]492    /* AIM HACK: process all AIM events at once. */
493    for(i = 0; !aim_done && i <= max_fd; i++) {
[df0138f]494      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
[7ca5d3e]495        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
[9c7a701]496          owl_process_aim();
497          aim_done = 1;
498        }
499      }
500    }
[df0138f]501    owl_select_io_dispatch(&r, &w, &e, max_fd);
[9c7a701]502  }
503}
[2c79eae]504#endif
505
506void owl_select_init(void)
507{
508  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
509  g_source_attach(owl_timer_source, NULL);
510
511  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
512  g_source_attach(owl_io_dispatch_source, NULL);
513}
[3ecd78b]514
515void owl_select_run_loop(void)
516{
[2c79eae]517  loop = g_main_loop_new(NULL, FALSE);
518  g_main_loop_run(loop);
[3ecd78b]519}
520
521void owl_select_quit_loop(void)
522{
[2c79eae]523  if (loop) {
524    g_main_loop_quit(loop);
525    loop = NULL;
526  }
[3ecd78b]527}
Note: See TracBrowser for help on using the repository browser.