source: select.c @ 3535a6e

release-1.10release-1.8release-1.9
Last change on this file since 3535a6e was 3535a6e, checked in by David Benjamin <davidben@mit.edu>, 13 years ago
First go at sigwait-based signal handling Instead of relying on pselect and signal masking to listen for signals, which glib doesn't support, we spawn a dedicated signal thread that loops in sigwait. These signals are posted back to the main message loop which may handle them at will. This avoids the need for complex reentrant code and sig_atomic_t. This removes the final pre-select action. SIGINT doesn't quite work right yet because we can no longer take it in the middle of an event loop iteration.
  • 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 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
335owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
336{
337  owl_ps_action *a = g_new(owl_ps_action, 1);
338  owl_list *psa_list = owl_global_get_psa_list(&g);
339  a->needs_gc = 0;
340  a->callback = cb;
341  a->destroy = destroy;
342  a->data = data;
343  owl_list_append_element(psa_list, a);
344  return a;
345}
346
347void owl_select_psa_gc(void)
348{
349  int i;
350  owl_list *psa_list;
351  owl_ps_action *a;
352
353  psa_list = owl_global_get_psa_list(&g);
354  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
355    a = owl_list_get_element(psa_list, i);
356    if (a->needs_gc) {
357      owl_list_remove_element(psa_list, i);
358      if (a->destroy) {
359        a->destroy(a);
360      }
361      g_free(a);
362    }
363  }
364}
365
366void owl_select_remove_pre_select_action(owl_ps_action *a)
367{
368  a->needs_gc = 1;
369  if (!psa_active)
370    owl_select_psa_gc();
371}
372
373int owl_select_do_pre_select_actions(void)
374{
375  int i, len, ret;
376  owl_list *psa_list;
377
378  psa_active = 1;
379  ret = 0;
380  psa_list = owl_global_get_psa_list(&g);
381  len = owl_list_get_size(psa_list);
382  for (i = 0; i < len; i++) {
383    owl_ps_action *a = owl_list_get_element(psa_list, i);
384    if (a->callback != NULL && a->callback(a, a->data)) {
385      ret = 1;
386    }
387  }
388  psa_active = 0;
389  owl_select_psa_gc();
390  return ret;
391}
392
393#if 0
394/* FIXME: Reimplement the AIM hack and handle AIM events. */
395void owl_select(void)
396{
397  int i, max_fd, max_fd2, aim_done, ret;
398  fd_set r;
399  fd_set w;
400  fd_set e;
401  fd_set aim_rfds, aim_wfds;
402  struct timespec timeout;
403  sigset_t mask;
404
405  owl_select_process_timers(&timeout);
406
407  owl_select_mask_signals(&mask);
408
409  if(owl_global_is_interrupted(&g)) {
410    owl_select_handle_intr(&mask);
411    return;
412  }
413  FD_ZERO(&r);
414  FD_ZERO(&w);
415  FD_ZERO(&e);
416
417  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
418
419  /* AIM HACK:
420   *
421   *  The problem - I'm not sure where to hook into the owl/faim
422   *  interface to keep track of when the AIM socket(s) open and
423   *  close. In particular, the bosconn thing throws me off. So,
424   *  rather than register particular dispatchers for AIM, I look up
425   *  the relevant FDs and add them to select's watch lists, then
426   *  check for them individually before moving on to the other
427   *  dispatchers. --asedeno
428   */
429  aim_done = 1;
430  FD_ZERO(&aim_rfds);
431  FD_ZERO(&aim_wfds);
432  if (owl_global_is_doaimevents(&g)) {
433    aim_done = 0;
434    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
435    if (max_fd < max_fd2) max_fd = max_fd2;
436    for(i = 0; i <= max_fd2; i++) {
437      if (FD_ISSET(i, &aim_rfds)) {
438        FD_SET(i, &r);
439        FD_SET(i, &e);
440      }
441      if (FD_ISSET(i, &aim_wfds)) {
442        FD_SET(i, &w);
443        FD_SET(i, &e);
444      }
445    }
446  }
447  /* END AIM HACK */
448
449  if (owl_select_do_pre_select_actions()) {
450    timeout.tv_sec = 0;
451    timeout.tv_nsec = 0;
452  }
453
454  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
455
456  if(ret < 0 && errno == EINTR) {
457    if(owl_global_is_interrupted(&g)) {
458      owl_select_handle_intr(NULL);
459    }
460    sigprocmask(SIG_SETMASK, &mask, NULL);
461    return;
462  }
463
464  sigprocmask(SIG_SETMASK, &mask, NULL);
465
466  if(ret > 0) {
467    /* AIM HACK: process all AIM events at once. */
468    for(i = 0; !aim_done && i <= max_fd; i++) {
469      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
470        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
471          owl_process_aim();
472          aim_done = 1;
473        }
474      }
475    }
476    owl_select_io_dispatch(&r, &w, &e, max_fd);
477  }
478}
479#endif
480
481void owl_select_init(void)
482{
483  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
484  g_source_attach(owl_timer_source, NULL);
485
486  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
487  g_source_attach(owl_io_dispatch_source, NULL);
488}
489
490void owl_select_run_loop(void)
491{
492  loop = g_main_loop_new(NULL, FALSE);
493  g_main_loop_run(loop);
494}
495
496void owl_select_quit_loop(void)
497{
498  if (loop) {
499    g_main_loop_quit(loop);
500    loop = NULL;
501  }
502}
Note: See TracBrowser for help on using the repository browser.