source: select.c @ df0138f

release-1.10release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since df0138f was df0138f, checked in by Alejandro R. Sedeño <asedeno@mit.edu>, 14 years ago
Add a new I/O Dispatch API Signed-off-by: Alejandro R. Sedeño <asedeno@mit.edu>
  • Property mode set to 100644
File size: 16.4 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_add_perl_dispatch(int fd, SV *cb)
161{
162  int elt;
163  owl_dispatch *d;
164  elt = owl_select_find_dispatch(fd);
165  if (elt != -1) {
166    d = owl_list_get_element(owl_global_get_dispatchlist(&g), elt);
167    if (d->cfunc != owl_perlconfig_dispatch) {
168      /* don't mess with non-perl dispatch functions from here. */
169      return 1;
170    }
171  }
172
173  d = owl_malloc(sizeof(owl_dispatch));
174  d->fd = fd;
175  d->cfunc = owl_perlconfig_dispatch;
176  d->destroy = owl_perlconfig_dispatch_free;
177  d->data = cb;
178  owl_select_add_dispatch(d);
179  return 0;
180}
181
182int owl_select_remove_perl_dispatch(int fd)
183{
184  int elt;
185  owl_dispatch *d;
186 
187  elt = owl_select_find_dispatch(fd);
188  if (elt != -1) {
189    d = owl_list_get_element(owl_global_get_dispatchlist(&g), elt);
190    if (d->cfunc == owl_perlconfig_dispatch) {
191      owl_select_remove_dispatch_at(elt);
192      return 0;
193    }
194  }
195  return 1;
196}
197
198int owl_select_dispatch_prepare_fd_sets(fd_set *r, fd_set *e)
199{
200  int i, len, max_fd;
201  owl_dispatch *d;
202  const owl_list *dl;
203
204  dl = owl_global_get_dispatchlist(&g);
205  max_fd = 0;
206  len = owl_select_dispatch_count();
207  for(i = 0; i < len; i++) {
208    d = owl_list_get_element(dl, i);
209    FD_SET(d->fd, r);
210    FD_SET(d->fd, e);
211    if (max_fd < d->fd) max_fd = d->fd;
212  }
213  return max_fd + 1;
214}
215
216void owl_select_gc(void)
217{
218  int i;
219  owl_list *dl;
220
221  dl = owl_global_get_dispatchlist(&g);
222  /*
223   * Count down so we aren't set off by removing items from the list
224   * during the iteration.
225   */
226  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
227    const owl_dispatch *d = owl_list_get_element(dl, i);
228    if(d->needs_gc) {
229      owl_select_remove_dispatch_at(i);
230    }
231  }
232}
233
234void owl_select_dispatch(fd_set *fds, int max_fd)
235{
236  int i, len;
237  owl_dispatch *d;
238  const owl_list *dl;
239
240  dl = owl_global_get_dispatchlist(&g);
241  len = owl_select_dispatch_count();
242
243  dispatch_active = 1;
244
245  for(i = 0; i < len; i++) {
246    d = owl_list_get_element(dl, i);
247    /* While d shouldn't normally be null, the list may be altered by
248     * functions we dispatch to. */
249    if (d != NULL && !d->needs_gc && FD_ISSET(d->fd, fds)) {
250      if (d->cfunc != NULL) {
251        d->cfunc(d);
252      }
253    }
254  }
255
256  dispatch_active = 0;
257  owl_select_gc();
258}
259
260static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
261{
262  int i, len;
263  const owl_list *dl;
264  owl_io_dispatch *d;
265  dl = owl_global_get_io_dispatch_list(&g);
266  len = owl_list_get_size(dl);
267  for(i = 0; i < len; i++) {
268    d = owl_list_get_element(dl, i);
269    if (d->fd == fd) return d;
270  }
271  return NULL;
272}
273
274static int owl_select_find_io_dispatch(const owl_io_dispatch *in)
275{
276  int i, len;
277  const owl_list *dl;
278
279  if (in != NULL) {
280    dl = owl_global_get_io_dispatch_list(&g);
281    len = owl_list_get_size(dl);
282    for(i = 0; i < len; i++) {
283      const owl_io_dispatch *d = owl_list_get_element(dl, i);
284      if (d == in) return i;
285    }
286  }
287  return -1;
288}
289
290void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
291{
292  int elt;
293  if (in != NULL) {
294    elt = owl_select_find_io_dispatch(in);
295    if (elt != -1) {
296      owl_list *dl = owl_global_get_io_dispatch_list(&g);
297      owl_io_dispatch *d = owl_list_get_element(dl, elt);
298      if (dispatch_active)
299        d->needs_gc = 1;
300      else {
301        owl_list_remove_element(dl, elt);
302        if (d->destroy)
303          d->destroy(d);
304        owl_free(d);
305      }
306    }
307  }
308}
309
310void owl_select_io_dispatch_gc(void)
311{
312  int i;
313  owl_list *dl;
314
315  dl = owl_global_get_io_dispatch_list(&g);
316  /*
317   * Count down so we aren't set off by removing items from the list
318   * during the iteration.
319   */
320  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
321    owl_io_dispatch *d = owl_list_get_element(dl, i);
322    if(d->needs_gc) {
323      owl_select_remove_io_dispatch(d);
324    }
325  }
326}
327
328/* Each FD may have at most one dispatcher.
329 * If a new dispatch is added for an FD, the old one is removed.
330 * mode determines what types of events are watched for, and may be any combination of:
331 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
332 */
333const 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)
334{
335  owl_io_dispatch *d = owl_malloc(sizeof(owl_io_dispatch));
336  owl_list *dl = owl_global_get_io_dispatch_list(&g);
337
338  d->fd = fd;
339  d->needs_gc = 0;
340  d->mode = mode;
341  d->callback = cb;
342  d->destroy = destroy;
343  d->data = data;
344
345  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
346  owl_list_append_element(dl, d);
347
348  return d;
349}
350
351int owl_select_prepare_io_dispatch_fd_sets(fd_set *rfds, fd_set *wfds, fd_set *efds) {
352  int i, len, max_fd;
353  owl_io_dispatch *d;
354  owl_list *dl = owl_global_get_io_dispatch_list(&g);
355
356  max_fd = 0;
357  len = owl_list_get_size(dl);
358  for (i = 0; i < len; i++) {
359    d = owl_list_get_element(dl, i);
360    if (d->mode & (OWL_IO_READ | OWL_IO_WRITE | OWL_IO_EXCEPT)) {
361      if (max_fd < d->fd) max_fd = d->fd;
362      if (d->mode & OWL_IO_READ) FD_SET(d->fd, rfds);
363      if (d->mode & OWL_IO_WRITE) FD_SET(d->fd, wfds);
364      if (d->mode & OWL_IO_EXCEPT) FD_SET(d->fd, efds);
365    }
366  }
367  return max_fd + 1;
368}
369
370void owl_select_io_dispatch(const fd_set *rfds, const fd_set *wfds, const fd_set *efds, const int max_fd)
371{
372  int i, len;
373  owl_io_dispatch *d;
374  owl_list *dl = owl_global_get_io_dispatch_list(&g);
375
376  dispatch_active = 1;
377  len = owl_list_get_size(dl);
378  for (i = 0; i < len; i++) {
379    d = owl_list_get_element(dl, i);
380    if (d->fd < max_fd && d->callback != NULL &&
381        ((d->mode & OWL_IO_READ && FD_ISSET(d->fd, rfds)) ||
382         (d->mode & OWL_IO_WRITE && FD_ISSET(d->fd, wfds)) ||
383         (d->mode & OWL_IO_EXCEPT && FD_ISSET(d->fd, efds)))) {
384      d->callback(d, d->data);
385    }
386  }
387  dispatch_active = 0;
388  owl_select_io_dispatch_gc();
389}
390
391int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
392{
393  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
394  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
395    /* Don't mess with non-perl dispatch functions from here. */
396    return 1;
397  }
398  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
399  return 0;
400}
401
402int owl_select_remove_perl_io_dispatch(int fd)
403{
404  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
405  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
406    /* Only remove perl io dispatchers from here. */
407    owl_select_remove_io_dispatch(d);
408    return 0;
409  }
410  return 1;
411}
412
413int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
414{
415  aim_conn_t *cur;
416  aim_session_t *sess;
417  int max_fd;
418
419  max_fd = 0;
420  sess = owl_global_get_aimsess(&g);
421  for (cur = sess->connlist, max_fd = 0; cur; cur = cur->next) {
422    if (cur->fd != -1) {
423      FD_SET(cur->fd, rfds);
424      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
425        /* Yes, we're checking writable sockets here. Without it, AIM
426           login is really slow. */
427        FD_SET(cur->fd, wfds);
428      }
429     
430      if (cur->fd > max_fd)
431        max_fd = cur->fd;
432    }
433  }
434  return max_fd;
435}
436
437void owl_process_input_char(owl_input j)
438{
439  int ret;
440  owl_popwin *pw;
441  owl_editwin *tw;
442
443  owl_global_set_lastinputtime(&g, time(NULL));
444  pw=owl_global_get_popwin(&g);
445  tw=owl_global_get_typwin(&g);
446
447  owl_global_set_lastinputtime(&g, time(NULL));
448  /* find and activate the current keymap.
449   * TODO: this should really get fixed by activating
450   * keymaps as we switch between windows...
451   */
452  if (pw && owl_popwin_is_active(pw) && owl_global_get_viewwin(&g)) {
453    owl_context_set_popless(owl_global_get_context(&g), 
454                            owl_global_get_viewwin(&g));
455    owl_function_activate_keymap("popless");
456  } else if (owl_global_is_typwin_active(&g) 
457             && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_ONELINE) {
458    /*
459      owl_context_set_editline(owl_global_get_context(&g), tw);
460      owl_function_activate_keymap("editline");
461    */
462  } else if (owl_global_is_typwin_active(&g) 
463             && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_MULTILINE) {
464    owl_context_set_editmulti(owl_global_get_context(&g), tw);
465    owl_function_activate_keymap("editmulti");
466  } else {
467    owl_context_set_recv(owl_global_get_context(&g));
468    owl_function_activate_keymap("recv");
469  }
470  /* now actually handle the keypress */
471  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
472  if (ret!=0 && ret!=1) {
473    owl_function_makemsg("Unable to handle keypress");
474  }
475}
476
477void owl_select_mask_signals(sigset_t *oldmask) {
478  sigset_t set;
479
480  sigemptyset(&set);
481  sigaddset(&set, SIGINT);
482  sigaddset(&set, SIGTSTP);
483  sigprocmask(SIG_BLOCK, &set, oldmask);
484}
485
486void owl_select_handle_intr(sigset_t *restore)
487{
488  owl_input in;
489
490  owl_global_unset_interrupted(&g);
491
492  sigprocmask(SIG_SETMASK, restore, NULL);
493
494  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
495  owl_process_input_char(in);
496}
497
498void owl_select_check_tstp(void) {
499  if(owl_global_is_sigstp(&g)) {
500    owl_function_makemsg("Use :suspend to suspend.");
501    owl_global_unset_got_sigstp(&g);
502  }
503}
504
505owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
506{
507  owl_ps_action *a = owl_malloc(sizeof(owl_ps_action));
508  owl_list *psa_list = owl_global_get_psa_list(&g);
509  a->needs_gc = 0;
510  a->callback = cb;
511  a->destroy = destroy;
512  a->data = data;
513  owl_list_append_element(psa_list, a);
514  return a;
515}
516
517void owl_select_psa_gc(void)
518{
519  int i;
520  owl_list *psa_list;
521  owl_ps_action *a;
522
523  psa_list = owl_global_get_psa_list(&g);
524  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
525    a = owl_list_get_element(psa_list, i);
526    if (a->needs_gc) {
527      owl_list_remove_element(psa_list, i);
528      if (a->destroy) {
529        a->destroy(a);
530      }
531      owl_free(a);
532    }
533  }
534}
535
536void owl_select_remove_pre_select_action(owl_ps_action *a)
537{
538  a->needs_gc = 1;
539  if (!psa_active)
540    owl_select_psa_gc();
541}
542
543int owl_select_do_pre_select_actions(void)
544{
545  int i, len, ret;
546  owl_list *psa_list;
547
548  psa_active = 1;
549  ret = 0;
550  psa_list = owl_global_get_psa_list(&g);
551  len = owl_list_get_size(psa_list);
552  for (i = 0; i < len; i++) {
553    owl_ps_action *a = owl_list_get_element(psa_list, i);
554    if (a->callback != NULL && a->callback(a, a->data)) {
555      ret = 1;
556    }
557  }
558  psa_active = 0;
559  owl_select_psa_gc();
560  return ret;
561}
562
563void owl_select(void)
564{
565  int i, max_fd, max_fd2, aim_done, ret;
566  fd_set r;
567  fd_set w;
568  fd_set e;
569  fd_set aim_rfds, aim_wfds;
570  struct timespec timeout;
571  sigset_t mask;
572
573  owl_select_process_timers(&timeout);
574
575  owl_select_mask_signals(&mask);
576
577  owl_select_check_tstp();
578  if(owl_global_is_interrupted(&g)) {
579    owl_select_handle_intr(&mask);
580    return;
581  }
582  FD_ZERO(&r);
583  FD_ZERO(&w);
584  FD_ZERO(&e);
585
586  max_fd = owl_select_dispatch_prepare_fd_sets(&r, &e);
587  max_fd2 = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
588  if (max_fd < max_fd2) max_fd = max_fd2;
589
590  /* AIM HACK:
591   *
592   *  The problem - I'm not sure where to hook into the owl/faim
593   *  interface to keep track of when the AIM socket(s) open and
594   *  close. In particular, the bosconn thing throws me off. So,
595   *  rather than register particular dispatchers for AIM, I look up
596   *  the relevant FDs and add them to select's watch lists, then
597   *  check for them individually before moving on to the other
598   *  dispatchers. --asedeno
599   */
600  aim_done = 1;
601  FD_ZERO(&aim_rfds);
602  FD_ZERO(&aim_wfds);
603  if (owl_global_is_doaimevents(&g)) {
604    aim_done = 0;
605    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
606    if (max_fd < max_fd2) max_fd = max_fd2;
607    for(i = 0; i <= max_fd2; i++) {
608      if (FD_ISSET(i, &aim_rfds)) {
609        FD_SET(i, &r);
610        FD_SET(i, &e);
611      }
612      if (FD_ISSET(i, &aim_wfds)) {
613        FD_SET(i, &w);
614        FD_SET(i, &e);
615      }
616    }
617  }
618  /* END AIM HACK */
619
620  if (owl_select_do_pre_select_actions()) {
621    timeout.tv_sec = 0;
622    timeout.tv_nsec = 0;
623  }
624
625  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
626
627  if(ret < 0 && errno == EINTR) {
628    owl_select_check_tstp();
629    if(owl_global_is_interrupted(&g)) {
630      owl_select_handle_intr(NULL);
631    }
632    sigprocmask(SIG_SETMASK, &mask, NULL);
633    return;
634  }
635
636  sigprocmask(SIG_SETMASK, &mask, NULL);
637
638  if(ret > 0) {
639    /* Merge fd_sets and clear AIM FDs. */
640    for(i = 0; i <= max_fd; i++) {
641      /* Merge all interesting FDs into one set, since we have a
642         single dispatch per FD. */
643      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
644        /* AIM HACK: no separate dispatch, just process here if
645           needed, and only once per run through. */
646        if (!aim_done && (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds))) {
647          owl_process_aim();
648          aim_done = 1;
649        }
650        else {
651          FD_SET(i, &r);
652        }
653      }
654    }
655    /* NOTE: the same dispatch function is called for both exceptional
656       and read ready FDs. */
657    owl_select_dispatch(&r, max_fd);
658    owl_select_io_dispatch(&r, &w, &e, max_fd);
659  }
660}
Note: See TracBrowser for help on using the repository browser.