source: select.c @ fafb842

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