source: select.c @ 7b2686d

Last change on this file since 7b2686d was b279013, 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.
  • Property mode set to 100644
File size: 12.9 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
339void owl_select_mask_signals(sigset_t *oldmask) {
340  sigset_t set;
341
342  sigemptyset(&set);
343  sigaddset(&set, SIGWINCH);
344  sigaddset(&set, SIGALRM);
345  sigaddset(&set, SIGPIPE);
346  sigaddset(&set, SIGTERM);
347  sigaddset(&set, SIGHUP);
348  sigaddset(&set, SIGINT);
349  sigprocmask(SIG_BLOCK, &set, oldmask);
350}
351
352void owl_select_handle_intr(sigset_t *restore)
353{
354  owl_input in;
355
356  owl_global_unset_interrupted(&g);
357
358  sigprocmask(SIG_SETMASK, restore, NULL);
359
360  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
361  owl_process_input_char(in);
362}
363
364owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
365{
366  owl_ps_action *a = g_new(owl_ps_action, 1);
367  owl_list *psa_list = owl_global_get_psa_list(&g);
368  a->needs_gc = 0;
369  a->callback = cb;
370  a->destroy = destroy;
371  a->data = data;
372  owl_list_append_element(psa_list, a);
373  return a;
374}
375
376void owl_select_psa_gc(void)
377{
378  int i;
379  owl_list *psa_list;
380  owl_ps_action *a;
381
382  psa_list = owl_global_get_psa_list(&g);
383  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
384    a = owl_list_get_element(psa_list, i);
385    if (a->needs_gc) {
386      owl_list_remove_element(psa_list, i);
387      if (a->destroy) {
388        a->destroy(a);
389      }
390      g_free(a);
391    }
392  }
393}
394
395void owl_select_remove_pre_select_action(owl_ps_action *a)
396{
397  a->needs_gc = 1;
398  if (!psa_active)
399    owl_select_psa_gc();
400}
401
402int owl_select_do_pre_select_actions(void)
403{
404  int i, len, ret;
405  owl_list *psa_list;
406
407  psa_active = 1;
408  ret = 0;
409  psa_list = owl_global_get_psa_list(&g);
410  len = owl_list_get_size(psa_list);
411  for (i = 0; i < len; i++) {
412    owl_ps_action *a = owl_list_get_element(psa_list, i);
413    if (a->callback != NULL && a->callback(a, a->data)) {
414      ret = 1;
415    }
416  }
417  psa_active = 0;
418  owl_select_psa_gc();
419  return ret;
420}
421
422#if 0
423void owl_select(void)
424{
425  int i, max_fd, max_fd2, aim_done, ret;
426  fd_set r;
427  fd_set w;
428  fd_set e;
429  fd_set aim_rfds, aim_wfds;
430  struct timespec timeout;
431  sigset_t mask;
432
433  owl_select_process_timers(&timeout);
434
435  owl_select_mask_signals(&mask);
436
437  if(owl_global_is_interrupted(&g)) {
438    owl_select_handle_intr(&mask);
439    return;
440  }
441  FD_ZERO(&r);
442  FD_ZERO(&w);
443  FD_ZERO(&e);
444
445  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
446
447  /* AIM HACK:
448   *
449   *  The problem - I'm not sure where to hook into the owl/faim
450   *  interface to keep track of when the AIM socket(s) open and
451   *  close. In particular, the bosconn thing throws me off. So,
452   *  rather than register particular dispatchers for AIM, I look up
453   *  the relevant FDs and add them to select's watch lists, then
454   *  check for them individually before moving on to the other
455   *  dispatchers. --asedeno
456   */
457  aim_done = 1;
458  FD_ZERO(&aim_rfds);
459  FD_ZERO(&aim_wfds);
460  if (owl_global_is_doaimevents(&g)) {
461    aim_done = 0;
462    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
463    if (max_fd < max_fd2) max_fd = max_fd2;
464    for(i = 0; i <= max_fd2; i++) {
465      if (FD_ISSET(i, &aim_rfds)) {
466        FD_SET(i, &r);
467        FD_SET(i, &e);
468      }
469      if (FD_ISSET(i, &aim_wfds)) {
470        FD_SET(i, &w);
471        FD_SET(i, &e);
472      }
473    }
474  }
475  /* END AIM HACK */
476
477  if (owl_select_do_pre_select_actions()) {
478    timeout.tv_sec = 0;
479    timeout.tv_nsec = 0;
480  }
481
482  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
483
484  if(ret < 0 && errno == EINTR) {
485    if(owl_global_is_interrupted(&g)) {
486      owl_select_handle_intr(NULL);
487    }
488    sigprocmask(SIG_SETMASK, &mask, NULL);
489    return;
490  }
491
492  sigprocmask(SIG_SETMASK, &mask, NULL);
493
494  if(ret > 0) {
495    /* AIM HACK: process all AIM events at once. */
496    for(i = 0; !aim_done && i <= max_fd; i++) {
497      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
498        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
499          owl_process_aim();
500          aim_done = 1;
501        }
502      }
503    }
504    owl_select_io_dispatch(&r, &w, &e, max_fd);
505  }
506}
507#endif
508
509void owl_select_init(void)
510{
511  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
512  g_source_attach(owl_timer_source, NULL);
513
514  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
515  g_source_attach(owl_io_dispatch_source, NULL);
516}
517
518void owl_select_run_loop(void)
519{
520  loop = g_main_loop_new(NULL, FALSE);
521  g_main_loop_run(loop);
522}
523
524void owl_select_quit_loop(void)
525{
526  if (loop) {
527    g_main_loop_quit(loop);
528    loop = NULL;
529  }
530}
Note: See TracBrowser for help on using the repository browser.