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
Line 
1#include "owl.h"
2
3static GMainLoop *loop = NULL;
4static int dispatch_active = 0;
5static int psa_active = 0;
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};
92
93int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
94  return t1->time - t2->time;
95}
96
97int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
98  return t1 == t2;
99}
100
101owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
102{
103  owl_timer *t = g_new(owl_timer, 1);
104  GList **timers = owl_global_get_timerlist(&g);
105
106  t->time = time(NULL) + after;
107  t->interval = interval;
108  t->callback = cb;
109  t->destroy = destroy;
110  t->data = data;
111  t->name = name ? g_strdup(name) : NULL;
112
113  *timers = g_list_insert_sorted(*timers, t,
114                                 (GCompareFunc)_owl_select_timer_cmp);
115  return t;
116}
117
118void owl_select_remove_timer(owl_timer *t)
119{
120  GList **timers = owl_global_get_timerlist(&g);
121  if (t && g_list_find(*timers, t)) {
122    *timers = g_list_remove(*timers, t);
123    if(t->destroy) {
124      t->destroy(t);
125    }
126    g_free(t->name);
127    g_free(t);
128  }
129}
130
131
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}
137
138static gboolean owl_io_dispatch_check(GSource *source) {
139  int i, len;
140  const owl_list *dl;
141
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;
148  }
149  return FALSE;
150}
151
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    }
164  }
165  dispatch_active = 0;
166  owl_select_io_dispatch_gc();
167
168  return TRUE;
169}
170
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
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);
222        g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
223        g_free(d);
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{
254  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
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
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
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
304int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
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);
312  for (cur = sess->connlist; cur; cur = cur->next) {
313    if (cur->fd != -1) {
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     
321      if (cur->fd > max_fd)
322        max_fd = cur->fd;
323    }
324  }
325  return max_fd;
326}
327
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
339owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
340{
341  owl_ps_action *a = g_new(owl_ps_action, 1);
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      }
365      g_free(a);
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
397#if 0
398void owl_select(void)
399{
400  int i, max_fd, max_fd2, aim_done, ret;
401  fd_set r;
402  fd_set w;
403  fd_set e;
404  fd_set aim_rfds, aim_wfds;
405  struct timespec timeout;
406  sigset_t mask;
407
408  owl_select_process_timers(&timeout);
409
410  owl_select_mask_signals(&mask);
411
412  if(owl_global_is_interrupted(&g)) {
413    owl_select_handle_intr(&mask);
414    return;
415  }
416  FD_ZERO(&r);
417  FD_ZERO(&w);
418  FD_ZERO(&e);
419
420  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
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;
433  FD_ZERO(&aim_rfds);
434  FD_ZERO(&aim_wfds);
435  if (owl_global_is_doaimevents(&g)) {
436    aim_done = 0;
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++) {
440      if (FD_ISSET(i, &aim_rfds)) {
441        FD_SET(i, &r);
442        FD_SET(i, &e);
443      }
444      if (FD_ISSET(i, &aim_wfds)) {
445        FD_SET(i, &w);
446        FD_SET(i, &e);
447      }
448    }
449  }
450  /* END AIM HACK */
451
452  if (owl_select_do_pre_select_actions()) {
453    timeout.tv_sec = 0;
454    timeout.tv_nsec = 0;
455  }
456
457  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
458
459  if(ret < 0 && errno == EINTR) {
460    if(owl_global_is_interrupted(&g)) {
461      owl_select_handle_intr(NULL);
462    }
463    sigprocmask(SIG_SETMASK, &mask, NULL);
464    return;
465  }
466
467  sigprocmask(SIG_SETMASK, &mask, NULL);
468
469  if(ret > 0) {
470    /* AIM HACK: process all AIM events at once. */
471    for(i = 0; !aim_done && i <= max_fd; i++) {
472      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
473        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
474          owl_process_aim();
475          aim_done = 1;
476        }
477      }
478    }
479    owl_select_io_dispatch(&r, &w, &e, max_fd);
480  }
481}
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}
492
493void owl_select_run_loop(void)
494{
495  loop = g_main_loop_new(NULL, FALSE);
496  g_main_loop_run(loop);
497}
498
499void owl_select_quit_loop(void)
500{
501  if (loop) {
502    g_main_loop_quit(loop);
503    loop = NULL;
504  }
505}
Note: See TracBrowser for help on using the repository browser.