source: select.c @ 0af5f9d

Last change on this file since 0af5f9d 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
Line 
1#include "owl.h"
2
3static GMainLoop *loop = NULL;
4static int dispatch_active = 0;
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};
91
92int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
93  return t1->time - t2->time;
94}
95
96int _owl_select_timer_eq(const owl_timer *t1, const owl_timer *t2) {
97  return t1 == t2;
98}
99
100owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
101{
102  owl_timer *t = g_new(owl_timer, 1);
103  GList **timers = owl_global_get_timerlist(&g);
104
105  t->time = time(NULL) + after;
106  t->interval = interval;
107  t->callback = cb;
108  t->destroy = destroy;
109  t->data = data;
110  t->name = name ? g_strdup(name) : NULL;
111
112  *timers = g_list_insert_sorted(*timers, t,
113                                 (GCompareFunc)_owl_select_timer_cmp);
114  return t;
115}
116
117void owl_select_remove_timer(owl_timer *t)
118{
119  GList **timers = owl_global_get_timerlist(&g);
120  if (t && g_list_find(*timers, t)) {
121    *timers = g_list_remove(*timers, t);
122    if(t->destroy) {
123      t->destroy(t);
124    }
125    g_free(t->name);
126    g_free(t);
127  }
128}
129
130
131static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
132  *timeout = -1;
133  return FALSE;
134}
135
136static gboolean owl_io_dispatch_check(GSource *source) {
137  int i, len;
138  const owl_list *dl;
139
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;
146  }
147  return FALSE;
148}
149
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    }
162  }
163  dispatch_active = 0;
164  owl_select_io_dispatch_gc();
165
166  return TRUE;
167}
168
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
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);
220        g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
221        g_free(d);
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{
252  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
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
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
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
302int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
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);
310  for (cur = sess->connlist; cur; cur = cur->next) {
311    if (cur->fd != -1) {
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     
319      if (cur->fd > max_fd)
320        max_fd = cur->fd;
321    }
322  }
323  return max_fd;
324}
325
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
337#if 0
338void owl_select(void)
339{
340  int i, max_fd, max_fd2, aim_done, ret;
341  fd_set r;
342  fd_set w;
343  fd_set e;
344  fd_set aim_rfds, aim_wfds;
345  struct timespec timeout;
346  sigset_t mask;
347
348  owl_select_process_timers(&timeout);
349
350  owl_select_mask_signals(&mask);
351
352  if(owl_global_is_interrupted(&g)) {
353    owl_select_handle_intr(&mask);
354    return;
355  }
356  FD_ZERO(&r);
357  FD_ZERO(&w);
358  FD_ZERO(&e);
359
360  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
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;
373  FD_ZERO(&aim_rfds);
374  FD_ZERO(&aim_wfds);
375  if (owl_global_is_doaimevents(&g)) {
376    aim_done = 0;
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++) {
380      if (FD_ISSET(i, &aim_rfds)) {
381        FD_SET(i, &r);
382        FD_SET(i, &e);
383      }
384      if (FD_ISSET(i, &aim_wfds)) {
385        FD_SET(i, &w);
386        FD_SET(i, &e);
387      }
388    }
389  }
390  /* END AIM HACK */
391
392  if (owl_select_do_pre_select_actions()) {
393    timeout.tv_sec = 0;
394    timeout.tv_nsec = 0;
395  }
396
397  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
398
399  if(ret < 0 && errno == EINTR) {
400    if(owl_global_is_interrupted(&g)) {
401      owl_select_handle_intr(NULL);
402    }
403    sigprocmask(SIG_SETMASK, &mask, NULL);
404    return;
405  }
406
407  sigprocmask(SIG_SETMASK, &mask, NULL);
408
409  if(ret > 0) {
410    /* AIM HACK: process all AIM events at once. */
411    for(i = 0; !aim_done && i <= max_fd; i++) {
412      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
413        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
414          owl_process_aim();
415          aim_done = 1;
416        }
417      }
418    }
419    owl_select_io_dispatch(&r, &w, &e, max_fd);
420  }
421}
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}
432
433void owl_select_run_loop(void)
434{
435  loop = g_main_loop_new(NULL, FALSE);
436  g_main_loop_run(loop);
437}
438
439void owl_select_quit_loop(void)
440{
441  if (loop) {
442    g_main_loop_quit(loop);
443    loop = NULL;
444  }
445}
Note: See TracBrowser for help on using the repository browser.