source: select.c @ 60fcd71

release-1.6release-1.7release-1.8release-1.9
Last change on this file since 60fcd71 was 2a17b63, checked in by Nelson Elhage <nelhage@mit.edu>, 12 years ago
Push and pop contexts whenever we change context. This allows us to get rid of the awful hack in owl_process_input_char, and opens the path to being able to invoke popup windows from the editwin and vice versa (In fact, you can now do so without segfaulting, although the display layer does not yet quite do the right thing).
  • Property mode set to 100644
File size: 10.6 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
83static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
84{
85  int i, len;
86  const owl_list *dl;
87  owl_io_dispatch *d;
88  dl = owl_global_get_io_dispatch_list(&g);
89  len = owl_list_get_size(dl);
90  for(i = 0; i < len; i++) {
91    d = owl_list_get_element(dl, i);
92    if (d->fd == fd) return d;
93  }
94  return NULL;
95}
96
97static int owl_select_find_io_dispatch(const owl_io_dispatch *in)
98{
99  int i, len;
100  const owl_list *dl;
101
102  if (in != NULL) {
103    dl = owl_global_get_io_dispatch_list(&g);
104    len = owl_list_get_size(dl);
105    for(i = 0; i < len; i++) {
106      const owl_io_dispatch *d = owl_list_get_element(dl, i);
107      if (d == in) return i;
108    }
109  }
110  return -1;
111}
112
113void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
114{
115  int elt;
116  if (in != NULL) {
117    elt = owl_select_find_io_dispatch(in);
118    if (elt != -1) {
119      owl_list *dl = owl_global_get_io_dispatch_list(&g);
120      owl_io_dispatch *d = owl_list_get_element(dl, elt);
121      if (dispatch_active)
122        d->needs_gc = 1;
123      else {
124        owl_list_remove_element(dl, elt);
125        if (d->destroy)
126          d->destroy(d);
127        owl_free(d);
128      }
129    }
130  }
131}
132
133void owl_select_io_dispatch_gc(void)
134{
135  int i;
136  owl_list *dl;
137
138  dl = owl_global_get_io_dispatch_list(&g);
139  /*
140   * Count down so we aren't set off by removing items from the list
141   * during the iteration.
142   */
143  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
144    owl_io_dispatch *d = owl_list_get_element(dl, i);
145    if(d->needs_gc) {
146      owl_select_remove_io_dispatch(d);
147    }
148  }
149}
150
151/* Each FD may have at most one dispatcher.
152 * If a new dispatch is added for an FD, the old one is removed.
153 * mode determines what types of events are watched for, and may be any combination of:
154 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
155 */
156const 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)
157{
158  owl_io_dispatch *d = owl_malloc(sizeof(owl_io_dispatch));
159  owl_list *dl = owl_global_get_io_dispatch_list(&g);
160
161  d->fd = fd;
162  d->needs_gc = 0;
163  d->mode = mode;
164  d->callback = cb;
165  d->destroy = destroy;
166  d->data = data;
167
168  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
169  owl_list_append_element(dl, d);
170
171  return d;
172}
173
174int owl_select_prepare_io_dispatch_fd_sets(fd_set *rfds, fd_set *wfds, fd_set *efds) {
175  int i, len, max_fd;
176  owl_io_dispatch *d;
177  owl_list *dl = owl_global_get_io_dispatch_list(&g);
178
179  max_fd = 0;
180  len = owl_list_get_size(dl);
181  for (i = 0; i < len; i++) {
182    d = owl_list_get_element(dl, i);
183    if (d->mode & (OWL_IO_READ | OWL_IO_WRITE | OWL_IO_EXCEPT)) {
184      if (max_fd < d->fd) max_fd = d->fd;
185      if (d->mode & OWL_IO_READ) FD_SET(d->fd, rfds);
186      if (d->mode & OWL_IO_WRITE) FD_SET(d->fd, wfds);
187      if (d->mode & OWL_IO_EXCEPT) FD_SET(d->fd, efds);
188    }
189  }
190  return max_fd + 1;
191}
192
193void owl_select_io_dispatch(const fd_set *rfds, const fd_set *wfds, const fd_set *efds, const int max_fd)
194{
195  int i, len;
196  owl_io_dispatch *d;
197  owl_list *dl = owl_global_get_io_dispatch_list(&g);
198
199  dispatch_active = 1;
200  len = owl_list_get_size(dl);
201  for (i = 0; i < len; i++) {
202    d = owl_list_get_element(dl, i);
203    if (d->fd < max_fd && d->callback != NULL &&
204        ((d->mode & OWL_IO_READ && FD_ISSET(d->fd, rfds)) ||
205         (d->mode & OWL_IO_WRITE && FD_ISSET(d->fd, wfds)) ||
206         (d->mode & OWL_IO_EXCEPT && FD_ISSET(d->fd, efds)))) {
207      d->callback(d, d->data);
208    }
209  }
210  dispatch_active = 0;
211  owl_select_io_dispatch_gc();
212}
213
214int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
215{
216  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
217  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
218    /* Don't mess with non-perl dispatch functions from here. */
219    return 1;
220  }
221  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
222  return 0;
223}
224
225int owl_select_remove_perl_io_dispatch(int fd)
226{
227  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
228  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
229    /* Only remove perl io dispatchers from here. */
230    owl_select_remove_io_dispatch(d);
231    return 0;
232  }
233  return 1;
234}
235
236int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
237{
238  aim_conn_t *cur;
239  aim_session_t *sess;
240  int max_fd;
241
242  max_fd = 0;
243  sess = owl_global_get_aimsess(&g);
244  for (cur = sess->connlist, max_fd = 0; cur; cur = cur->next) {
245    if (cur->fd != -1) {
246      FD_SET(cur->fd, rfds);
247      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
248        /* Yes, we're checking writable sockets here. Without it, AIM
249           login is really slow. */
250        FD_SET(cur->fd, wfds);
251      }
252     
253      if (cur->fd > max_fd)
254        max_fd = cur->fd;
255    }
256  }
257  return max_fd;
258}
259
260void owl_process_input_char(owl_input j)
261{
262  int ret;
263
264  owl_global_set_lastinputtime(&g, time(NULL));
265  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
266  if (ret!=0 && ret!=1) {
267    owl_function_makemsg("Unable to handle keypress");
268  }
269}
270
271void owl_select_mask_signals(sigset_t *oldmask) {
272  sigset_t set;
273
274  sigemptyset(&set);
275  sigaddset(&set, SIGINT);
276  sigprocmask(SIG_BLOCK, &set, oldmask);
277}
278
279void owl_select_handle_intr(sigset_t *restore)
280{
281  owl_input in;
282
283  owl_global_unset_interrupted(&g);
284
285  sigprocmask(SIG_SETMASK, restore, NULL);
286
287  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
288  owl_process_input_char(in);
289}
290
291owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
292{
293  owl_ps_action *a = owl_malloc(sizeof(owl_ps_action));
294  owl_list *psa_list = owl_global_get_psa_list(&g);
295  a->needs_gc = 0;
296  a->callback = cb;
297  a->destroy = destroy;
298  a->data = data;
299  owl_list_append_element(psa_list, a);
300  return a;
301}
302
303void owl_select_psa_gc(void)
304{
305  int i;
306  owl_list *psa_list;
307  owl_ps_action *a;
308
309  psa_list = owl_global_get_psa_list(&g);
310  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
311    a = owl_list_get_element(psa_list, i);
312    if (a->needs_gc) {
313      owl_list_remove_element(psa_list, i);
314      if (a->destroy) {
315        a->destroy(a);
316      }
317      owl_free(a);
318    }
319  }
320}
321
322void owl_select_remove_pre_select_action(owl_ps_action *a)
323{
324  a->needs_gc = 1;
325  if (!psa_active)
326    owl_select_psa_gc();
327}
328
329int owl_select_do_pre_select_actions(void)
330{
331  int i, len, ret;
332  owl_list *psa_list;
333
334  psa_active = 1;
335  ret = 0;
336  psa_list = owl_global_get_psa_list(&g);
337  len = owl_list_get_size(psa_list);
338  for (i = 0; i < len; i++) {
339    owl_ps_action *a = owl_list_get_element(psa_list, i);
340    if (a->callback != NULL && a->callback(a, a->data)) {
341      ret = 1;
342    }
343  }
344  psa_active = 0;
345  owl_select_psa_gc();
346  return ret;
347}
348
349void owl_select(void)
350{
351  int i, max_fd, max_fd2, aim_done, ret;
352  fd_set r;
353  fd_set w;
354  fd_set e;
355  fd_set aim_rfds, aim_wfds;
356  struct timespec timeout;
357  sigset_t mask;
358
359  owl_select_process_timers(&timeout);
360
361  owl_select_mask_signals(&mask);
362
363  if(owl_global_is_interrupted(&g)) {
364    owl_select_handle_intr(&mask);
365    return;
366  }
367  FD_ZERO(&r);
368  FD_ZERO(&w);
369  FD_ZERO(&e);
370
371  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
372
373  /* AIM HACK:
374   *
375   *  The problem - I'm not sure where to hook into the owl/faim
376   *  interface to keep track of when the AIM socket(s) open and
377   *  close. In particular, the bosconn thing throws me off. So,
378   *  rather than register particular dispatchers for AIM, I look up
379   *  the relevant FDs and add them to select's watch lists, then
380   *  check for them individually before moving on to the other
381   *  dispatchers. --asedeno
382   */
383  aim_done = 1;
384  FD_ZERO(&aim_rfds);
385  FD_ZERO(&aim_wfds);
386  if (owl_global_is_doaimevents(&g)) {
387    aim_done = 0;
388    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
389    if (max_fd < max_fd2) max_fd = max_fd2;
390    for(i = 0; i <= max_fd2; i++) {
391      if (FD_ISSET(i, &aim_rfds)) {
392        FD_SET(i, &r);
393        FD_SET(i, &e);
394      }
395      if (FD_ISSET(i, &aim_wfds)) {
396        FD_SET(i, &w);
397        FD_SET(i, &e);
398      }
399    }
400  }
401  /* END AIM HACK */
402
403  if (owl_select_do_pre_select_actions()) {
404    timeout.tv_sec = 0;
405    timeout.tv_nsec = 0;
406  }
407
408  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
409
410  if(ret < 0 && errno == EINTR) {
411    if(owl_global_is_interrupted(&g)) {
412      owl_select_handle_intr(NULL);
413    }
414    sigprocmask(SIG_SETMASK, &mask, NULL);
415    return;
416  }
417
418  sigprocmask(SIG_SETMASK, &mask, NULL);
419
420  if(ret > 0) {
421    /* AIM HACK: process all AIM events at once. */
422    for(i = 0; !aim_done && i <= max_fd; i++) {
423      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
424        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
425          owl_process_aim();
426          aim_done = 1;
427        }
428      }
429    }
430    owl_select_io_dispatch(&r, &w, &e, max_fd);
431  }
432}
Note: See TracBrowser for help on using the repository browser.