source: select.c @ bdbec0a

release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since bdbec0a was 4f2166b, checked in by Alejandro R. Sedeño <asedeno@mit.edu>, 12 years ago
Add a pre-select action list. Allow us to add actions that should be performed before calling pselect(). If the action performs any useful work, it should return non-zero. If any of the actions return a non-zero value, the timeout for pselect() will be set to 0s, so that we can process all of these actions again in case they are interacting with one another, while still keeping an eye on our file descriptors and timers.
  • Property mode set to 100644
File size: 12.0 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  FD_ZERO(r);
206  FD_ZERO(e);
207  max_fd = 0;
208  len = owl_select_dispatch_count();
209  for(i = 0; i < len; i++) {
210    d = owl_list_get_element(dl, i);
211    FD_SET(d->fd, r);
212    FD_SET(d->fd, e);
213    if (max_fd < d->fd) max_fd = d->fd;
214  }
215  return max_fd + 1;
216}
217
218void owl_select_gc(void)
219{
220  int i;
221  owl_list *dl;
222
223  dl = owl_global_get_dispatchlist(&g);
224  /*
225   * Count down so we aren't set off by removing items from the list
226   * during the iteration.
227   */
228  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
229    const owl_dispatch *d = owl_list_get_element(dl, i);
230    if(d->needs_gc) {
231      owl_select_remove_dispatch_at(i);
232    }
233  }
234}
235
236void owl_select_dispatch(fd_set *fds, int max_fd)
237{
238  int i, len;
239  owl_dispatch *d;
240  const owl_list *dl;
241
242  dl = owl_global_get_dispatchlist(&g);
243  len = owl_select_dispatch_count();
244
245  dispatch_active = 1;
246
247  for(i = 0; i < len; i++) {
248    d = owl_list_get_element(dl, i);
249    /* While d shouldn't normally be null, the list may be altered by
250     * functions we dispatch to. */
251    if (d != NULL && !d->needs_gc && FD_ISSET(d->fd, fds)) {
252      if (d->cfunc != NULL) {
253        d->cfunc(d);
254      }
255    }
256  }
257
258  dispatch_active = 0;
259  owl_select_gc();
260}
261
262int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
263{
264  aim_conn_t *cur;
265  aim_session_t *sess;
266  int max_fd;
267
268  FD_ZERO(rfds);
269  FD_ZERO(wfds);
270  max_fd = 0;
271  sess = owl_global_get_aimsess(&g);
272  for (cur = sess->connlist, max_fd = 0; cur; cur = cur->next) {
273    if (cur->fd != -1) {
274      FD_SET(cur->fd, rfds);
275      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
276        /* Yes, we're checking writable sockets here. Without it, AIM
277           login is really slow. */
278        FD_SET(cur->fd, wfds);
279      }
280     
281      if (cur->fd > max_fd)
282        max_fd = cur->fd;
283    }
284  }
285  return max_fd;
286}
287
288void owl_process_input_char(owl_input j)
289{
290  int ret;
291  owl_popwin *pw;
292  owl_editwin *tw;
293
294  owl_global_set_lastinputtime(&g, time(NULL));
295  pw=owl_global_get_popwin(&g);
296  tw=owl_global_get_typwin(&g);
297
298  owl_global_set_lastinputtime(&g, time(NULL));
299  /* find and activate the current keymap.
300   * TODO: this should really get fixed by activating
301   * keymaps as we switch between windows...
302   */
303  if (pw && owl_popwin_is_active(pw) && owl_global_get_viewwin(&g)) {
304    owl_context_set_popless(owl_global_get_context(&g), 
305                            owl_global_get_viewwin(&g));
306    owl_function_activate_keymap("popless");
307  } else if (owl_global_is_typwin_active(&g) 
308             && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_ONELINE) {
309    /*
310      owl_context_set_editline(owl_global_get_context(&g), tw);
311      owl_function_activate_keymap("editline");
312    */
313  } else if (owl_global_is_typwin_active(&g) 
314             && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_MULTILINE) {
315    owl_context_set_editmulti(owl_global_get_context(&g), tw);
316    owl_function_activate_keymap("editmulti");
317  } else {
318    owl_context_set_recv(owl_global_get_context(&g));
319    owl_function_activate_keymap("recv");
320  }
321  /* now actually handle the keypress */
322  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
323  if (ret!=0 && ret!=1) {
324    owl_function_makemsg("Unable to handle keypress");
325  }
326}
327
328void owl_select_mask_signals(sigset_t *oldmask) {
329  sigset_t set;
330
331  sigemptyset(&set);
332  sigaddset(&set, SIGINT);
333  sigaddset(&set, SIGTSTP);
334  sigprocmask(SIG_BLOCK, &set, oldmask);
335}
336
337void owl_select_handle_intr(sigset_t *restore)
338{
339  owl_input in;
340
341  owl_global_unset_interrupted(&g);
342
343  sigprocmask(SIG_SETMASK, restore, NULL);
344
345  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
346  owl_process_input_char(in);
347}
348
349void owl_select_check_tstp(void) {
350  if(owl_global_is_sigstp(&g)) {
351    owl_function_makemsg("Use :suspend to suspend.");
352    owl_global_unset_got_sigstp(&g);
353  }
354}
355
356owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
357{
358  owl_ps_action *a = owl_malloc(sizeof(owl_ps_action));
359  owl_list *psa_list = owl_global_get_psa_list(&g);
360  a->needs_gc = 0;
361  a->callback = cb;
362  a->destroy = destroy;
363  a->data = data;
364  owl_list_append_element(psa_list, a);
365  return a;
366}
367
368void owl_select_psa_gc(void)
369{
370  int i;
371  owl_list *psa_list;
372  owl_ps_action *a;
373
374  psa_list = owl_global_get_psa_list(&g);
375  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
376    a = owl_list_get_element(psa_list, i);
377    if (a->needs_gc) {
378      owl_list_remove_element(psa_list, i);
379      if (a->destroy) {
380        a->destroy(a);
381      }
382      owl_free(a);
383    }
384  }
385}
386
387void owl_select_remove_pre_select_action(owl_ps_action *a)
388{
389  a->needs_gc = 1;
390  if (!psa_active)
391    owl_select_psa_gc();
392}
393
394int owl_select_do_pre_select_actions(void)
395{
396  int i, len, ret;
397  owl_list *psa_list;
398
399  psa_active = 1;
400  ret = 0;
401  psa_list = owl_global_get_psa_list(&g);
402  len = owl_list_get_size(psa_list);
403  for (i = 0; i < len; i++) {
404    owl_ps_action *a = owl_list_get_element(psa_list, i);
405    if (a->callback != NULL && a->callback(a, a->data)) {
406      ret = 1;
407    }
408  }
409  psa_active = 0;
410  owl_select_psa_gc();
411  return ret;
412}
413
414void owl_select(void)
415{
416  int i, max_fd, aim_max_fd, aim_done, ret;
417  fd_set r;
418  fd_set e;
419  fd_set aim_rfds, aim_wfds;
420  struct timespec timeout;
421  sigset_t mask;
422
423  owl_select_process_timers(&timeout);
424
425  owl_select_mask_signals(&mask);
426
427  owl_select_check_tstp();
428  if(owl_global_is_interrupted(&g)) {
429    owl_select_handle_intr(&mask);
430    return;
431  }
432
433  max_fd = owl_select_dispatch_prepare_fd_sets(&r, &e);
434
435  /* AIM HACK:
436   *
437   *  The problem - I'm not sure where to hook into the owl/faim
438   *  interface to keep track of when the AIM socket(s) open and
439   *  close. In particular, the bosconn thing throws me off. So,
440   *  rather than register particular dispatchers for AIM, I look up
441   *  the relevant FDs and add them to select's watch lists, then
442   *  check for them individually before moving on to the other
443   *  dispatchers. --asedeno
444   */
445  aim_done = 1;
446  FD_ZERO(&aim_rfds);
447  FD_ZERO(&aim_wfds);
448  if (owl_global_is_doaimevents(&g)) {
449    aim_done = 0;
450    aim_max_fd = owl_select_aim_hack(&aim_rfds, &aim_wfds);
451    if (max_fd < aim_max_fd) max_fd = aim_max_fd;
452    for(i = 0; i <= aim_max_fd; i++) {
453      if (FD_ISSET(i, &aim_rfds)) {
454        FD_SET(i, &r);
455        FD_SET(i, &e);
456      }
457    }
458  }
459  /* END AIM HACK */
460
461  if (owl_select_do_pre_select_actions()) {
462    timeout.tv_sec = 0;
463    timeout.tv_nsec = 0;
464  }
465
466  ret = pselect(max_fd+1, &r, &aim_wfds, &e, &timeout, &mask);
467
468  if(ret < 0 && errno == EINTR) {
469    owl_select_check_tstp();
470    if(owl_global_is_interrupted(&g)) {
471      owl_select_handle_intr(NULL);
472    }
473    sigprocmask(SIG_SETMASK, &mask, NULL);
474    return;
475  }
476
477  sigprocmask(SIG_SETMASK, &mask, NULL);
478
479  if(ret > 0) {
480    /* Merge fd_sets and clear AIM FDs. */
481    for(i = 0; i <= max_fd; i++) {
482      /* Merge all interesting FDs into one set, since we have a
483         single dispatch per FD. */
484      if (FD_ISSET(i, &r) || FD_ISSET(i, &aim_wfds) || FD_ISSET(i, &e)) {
485        /* AIM HACK: no separate dispatch, just process here if
486           needed, and only once per run through. */
487        if (!aim_done && (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds))) {
488          owl_process_aim();
489          aim_done = 1;
490        }
491        else {
492          FD_SET(i, &r);
493        }
494      }
495    }
496    /* NOTE: the same dispatch function is called for both exceptional
497       and read ready FDs. */
498    owl_select_dispatch(&r, max_fd);
499  }
500}
Note: See TracBrowser for help on using the repository browser.