source: select.c @ 81db142

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