source: select.c @ c6adf17

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