source: select.c @ 87833a8

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