source: select.c @ 8ba9313

release-1.10release-1.8release-1.9
Last change on this file since 8ba9313 was fb96152, checked in by Nelson Elhage <nelhage@mit.edu>, 14 years ago
Don't die horribly if perl registers a dispatch and then closes the fd. Well-behaved perl code should of course remove the dispatch when it closes the fd, but there's no reason not to be robust to this failure mode.
  • Property mode set to 100644
File size: 11.5 KB
Line 
1#include "owl.h"
2#include <sys/stat.h>
3
4static int dispatch_active = 0;
5static int psa_active = 0;
6static int loop_active = 0;
7
8int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
9  return t1->time - t2->time;
10}
11
12int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
13  return t1 == t2;
14}
15
16owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
17{
18  owl_timer *t = g_new(owl_timer, 1);
19  GList **timers = owl_global_get_timerlist(&g);
20
21  t->time = time(NULL) + after;
22  t->interval = interval;
23  t->callback = cb;
24  t->destroy = destroy;
25  t->data = data;
26  t->name = name ? g_strdup(name) : NULL;
27
28  *timers = g_list_insert_sorted(*timers, t,
29                                 (GCompareFunc)_owl_select_timer_cmp);
30  return t;
31}
32
33void owl_select_remove_timer(owl_timer *t)
34{
35  GList **timers = owl_global_get_timerlist(&g);
36  if (t && g_list_find(*timers, t)) {
37    *timers = g_list_remove(*timers, t);
38    if(t->destroy) {
39      t->destroy(t);
40    }
41    g_free(t->name);
42    g_free(t);
43  }
44}
45
46void owl_select_process_timers(struct timespec *timeout)
47{
48  time_t now = time(NULL);
49  GList **timers = owl_global_get_timerlist(&g);
50
51  while(*timers) {
52    owl_timer *t = (*timers)->data;
53    int remove = 0;
54
55    if(t->time > now)
56      break;
57
58    /* Reschedule if appropriate */
59    if(t->interval > 0) {
60      t->time = now + t->interval;
61      *timers = g_list_remove(*timers, t);
62      *timers = g_list_insert_sorted(*timers, t,
63                                     (GCompareFunc)_owl_select_timer_cmp);
64    } else {
65      remove = 1;
66    }
67
68    /* Do the callback */
69    t->callback(t, t->data);
70    if(remove) {
71      owl_select_remove_timer(t);
72    }
73  }
74
75  if(*timers) {
76    owl_timer *t = (*timers)->data;
77    timeout->tv_sec = t->time - now;
78    if (timeout->tv_sec > 60)
79      timeout->tv_sec = 60;
80  } else {
81    timeout->tv_sec = 60;
82  }
83
84  timeout->tv_nsec = 0;
85}
86
87static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
88{
89  int i, len;
90  const owl_list *dl;
91  owl_io_dispatch *d;
92  dl = owl_global_get_io_dispatch_list(&g);
93  len = owl_list_get_size(dl);
94  for(i = 0; i < len; i++) {
95    d = owl_list_get_element(dl, i);
96    if (d->fd == fd) return d;
97  }
98  return NULL;
99}
100
101static int owl_select_find_io_dispatch(const owl_io_dispatch *in)
102{
103  int i, len;
104  const owl_list *dl;
105
106  if (in != NULL) {
107    dl = owl_global_get_io_dispatch_list(&g);
108    len = owl_list_get_size(dl);
109    for(i = 0; i < len; i++) {
110      const owl_io_dispatch *d = owl_list_get_element(dl, i);
111      if (d == in) return i;
112    }
113  }
114  return -1;
115}
116
117void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
118{
119  int elt;
120  if (in != NULL) {
121    elt = owl_select_find_io_dispatch(in);
122    if (elt != -1) {
123      owl_list *dl = owl_global_get_io_dispatch_list(&g);
124      owl_io_dispatch *d = owl_list_get_element(dl, elt);
125      if (dispatch_active)
126        d->needs_gc = 1;
127      else {
128        owl_list_remove_element(dl, elt);
129        if (d->destroy)
130          d->destroy(d);
131        g_free(d);
132      }
133    }
134  }
135}
136
137void owl_select_io_dispatch_gc(void)
138{
139  int i;
140  owl_list *dl;
141
142  dl = owl_global_get_io_dispatch_list(&g);
143  /*
144   * Count down so we aren't set off by removing items from the list
145   * during the iteration.
146   */
147  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
148    owl_io_dispatch *d = owl_list_get_element(dl, i);
149    if(d->needs_gc) {
150      owl_select_remove_io_dispatch(d);
151    }
152  }
153}
154
155/* Each FD may have at most one dispatcher.
156 * If a new dispatch is added for an FD, the old one is removed.
157 * mode determines what types of events are watched for, and may be any combination of:
158 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
159 */
160const 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)
161{
162  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
163  owl_list *dl = owl_global_get_io_dispatch_list(&g);
164
165  d->fd = fd;
166  d->needs_gc = 0;
167  d->mode = mode;
168  d->callback = cb;
169  d->destroy = destroy;
170  d->data = data;
171
172  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
173  owl_list_append_element(dl, d);
174
175  return d;
176}
177
178int owl_select_prepare_io_dispatch_fd_sets(fd_set *rfds, fd_set *wfds, fd_set *efds) {
179  int i, len, max_fd;
180  owl_io_dispatch *d;
181  owl_list *dl = owl_global_get_io_dispatch_list(&g);
182
183  max_fd = 0;
184  len = owl_list_get_size(dl);
185  for (i = 0; i < len; i++) {
186    d = owl_list_get_element(dl, i);
187    if (d->mode & (OWL_IO_READ | OWL_IO_WRITE | OWL_IO_EXCEPT)) {
188      if (max_fd < d->fd) max_fd = d->fd;
189      if (d->mode & OWL_IO_READ) FD_SET(d->fd, rfds);
190      if (d->mode & OWL_IO_WRITE) FD_SET(d->fd, wfds);
191      if (d->mode & OWL_IO_EXCEPT) FD_SET(d->fd, efds);
192    }
193  }
194  return max_fd + 1;
195}
196
197void owl_select_io_dispatch(const fd_set *rfds, const fd_set *wfds, const fd_set *efds, const int max_fd)
198{
199  int i, len;
200  owl_io_dispatch *d;
201  owl_list *dl = owl_global_get_io_dispatch_list(&g);
202
203  dispatch_active = 1;
204  len = owl_list_get_size(dl);
205  for (i = 0; i < len; i++) {
206    d = owl_list_get_element(dl, i);
207    if (d->fd < max_fd && d->callback != NULL &&
208        ((d->mode & OWL_IO_READ && FD_ISSET(d->fd, rfds)) ||
209         (d->mode & OWL_IO_WRITE && FD_ISSET(d->fd, wfds)) ||
210         (d->mode & OWL_IO_EXCEPT && FD_ISSET(d->fd, efds)))) {
211      d->callback(d, d->data);
212    }
213  }
214  dispatch_active = 0;
215  owl_select_io_dispatch_gc();
216}
217
218int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
219{
220  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
221  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
222    /* Don't mess with non-perl dispatch functions from here. */
223    return 1;
224  }
225  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
226  return 0;
227}
228
229int owl_select_remove_perl_io_dispatch(int fd)
230{
231  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
232  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
233    /* Only remove perl io dispatchers from here. */
234    owl_select_remove_io_dispatch(d);
235    return 0;
236  }
237  return 1;
238}
239
240int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
241{
242  aim_conn_t *cur;
243  aim_session_t *sess;
244  int max_fd;
245
246  max_fd = 0;
247  sess = owl_global_get_aimsess(&g);
248  for (cur = sess->connlist; cur; cur = cur->next) {
249    if (cur->fd != -1) {
250      FD_SET(cur->fd, rfds);
251      if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
252        /* Yes, we're checking writable sockets here. Without it, AIM
253           login is really slow. */
254        FD_SET(cur->fd, wfds);
255      }
256     
257      if (cur->fd > max_fd)
258        max_fd = cur->fd;
259    }
260  }
261  return max_fd;
262}
263
264void owl_process_input_char(owl_input j)
265{
266  int ret;
267
268  owl_global_set_lastinputtime(&g, time(NULL));
269  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
270  if (ret!=0 && ret!=1) {
271    owl_function_makemsg("Unable to handle keypress");
272  }
273}
274
275void owl_select_mask_signals(sigset_t *oldmask) {
276  sigset_t set;
277
278  sigemptyset(&set);
279  sigaddset(&set, SIGWINCH);
280  sigaddset(&set, SIGALRM);
281  sigaddset(&set, SIGPIPE);
282  sigaddset(&set, SIGTERM);
283  sigaddset(&set, SIGHUP);
284  sigaddset(&set, SIGINT);
285  sigprocmask(SIG_BLOCK, &set, oldmask);
286}
287
288void owl_select_handle_intr(sigset_t *restore)
289{
290  owl_input in;
291
292  owl_global_unset_interrupted(&g);
293
294  sigprocmask(SIG_SETMASK, restore, NULL);
295
296  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
297  owl_process_input_char(in);
298}
299
300owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
301{
302  owl_ps_action *a = g_new(owl_ps_action, 1);
303  owl_list *psa_list = owl_global_get_psa_list(&g);
304  a->needs_gc = 0;
305  a->callback = cb;
306  a->destroy = destroy;
307  a->data = data;
308  owl_list_append_element(psa_list, a);
309  return a;
310}
311
312void owl_select_psa_gc(void)
313{
314  int i;
315  owl_list *psa_list;
316  owl_ps_action *a;
317
318  psa_list = owl_global_get_psa_list(&g);
319  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
320    a = owl_list_get_element(psa_list, i);
321    if (a->needs_gc) {
322      owl_list_remove_element(psa_list, i);
323      if (a->destroy) {
324        a->destroy(a);
325      }
326      g_free(a);
327    }
328  }
329}
330
331void owl_select_remove_pre_select_action(owl_ps_action *a)
332{
333  a->needs_gc = 1;
334  if (!psa_active)
335    owl_select_psa_gc();
336}
337
338int owl_select_do_pre_select_actions(void)
339{
340  int i, len, ret;
341  owl_list *psa_list;
342
343  psa_active = 1;
344  ret = 0;
345  psa_list = owl_global_get_psa_list(&g);
346  len = owl_list_get_size(psa_list);
347  for (i = 0; i < len; i++) {
348    owl_ps_action *a = owl_list_get_element(psa_list, i);
349    if (a->callback != NULL && a->callback(a, a->data)) {
350      ret = 1;
351    }
352  }
353  psa_active = 0;
354  owl_select_psa_gc();
355  return ret;
356}
357
358static void owl_select_prune_bad_fds(void) {
359  owl_list *dl = owl_global_get_io_dispatch_list(&g);
360  int len, i;
361  struct stat st;
362  owl_io_dispatch *d;
363
364  len = owl_list_get_size(dl);
365  for (i = 0; i < len; i++) {
366    d = owl_list_get_element(dl, i);
367    if (fstat(d->fd, &st) < 0 && errno == EBADF) {
368      owl_function_debugmsg("Pruning defunct dispatch on fd %d.", d->fd);
369      d->needs_gc = 1;
370    }
371  }
372  owl_select_io_dispatch_gc();
373}
374
375void owl_select(void)
376{
377  int i, max_fd, max_fd2, aim_done, ret;
378  fd_set r;
379  fd_set w;
380  fd_set e;
381  fd_set aim_rfds, aim_wfds;
382  struct timespec timeout;
383  sigset_t mask;
384
385  owl_select_process_timers(&timeout);
386
387  owl_select_mask_signals(&mask);
388
389  if(owl_global_is_interrupted(&g)) {
390    owl_select_handle_intr(&mask);
391    return;
392  }
393  FD_ZERO(&r);
394  FD_ZERO(&w);
395  FD_ZERO(&e);
396
397  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
398
399  /* AIM HACK:
400   *
401   *  The problem - I'm not sure where to hook into the owl/faim
402   *  interface to keep track of when the AIM socket(s) open and
403   *  close. In particular, the bosconn thing throws me off. So,
404   *  rather than register particular dispatchers for AIM, I look up
405   *  the relevant FDs and add them to select's watch lists, then
406   *  check for them individually before moving on to the other
407   *  dispatchers. --asedeno
408   */
409  aim_done = 1;
410  FD_ZERO(&aim_rfds);
411  FD_ZERO(&aim_wfds);
412  if (owl_global_is_doaimevents(&g)) {
413    aim_done = 0;
414    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
415    if (max_fd < max_fd2) max_fd = max_fd2;
416    for(i = 0; i <= max_fd2; i++) {
417      if (FD_ISSET(i, &aim_rfds)) {
418        FD_SET(i, &r);
419        FD_SET(i, &e);
420      }
421      if (FD_ISSET(i, &aim_wfds)) {
422        FD_SET(i, &w);
423        FD_SET(i, &e);
424      }
425    }
426  }
427  /* END AIM HACK */
428
429  if (owl_select_do_pre_select_actions()) {
430    timeout.tv_sec = 0;
431    timeout.tv_nsec = 0;
432  }
433
434  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
435
436  if(ret < 0) {
437    if (errno == EINTR) {
438      if(owl_global_is_interrupted(&g)) {
439        owl_select_handle_intr(NULL);
440      }
441    } else if (errno == EBADF) {
442      /* Perl must have closed an fd on us without removing it first. */
443      owl_select_prune_bad_fds();
444    }
445    sigprocmask(SIG_SETMASK, &mask, NULL);
446    return;
447  }
448
449  sigprocmask(SIG_SETMASK, &mask, NULL);
450
451  if(ret > 0) {
452    /* AIM HACK: process all AIM events at once. */
453    for(i = 0; !aim_done && i <= max_fd; i++) {
454      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
455        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
456          owl_process_aim();
457          aim_done = 1;
458        }
459      }
460    }
461    owl_select_io_dispatch(&r, &w, &e, max_fd);
462  }
463}
464
465void owl_select_run_loop(void)
466{
467  loop_active = 1;
468  while (loop_active) {
469    owl_select();
470  }
471}
472
473void owl_select_quit_loop(void)
474{
475  loop_active = 0;
476}
Note: See TracBrowser for help on using the repository browser.