source: select.c @ d6f2d21

release-1.7release-1.8release-1.9
Last change on this file since d6f2d21 was c3031f3, checked in by Anders Kaseorg <andersk@mit.edu>, 11 years ago
Mask all handled signals while running pre-select actions. This closes the race condition where a signal could be delivered just before the pselect() call, which hangs until the next event. Signed-off-by: Anders Kaseorg <andersk@mit.edu> Reviewed-by: Nelson Elhage <nelhage@mit.edu>
  • Property mode set to 100644
File size: 10.7 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, SIGWINCH);
276  sigaddset(&set, SIGALRM);
277  sigaddset(&set, SIGPIPE);
278  sigaddset(&set, SIGTERM);
279  sigaddset(&set, SIGHUP);
280  sigaddset(&set, SIGINT);
281  sigprocmask(SIG_BLOCK, &set, oldmask);
282}
283
284void owl_select_handle_intr(sigset_t *restore)
285{
286  owl_input in;
287
288  owl_global_unset_interrupted(&g);
289
290  sigprocmask(SIG_SETMASK, restore, NULL);
291
292  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
293  owl_process_input_char(in);
294}
295
296owl_ps_action *owl_select_add_pre_select_action(int (*cb)(owl_ps_action *, void *), void (*destroy)(owl_ps_action *), void *data)
297{
298  owl_ps_action *a = owl_malloc(sizeof(owl_ps_action));
299  owl_list *psa_list = owl_global_get_psa_list(&g);
300  a->needs_gc = 0;
301  a->callback = cb;
302  a->destroy = destroy;
303  a->data = data;
304  owl_list_append_element(psa_list, a);
305  return a;
306}
307
308void owl_select_psa_gc(void)
309{
310  int i;
311  owl_list *psa_list;
312  owl_ps_action *a;
313
314  psa_list = owl_global_get_psa_list(&g);
315  for (i = owl_list_get_size(psa_list) - 1; i >= 0; i--) {
316    a = owl_list_get_element(psa_list, i);
317    if (a->needs_gc) {
318      owl_list_remove_element(psa_list, i);
319      if (a->destroy) {
320        a->destroy(a);
321      }
322      owl_free(a);
323    }
324  }
325}
326
327void owl_select_remove_pre_select_action(owl_ps_action *a)
328{
329  a->needs_gc = 1;
330  if (!psa_active)
331    owl_select_psa_gc();
332}
333
334int owl_select_do_pre_select_actions(void)
335{
336  int i, len, ret;
337  owl_list *psa_list;
338
339  psa_active = 1;
340  ret = 0;
341  psa_list = owl_global_get_psa_list(&g);
342  len = owl_list_get_size(psa_list);
343  for (i = 0; i < len; i++) {
344    owl_ps_action *a = owl_list_get_element(psa_list, i);
345    if (a->callback != NULL && a->callback(a, a->data)) {
346      ret = 1;
347    }
348  }
349  psa_active = 0;
350  owl_select_psa_gc();
351  return ret;
352}
353
354void owl_select(void)
355{
356  int i, max_fd, max_fd2, aim_done, ret;
357  fd_set r;
358  fd_set w;
359  fd_set e;
360  fd_set aim_rfds, aim_wfds;
361  struct timespec timeout;
362  sigset_t mask;
363
364  owl_select_process_timers(&timeout);
365
366  owl_select_mask_signals(&mask);
367
368  if(owl_global_is_interrupted(&g)) {
369    owl_select_handle_intr(&mask);
370    return;
371  }
372  FD_ZERO(&r);
373  FD_ZERO(&w);
374  FD_ZERO(&e);
375
376  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
377
378  /* AIM HACK:
379   *
380   *  The problem - I'm not sure where to hook into the owl/faim
381   *  interface to keep track of when the AIM socket(s) open and
382   *  close. In particular, the bosconn thing throws me off. So,
383   *  rather than register particular dispatchers for AIM, I look up
384   *  the relevant FDs and add them to select's watch lists, then
385   *  check for them individually before moving on to the other
386   *  dispatchers. --asedeno
387   */
388  aim_done = 1;
389  FD_ZERO(&aim_rfds);
390  FD_ZERO(&aim_wfds);
391  if (owl_global_is_doaimevents(&g)) {
392    aim_done = 0;
393    max_fd2 = owl_select_aim_hack(&aim_rfds, &aim_wfds);
394    if (max_fd < max_fd2) max_fd = max_fd2;
395    for(i = 0; i <= max_fd2; i++) {
396      if (FD_ISSET(i, &aim_rfds)) {
397        FD_SET(i, &r);
398        FD_SET(i, &e);
399      }
400      if (FD_ISSET(i, &aim_wfds)) {
401        FD_SET(i, &w);
402        FD_SET(i, &e);
403      }
404    }
405  }
406  /* END AIM HACK */
407
408  if (owl_select_do_pre_select_actions()) {
409    timeout.tv_sec = 0;
410    timeout.tv_nsec = 0;
411  }
412
413  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
414
415  if(ret < 0 && errno == EINTR) {
416    if(owl_global_is_interrupted(&g)) {
417      owl_select_handle_intr(NULL);
418    }
419    sigprocmask(SIG_SETMASK, &mask, NULL);
420    return;
421  }
422
423  sigprocmask(SIG_SETMASK, &mask, NULL);
424
425  if(ret > 0) {
426    /* AIM HACK: process all AIM events at once. */
427    for(i = 0; !aim_done && i <= max_fd; i++) {
428      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
429        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
430          owl_process_aim();
431          aim_done = 1;
432        }
433      }
434    }
435    owl_select_io_dispatch(&r, &w, &e, max_fd);
436  }
437}
Note: See TracBrowser for help on using the repository browser.