source: select.c @ 57bc141

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