source: select.c @ de6f317

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