source: select.c @ 959cb85

release-1.8release-1.9
Last change on this file since 959cb85 was 2c79eae, checked in by David Benjamin <davidben@mit.edu>, 10 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
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 int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
11  return t1->time - t2->time;
12}
13
14owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
15{
16  owl_timer *t = g_new(owl_timer, 1);
17  GList **timers = owl_global_get_timerlist(&g);
18
19  t->time = time(NULL) + after;
20  t->interval = interval;
21  t->callback = cb;
22  t->destroy = destroy;
23  t->data = data;
24  t->name = name ? g_strdup(name) : NULL;
25
26  *timers = g_list_insert_sorted(*timers, t,
27                                 (GCompareFunc)_owl_select_timer_cmp);
28  return t;
29}
30
31void owl_select_remove_timer(owl_timer *t)
32{
33  GList **timers = owl_global_get_timerlist(&g);
34  if (t && g_list_find(*timers, t)) {
35    *timers = g_list_remove(*timers, t);
36    if(t->destroy) {
37      t->destroy(t);
38    }
39    g_free(t->name);
40    g_free(t);
41  }
42}
43
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) {
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
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? */
94  while(*timers) {
95    owl_timer *t = (*timers)->data;
96    int remove = 0;
97
98    if(t->time > now.tv_sec)
99      break;
100
101    /* Reschedule if appropriate */
102    if(t->interval > 0) {
103      t->time = now.tv_sec + t->interval;
104      *timers = g_list_remove(*timers, t);
105      *timers = g_list_insert_sorted(*timers, t,
106                                     (GCompareFunc)_owl_select_timer_cmp);
107    } else {
108      remove = 1;
109    }
110
111    /* Do the callback */
112    t->callback(t, t->data);
113    if(remove) {
114      owl_select_remove_timer(t);
115    }
116  }
117  return TRUE;
118}
119
120static GSourceFuncs owl_timer_funcs = {
121  owl_timer_prepare,
122  owl_timer_check,
123  owl_timer_dispatch,
124  NULL
125};
126
127
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);
172        g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
173        g_free(d);
174      }
175    }
176  }
177}
178
179static void owl_select_io_dispatch_gc(void)
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{
204  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
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
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
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
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}
237
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);
243  len = owl_list_get_size(dl);
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;
248  }
249  return FALSE;
250}
251
252static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
253  int i, len;
254  const owl_list *dl;
255
256  dispatch_active = 1;
257  dl = owl_global_get_io_dispatch_list(&g);
258  len = owl_list_get_size(dl);
259  for (i = 0; i < len; i++) {
260    owl_io_dispatch *d = owl_list_get_element(dl, i);
261    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
262      d->callback(d, d->data);
263    }
264  }
265  dispatch_active = 0;
266  owl_select_io_dispatch_gc();
267
268  return TRUE;
269}
270
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
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
300int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
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);
308  for (cur = sess->connlist; cur; cur = cur->next) {
309    if (cur->fd != -1) {
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     
317      if (cur->fd > max_fd)
318        max_fd = cur->fd;
319    }
320  }
321  return max_fd;
322}
323
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
335void owl_select_mask_signals(sigset_t *oldmask) {
336  sigset_t set;
337
338  sigemptyset(&set);
339  sigaddset(&set, SIGWINCH);
340  sigaddset(&set, SIGALRM);
341  sigaddset(&set, SIGPIPE);
342  sigaddset(&set, SIGTERM);
343  sigaddset(&set, SIGHUP);
344  sigaddset(&set, SIGINT);
345  sigprocmask(SIG_BLOCK, &set, oldmask);
346}
347
348void owl_select_handle_intr(sigset_t *restore)
349{
350  owl_input in;
351
352  owl_global_unset_interrupted(&g);
353
354  sigprocmask(SIG_SETMASK, restore, NULL);
355
356  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
357  owl_process_input_char(in);
358}
359
360owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
361{
362  owl_ps_action *a = g_new(owl_ps_action, 1);
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      }
386      g_free(a);
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
418#if 0
419/* FIXME: Reimplement the AIM hack and handle AIM events. */
420void owl_select(void)
421{
422  int i, max_fd, max_fd2, aim_done, ret;
423  fd_set r;
424  fd_set w;
425  fd_set e;
426  fd_set aim_rfds, aim_wfds;
427  struct timespec timeout;
428  sigset_t mask;
429
430  owl_select_process_timers(&timeout);
431
432  owl_select_mask_signals(&mask);
433
434  if(owl_global_is_interrupted(&g)) {
435    owl_select_handle_intr(&mask);
436    return;
437  }
438  FD_ZERO(&r);
439  FD_ZERO(&w);
440  FD_ZERO(&e);
441
442  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
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;
455  FD_ZERO(&aim_rfds);
456  FD_ZERO(&aim_wfds);
457  if (owl_global_is_doaimevents(&g)) {
458    aim_done = 0;
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++) {
462      if (FD_ISSET(i, &aim_rfds)) {
463        FD_SET(i, &r);
464        FD_SET(i, &e);
465      }
466      if (FD_ISSET(i, &aim_wfds)) {
467        FD_SET(i, &w);
468        FD_SET(i, &e);
469      }
470    }
471  }
472  /* END AIM HACK */
473
474  if (owl_select_do_pre_select_actions()) {
475    timeout.tv_sec = 0;
476    timeout.tv_nsec = 0;
477  }
478
479  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
480
481  if(ret < 0 && errno == EINTR) {
482    if(owl_global_is_interrupted(&g)) {
483      owl_select_handle_intr(NULL);
484    }
485    sigprocmask(SIG_SETMASK, &mask, NULL);
486    return;
487  }
488
489  sigprocmask(SIG_SETMASK, &mask, NULL);
490
491  if(ret > 0) {
492    /* AIM HACK: process all AIM events at once. */
493    for(i = 0; !aim_done && i <= max_fd; i++) {
494      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
495        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
496          owl_process_aim();
497          aim_done = 1;
498        }
499      }
500    }
501    owl_select_io_dispatch(&r, &w, &e, max_fd);
502  }
503}
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}
514
515void owl_select_run_loop(void)
516{
517  loop = g_main_loop_new(NULL, FALSE);
518  g_main_loop_run(loop);
519}
520
521void owl_select_quit_loop(void)
522{
523  if (loop) {
524    g_main_loop_quit(loop);
525    loop = NULL;
526  }
527}
Note: See TracBrowser for help on using the repository browser.