source: select.c @ 09065ed

release-1.10release-1.7release-1.8release-1.9
Last change on this file since 09065ed was c6adf17, checked in by David Benjamin <davidben@mit.edu>, 14 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
RevLine 
[9c7a701]1#include "owl.h"
2
[1895c29]3static int dispatch_active = 0;
[4f2166b]4static int psa_active = 0;
[3ecd78b]5static int loop_active = 0;
[1895c29]6
[9cf96c8]7int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
[ca1fc33a]8  return t1->time - t2->time;
[b7bb454]9}
10
[9cf96c8]11int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
[ca1fc33a]12  return t1 == t2;
[b7bb454]13}
14
[c6adf17]15owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
[b7bb454]16{
[ca1fc33a]17  owl_timer *t = owl_malloc(sizeof(owl_timer));
[58d1f8a]18  GList **timers = owl_global_get_timerlist(&g);
[b7bb454]19
[ca1fc33a]20  t->time = time(NULL) + after;
21  t->interval = interval;
22  t->callback = cb;
[c675b39]23  t->destroy = destroy;
[ca1fc33a]24  t->data = data;
[c6adf17]25  t->name = name ? owl_strdup(name) : NULL;
[b7bb454]26
[58d1f8a]27  *timers = g_list_insert_sorted(*timers, t,
28                                 (GCompareFunc)_owl_select_timer_cmp);
[ca1fc33a]29  return t;
[b7bb454]30}
31
32void owl_select_remove_timer(owl_timer *t)
33{
[58d1f8a]34  GList **timers = owl_global_get_timerlist(&g);
35  if (t && g_list_find(*timers, t)) {
36    *timers = g_list_remove(*timers, t);
[c675b39]37    if(t->destroy) {
38      t->destroy(t);
39    }
[c6adf17]40    owl_free(t->name);
[ca1fc33a]41    owl_free(t);
42  }
[b7bb454]43}
44
[3a84694]45void owl_select_process_timers(struct timespec *timeout)
[b7bb454]46{
[ca1fc33a]47  time_t now = time(NULL);
[58d1f8a]48  GList **timers = owl_global_get_timerlist(&g);
[b7bb454]49
[58d1f8a]50  while(*timers) {
51    owl_timer *t = (*timers)->data;
[c675b39]52    int remove = 0;
[b7bb454]53
[ca1fc33a]54    if(t->time > now)
55      break;
[b7bb454]56
[ca1fc33a]57    /* Reschedule if appropriate */
58    if(t->interval > 0) {
59      t->time = now + t->interval;
[58d1f8a]60      *timers = g_list_remove(*timers, t);
61      *timers = g_list_insert_sorted(*timers, t,
62                                     (GCompareFunc)_owl_select_timer_cmp);
[ca1fc33a]63    } else {
[c675b39]64      remove = 1;
[ca1fc33a]65    }
[b7bb454]66
[ca1fc33a]67    /* Do the callback */
[c675b39]68    t->callback(t, t->data);
69    if(remove) {
70      owl_select_remove_timer(t);
71    }
[ca1fc33a]72  }
[b7bb454]73
[58d1f8a]74  if(*timers) {
75    owl_timer *t = (*timers)->data;
[ca1fc33a]76    timeout->tv_sec = t->time - now;
[58d1f8a]77    if (timeout->tv_sec > 60)
78      timeout->tv_sec = 60;
79  } else {
80    timeout->tv_sec = 60;
[ca1fc33a]81  }
[b7bb454]82
[3a84694]83  timeout->tv_nsec = 0;
[b7bb454]84}
85
[df0138f]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
[2f69081]239int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
[9c7a701]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) {
[2f69081]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     
[9c7a701]256      if (cur->fd > max_fd)
257        max_fd = cur->fd;
258    }
259  }
260  return max_fd;
261}
262
[5a35c708]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
[40bda84]274void owl_select_mask_signals(sigset_t *oldmask) {
275  sigset_t set;
276
277  sigemptyset(&set);
[c3031f3]278  sigaddset(&set, SIGWINCH);
279  sigaddset(&set, SIGALRM);
280  sigaddset(&set, SIGPIPE);
281  sigaddset(&set, SIGTERM);
282  sigaddset(&set, SIGHUP);
[40bda84]283  sigaddset(&set, SIGINT);
284  sigprocmask(SIG_BLOCK, &set, oldmask);
285}
286
287void owl_select_handle_intr(sigset_t *restore)
[3a84694]288{
289  owl_input in;
[0cb6c26]290
291  owl_global_unset_interrupted(&g);
[40bda84]292
293  sigprocmask(SIG_SETMASK, restore, NULL);
[0cb6c26]294
[3a84694]295  in.ch = in.uch = owl_global_get_startup_tio(&g)->c_cc[VINTR];
296  owl_process_input_char(in);
297}
298
[4f2166b]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
[c79a047]357void owl_select(void)
[9c7a701]358{
[df0138f]359  int i, max_fd, max_fd2, aim_done, ret;
[9c7a701]360  fd_set r;
[df0138f]361  fd_set w;
[9c7a701]362  fd_set e;
[2f69081]363  fd_set aim_rfds, aim_wfds;
[3a84694]364  struct timespec timeout;
[0cb6c26]365  sigset_t mask;
[9c7a701]366
[b7bb454]367  owl_select_process_timers(&timeout);
[9c7a701]368
[40bda84]369  owl_select_mask_signals(&mask);
370
[3a84694]371  if(owl_global_is_interrupted(&g)) {
[40bda84]372    owl_select_handle_intr(&mask);
[3a84694]373    return;
374  }
[df0138f]375  FD_ZERO(&r);
376  FD_ZERO(&w);
377  FD_ZERO(&e);
[3a84694]378
[6fc40a7]379  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
[9c7a701]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;
[2f69081]392  FD_ZERO(&aim_rfds);
393  FD_ZERO(&aim_wfds);
[9c7a701]394  if (owl_global_is_doaimevents(&g)) {
395    aim_done = 0;
[df0138f]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++) {
[2f69081]399      if (FD_ISSET(i, &aim_rfds)) {
[9c7a701]400        FD_SET(i, &r);
401        FD_SET(i, &e);
402      }
[df0138f]403      if (FD_ISSET(i, &aim_wfds)) {
404        FD_SET(i, &w);
405        FD_SET(i, &e);
406      }
[9c7a701]407    }
408  }
409  /* END AIM HACK */
[2f69081]410
[4f2166b]411  if (owl_select_do_pre_select_actions()) {
412    timeout.tv_sec = 0;
413    timeout.tv_nsec = 0;
414  }
415
[df0138f]416  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
[3a84694]417
418  if(ret < 0 && errno == EINTR) {
419    if(owl_global_is_interrupted(&g)) {
[40bda84]420      owl_select_handle_intr(NULL);
[3a84694]421    }
[40bda84]422    sigprocmask(SIG_SETMASK, &mask, NULL);
[3a84694]423    return;
424  }
425
[40bda84]426  sigprocmask(SIG_SETMASK, &mask, NULL);
[3a84694]427
428  if(ret > 0) {
[7ca5d3e]429    /* AIM HACK: process all AIM events at once. */
430    for(i = 0; !aim_done && i <= max_fd; i++) {
[df0138f]431      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
[7ca5d3e]432        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
[9c7a701]433          owl_process_aim();
434          aim_done = 1;
435        }
436      }
437    }
[df0138f]438    owl_select_io_dispatch(&r, &w, &e, max_fd);
[9c7a701]439  }
440}
[3ecd78b]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.