source: select.c @ ffc4df6

release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since ffc4df6 was ffc4df6, checked in by Alejandro R. Sedeño <asedeno@mit.edu>, 12 years ago
Switch perl API to new I/O Dispatch API. Signed-off-by: Alejandro R. Sedeño <asedeno@mit.edu>
  • Property mode set to 100644
File size: 15.5 KB
Line 
1#include "owl.h"
2
3static int dispatch_active = 0;
4static int psa_active = 0;
5
6int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
7  return t1->time - t2->time;
8}
9
10int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
11  return t1 == t2;
12}
13
14owl_timer *owl_select_add_timer(int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
15{
16  owl_timer *t = owl_malloc(sizeof(owl_timer));
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
25  *timers = g_list_insert_sorted(*timers, t,
26                                 (GCompareFunc)_owl_select_timer_cmp);
27  return t;
28}
29
30void owl_select_remove_timer(owl_timer *t)
31{
32  GList **timers = owl_global_get_timerlist(&g);
33  if (t && g_list_find(*timers, t)) {
34    *timers = g_list_remove(*timers, t);
35    if(t->destroy) {
36      t->destroy(t);
37    }
38    owl_free(t);
39  }
40}
41
42void owl_select_process_timers(struct timespec *timeout)
43{
44  time_t now = time(NULL);
45  GList **timers = owl_global_get_timerlist(&g);
46
47  while(*timers) {
48    owl_timer *t = (*timers)->data;
49    int remove = 0;
50
51    if(t->time > now)
52      break;
53
54    /* Reschedule if appropriate */
55    if(t->interval > 0) {
56      t->time = now + t->interval;
57      *timers = g_list_remove(*timers, t);
58      *timers = g_list_insert_sorted(*timers, t,
59                                     (GCompareFunc)_owl_select_timer_cmp);
60    } else {
61      remove = 1;
62    }
63
64    /* Do the callback */
65    t->callback(t, t->data);
66    if(remove) {
67      owl_select_remove_timer(t);
68    }
69  }
70
71  if(*timers) {
72    owl_timer *t = (*timers)->data;
73    timeout->tv_sec = t->time - now;
74    if (timeout->tv_sec > 60)
75      timeout->tv_sec = 60;
76  } else {
77    timeout->tv_sec = 60;
78  }
79
80  timeout->tv_nsec = 0;
81}
82
83/* Returns the index of the dispatch for the file descriptor. */
84int owl_select_find_dispatch(int fd)
85{
86  int i, len;
87  const owl_list *dl;
88  const owl_dispatch *d;
89 
90  dl = owl_global_get_dispatchlist(&g);
91  len = owl_list_get_size(dl);
92  for(i = 0; i < len; i++) {
93    d = owl_list_get_element(dl, i);
94    if (d->fd == fd) return i;
95  }
96  return -1;
97}
98
99void owl_select_remove_dispatch_at(int elt) /* noproto */
100{
101  owl_list *dl;
102  owl_dispatch *d;
103
104  dl = owl_global_get_dispatchlist(&g);
105  d = owl_list_get_element(dl, elt);
106  owl_list_remove_element(dl, elt);
107  if (d->destroy) {
108    d->destroy(d);
109  }
110}
111
112/* Adds a new owl_dispatch to the list, replacing existing ones if needed. */
113void owl_select_add_dispatch(owl_dispatch *d)
114{
115  int elt;
116  owl_list *dl;
117
118  d->needs_gc = 0;
119
120  elt = owl_select_find_dispatch(d->fd);
121  dl = owl_global_get_dispatchlist(&g);
122 
123  if (elt != -1) {  /* If we have a dispatch for this FD */
124    owl_dispatch *d_old;
125    d_old = owl_list_get_element(dl, elt);
126    /* Ignore if we're adding the same dispatch again.  Otherwise
127       replace the old dispatch. */
128    if (d_old != d) {
129      owl_select_remove_dispatch_at(elt);
130    }
131  }
132  owl_list_append_element(dl, d);
133}
134
135/* Removes an owl_dispatch to the list, based on it's file descriptor. */
136void owl_select_remove_dispatch(int fd)
137{
138  int elt;
139  owl_list *dl;
140  owl_dispatch *d;
141
142  elt = owl_select_find_dispatch(fd);
143  if(elt == -1) {
144    return;
145  } else if(dispatch_active) {
146    /* Defer the removal until dispatch is done walking the list */
147    dl = owl_global_get_dispatchlist(&g);
148    d = owl_list_get_element(dl, elt);
149    d->needs_gc = 1;
150  } else {
151    owl_select_remove_dispatch_at(elt);
152  }
153}
154
155int owl_select_dispatch_count(void)
156{
157  return owl_list_get_size(owl_global_get_dispatchlist(&g));
158}
159
160int owl_select_dispatch_prepare_fd_sets(fd_set *r, fd_set *e)
161{
162  int i, len, max_fd;
163  owl_dispatch *d;
164  const owl_list *dl;
165
166  dl = owl_global_get_dispatchlist(&g);
167  max_fd = 0;
168  len = owl_select_dispatch_count();
169  for(i = 0; i < len; i++) {
170    d = owl_list_get_element(dl, i);
171    FD_SET(d->fd, r);
172    FD_SET(d->fd, e);
173    if (max_fd < d->fd) max_fd = d->fd;
174  }
175  return max_fd + 1;
176}
177
178void owl_select_gc(void)
179{
180  int i;
181  owl_list *dl;
182
183  dl = owl_global_get_dispatchlist(&g);
184  /*
185   * Count down so we aren't set off by removing items from the list
186   * during the iteration.
187   */
188  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
189    const owl_dispatch *d = owl_list_get_element(dl, i);
190    if(d->needs_gc) {
191      owl_select_remove_dispatch_at(i);
192    }
193  }
194}
195
196void owl_select_dispatch(fd_set *fds, int max_fd)
197{
198  int i, len;
199  owl_dispatch *d;
200  const owl_list *dl;
201
202  dl = owl_global_get_dispatchlist(&g);
203  len = owl_select_dispatch_count();
204
205  dispatch_active = 1;
206
207  for(i = 0; i < len; i++) {
208    d = owl_list_get_element(dl, i);
209    /* While d shouldn't normally be null, the list may be altered by
210     * functions we dispatch to. */
211    if (d != NULL && !d->needs_gc && FD_ISSET(d->fd, fds)) {
212      if (d->cfunc != NULL) {
213        d->cfunc(d);
214      }
215    }
216  }
217
218  dispatch_active = 0;
219  owl_select_gc();
220}
221
222static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
223{
224  int i, len;
225  const owl_list *dl;
226  owl_io_dispatch *d;
227  dl = owl_global_get_io_dispatch_list(&g);
228  len = owl_list_get_size(dl);
229  for(i = 0; i < len; i++) {
230    d = owl_list_get_element(dl, i);
231    if (d->fd == fd) return d;
232  }
233  return NULL;
234}
235
236static int owl_select_find_io_dispatch(const owl_io_dispatch *in)
237{
238  int i, len;
239  const owl_list *dl;
240
241  if (in != NULL) {
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 == in) return i;
247    }
248  }
249  return -1;
250}
251
252void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
253{
254  int elt;
255  if (in != NULL) {
256    elt = owl_select_find_io_dispatch(in);
257    if (elt != -1) {
258      owl_list *dl = owl_global_get_io_dispatch_list(&g);
259      owl_io_dispatch *d = owl_list_get_element(dl, elt);
260      if (dispatch_active)
261        d->needs_gc = 1;
262      else {
263        owl_list_remove_element(dl, elt);
264        if (d->destroy)
265          d->destroy(d);
266        owl_free(d);
267      }
268    }
269  }
270}
271
272void owl_select_io_dispatch_gc(void)
273{
274  int i;
275  owl_list *dl;
276
277  dl = owl_global_get_io_dispatch_list(&g);
278  /*
279   * Count down so we aren't set off by removing items from the list
280   * during the iteration.
281   */
282  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
283    owl_io_dispatch *d = owl_list_get_element(dl, i);
284    if(d->needs_gc) {
285      owl_select_remove_io_dispatch(d);
286    }
287  }
288}
289
290/* Each FD may have at most one dispatcher.
291 * If a new dispatch is added for an FD, the old one is removed.
292 * mode determines what types of events are watched for, and may be any combination of:
293 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
294 */
295const 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)
296{
297  owl_io_dispatch *d = owl_malloc(sizeof(owl_io_dispatch));
298  owl_list *dl = owl_global_get_io_dispatch_list(&g);
299
300  d->fd = fd;
301  d->needs_gc = 0;
302  d->mode = mode;
303  d->callback = cb;
304  d->destroy = destroy;
305  d->data = data;
306
307  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
308  owl_list_append_element(dl, d);
309
310  return d;
311}
312
313int owl_select_prepare_io_dispatch_fd_sets(fd_set *rfds, fd_set *wfds, fd_set *efds) {
314  int i, len, max_fd;
315  owl_io_dispatch *d;
316  owl_list *dl = owl_global_get_io_dispatch_list(&g);
317
318  max_fd = 0;
319  len = owl_list_get_size(dl);
320  for (i = 0; i < len; i++) {
321    d = owl_list_get_element(dl, i);
322    if (d->mode & (OWL_IO_READ | OWL_IO_WRITE | OWL_IO_EXCEPT)) {
323      if (max_fd < d->fd) max_fd = d->fd;
324      if (d->mode & OWL_IO_READ) FD_SET(d->fd, rfds);
325      if (d->mode & OWL_IO_WRITE) FD_SET(d->fd, wfds);
326      if (d->mode & OWL_IO_EXCEPT) FD_SET(d->fd, efds);
327    }
328  }
329  return max_fd + 1;
330}
331
332void owl_select_io_dispatch(const fd_set *rfds, const fd_set *wfds, const fd_set *efds, const int max_fd)
333{
334  int i, len;
335  owl_io_dispatch *d;
336  owl_list *dl = owl_global_get_io_dispatch_list(&g);
337
338  dispatch_active = 1;
339  len = owl_list_get_size(dl);
340  for (i = 0; i < len; i++) {
341    d = owl_list_get_element(dl, i);
342    if (d->fd < max_fd && d->callback != NULL &&
343        ((d->mode & OWL_IO_READ && FD_ISSET(d->fd, rfds)) ||
344         (d->mode & OWL_IO_WRITE && FD_ISSET(d->fd, wfds)) ||
345         (d->mode & OWL_IO_EXCEPT && FD_ISSET(d->fd, efds)))) {
346      d->callback(d, d->data);
347    }
348  }
349  dispatch_active = 0;
350  owl_select_io_dispatch_gc();
351}
352
353int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
354{
355  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
356  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
357    /* Don't mess with non-perl dispatch functions from here. */
358    return 1;
359  }
360  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
361  return 0;
362}
363
364int owl_select_remove_perl_io_dispatch(int fd)
365{
366  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
367  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
368    /* Only remove perl io dispatchers from here. */
369    owl_select_remove_io_dispatch(d);
370    return 0;
371  }
372  return 1;
373}
374
375int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
376{
377  aim_conn_t *cur;
378  aim_session_t *sess;
379  int max_fd;
380
381  max_fd = 0;
382  sess = owl_global_get_aimsess(&g);
383  for (cur = sess->connlist, max_fd = 0; cur; cur = cur->next) {
384    if (cur->fd != -1) {
385      FD_SET(cur->fd, rfds);
386      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
387        /* Yes, we're checking writable sockets here. Without it, AIM
388           login is really slow. */
389        FD_SET(cur->fd, wfds);
390      }
391     
392      if (cur->fd > max_fd)
393        max_fd = cur->fd;
394    }
395  }
396  return max_fd;
397}
398
399void owl_process_input_char(owl_input j)
400{
401  int ret;
402  owl_popwin *pw;
403  owl_editwin *tw;
404
405  owl_global_set_lastinputtime(&g, time(NULL));
406  pw=owl_global_get_popwin(&g);
407  tw=owl_global_get_typwin(&g);
408
409  owl_global_set_lastinputtime(&g, time(NULL));
410  /* find and activate the current keymap.
411   * TODO: this should really get fixed by activating
412   * keymaps as we switch between windows...
413   */
414  if (pw && owl_popwin_is_active(pw) && owl_global_get_viewwin(&g)) {
415    owl_context_set_popless(owl_global_get_context(&g), 
416                            owl_global_get_viewwin(&g));
417    owl_function_activate_keymap("popless");
418  } else if (owl_global_is_typwin_active(&g) 
419             && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_ONELINE) {
420    /*
421      owl_context_set_editline(owl_global_get_context(&g), tw);
422      owl_function_activate_keymap("editline");
423    */
424  } else if (owl_global_is_typwin_active(&g) 
425             && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_MULTILINE) {
426    owl_context_set_editmulti(owl_global_get_context(&g), tw);
427    owl_function_activate_keymap("editmulti");
428  } else {
429    owl_context_set_recv(owl_global_get_context(&g));
430    owl_function_activate_keymap("recv");
431  }
432  /* now actually handle the keypress */
433  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
434  if (ret!=0 && ret!=1) {
435    owl_function_makemsg("Unable to handle keypress");
436  }
437}
438
439void owl_select_mask_signals(sigset_t *oldmask) {
440  sigset_t set;
441
442  sigemptyset(&set);
443  sigaddset(&set, SIGINT);
444  sigaddset(&set, SIGTSTP);
445  sigprocmask(SIG_BLOCK, &set, oldmask);
446}
447
448void owl_select_handle_intr(sigset_t *restore)
449{
450  owl_input in;
451
452  owl_global_unset_interrupted(&g);
453
454  sigprocmask(SIG_SETMASK, restore, NULL);
455
456  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
457  owl_process_input_char(in);
458}
459
460void owl_select_check_tstp(void) {
461  if(owl_global_is_sigstp(&g)) {
462    owl_function_makemsg("Use :suspend to suspend.");
463    owl_global_unset_got_sigstp(&g);
464  }
465}
466
467owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
468{
469  owl_ps_action *a = owl_malloc(sizeof(owl_ps_action));
470  owl_list *psa_list = owl_global_get_psa_list(&g);
471  a->needs_gc = 0;
472  a->callback = cb;
473  a->destroy = destroy;
474  a->data = data;
475  owl_list_append_element(psa_list, a);
476  return a;
477}
478
479void owl_select_psa_gc(void)
480{
481  int i;
482  owl_list *psa_list;
483  owl_ps_action *a;
484
485  psa_list = owl_global_get_psa_list(&g);
486  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
487    a = owl_list_get_element(psa_list, i);
488    if (a->needs_gc) {
489      owl_list_remove_element(psa_list, i);
490      if (a->destroy) {
491        a->destroy(a);
492      }
493      owl_free(a);
494    }
495  }
496}
497
498void owl_select_remove_pre_select_action(owl_ps_action *a)
499{
500  a->needs_gc = 1;
501  if (!psa_active)
502    owl_select_psa_gc();
503}
504
505int owl_select_do_pre_select_actions(void)
506{
507  int i, len, ret;
508  owl_list *psa_list;
509
510  psa_active = 1;
511  ret = 0;
512  psa_list = owl_global_get_psa_list(&g);
513  len = owl_list_get_size(psa_list);
514  for (i = 0; i < len; i++) {
515    owl_ps_action *a = owl_list_get_element(psa_list, i);
516    if (a->callback != NULL && a->callback(a, a->data)) {
517      ret = 1;
518    }
519  }
520  psa_active = 0;
521  owl_select_psa_gc();
522  return ret;
523}
524
525void owl_select(void)
526{
527  int i, max_fd, max_fd2, aim_done, ret;
528  fd_set r;
529  fd_set w;
530  fd_set e;
531  fd_set aim_rfds, aim_wfds;
532  struct timespec timeout;
533  sigset_t mask;
534
535  owl_select_process_timers(&timeout);
536
537  owl_select_mask_signals(&mask);
538
539  owl_select_check_tstp();
540  if(owl_global_is_interrupted(&g)) {
541    owl_select_handle_intr(&mask);
542    return;
543  }
544  FD_ZERO(&r);
545  FD_ZERO(&w);
546  FD_ZERO(&e);
547
548  max_fd = owl_select_dispatch_prepare_fd_sets(&r, &e);
549  max_fd2 = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
550  if (max_fd < max_fd2) max_fd = max_fd2;
551
552  /* AIM HACK:
553   *
554   *  The problem - I'm not sure where to hook into the owl/faim
555   *  interface to keep track of when the AIM socket(s) open and
556   *  close. In particular, the bosconn thing throws me off. So,
557   *  rather than register particular dispatchers for AIM, I look up
558   *  the relevant FDs and add them to select's watch lists, then
559   *  check for them individually before moving on to the other
560   *  dispatchers. --asedeno
561   */
562  aim_done = 1;
563  FD_ZERO(&aim_rfds);
564  FD_ZERO(&aim_wfds);
565  if (owl_global_is_doaimevents(&g)) {
566    aim_done = 0;
567    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
568    if (max_fd < max_fd2) max_fd = max_fd2;
569    for(i = 0; i <= max_fd2; i++) {
570      if (FD_ISSET(i, &aim_rfds)) {
571        FD_SET(i, &r);
572        FD_SET(i, &e);
573      }
574      if (FD_ISSET(i, &aim_wfds)) {
575        FD_SET(i, &w);
576        FD_SET(i, &e);
577      }
578    }
579  }
580  /* END AIM HACK */
581
582  if (owl_select_do_pre_select_actions()) {
583    timeout.tv_sec = 0;
584    timeout.tv_nsec = 0;
585  }
586
587  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
588
589  if(ret < 0 && errno == EINTR) {
590    owl_select_check_tstp();
591    if(owl_global_is_interrupted(&g)) {
592      owl_select_handle_intr(NULL);
593    }
594    sigprocmask(SIG_SETMASK, &mask, NULL);
595    return;
596  }
597
598  sigprocmask(SIG_SETMASK, &mask, NULL);
599
600  if(ret > 0) {
601    /* Merge fd_sets and clear AIM FDs. */
602    for(i = 0; i <= max_fd; i++) {
603      /* Merge all interesting FDs into one set, since we have a
604         single dispatch per FD. */
605      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
606        /* AIM HACK: no separate dispatch, just process here if
607           needed, and only once per run through. */
608        if (!aim_done && (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds))) {
609          owl_process_aim();
610          aim_done = 1;
611        }
612        else {
613          FD_SET(i, &r);
614        }
615      }
616    }
617    /* NOTE: the same dispatch function is called for both exceptional
618       and read ready FDs. */
619    owl_select_dispatch(&r, max_fd);
620    owl_select_io_dispatch(&r, &w, &e, max_fd);
621  }
622}
Note: See TracBrowser for help on using the repository browser.