source: select.c @ 257b9c4

release-1.10release-1.8release-1.9
Last change on this file since 257b9c4 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
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 int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
10  return t1->time - t2->time;
11}
12
13owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
14{
15  owl_timer *t = g_new(owl_timer, 1);
16  GList **timers = owl_global_get_timerlist(&g);
17
18  t->time = time(NULL) + after;
19  t->interval = interval;
20  t->callback = cb;
21  t->destroy = destroy;
22  t->data = data;
23  t->name = name ? g_strdup(name) : NULL;
24
25  *timers = g_list_insert_sorted(*timers, t,
26                                 (GCompareFunc)_owl_select_timer_cmp);
27  return t;
28}
29
30void owl_select_remove_timer(owl_timer *t)
31{
32  GList **timers = owl_global_get_timerlist(&g);
33  if (t && g_list_find(*timers, t)) {
34    *timers = g_list_remove(*timers, t);
35    if(t->destroy) {
36      t->destroy(t);
37    }
38    g_free(t->name);
39    g_free(t);
40  }
41}
42
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) {
68  GList **timers = owl_global_get_timerlist(&g);
69  GTimeVal now;
70
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? */
93  while(*timers) {
94    owl_timer *t = (*timers)->data;
95    int remove = 0;
96
97    if(t->time > now.tv_sec)
98      break;
99
100    /* Reschedule if appropriate */
101    if(t->interval > 0) {
102      t->time = now.tv_sec + t->interval;
103      *timers = g_list_remove(*timers, t);
104      *timers = g_list_insert_sorted(*timers, t,
105                                     (GCompareFunc)_owl_select_timer_cmp);
106    } else {
107      remove = 1;
108    }
109
110    /* Do the callback */
111    t->callback(t, t->data);
112    if(remove) {
113      owl_select_remove_timer(t);
114    }
115  }
116  return TRUE;
117}
118
119static GSourceFuncs owl_timer_funcs = {
120  owl_timer_prepare,
121  owl_timer_check,
122  owl_timer_dispatch,
123  NULL
124};
125
126
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);
171        g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
172        g_free(d);
173      }
174    }
175  }
176}
177
178static void owl_select_io_dispatch_gc(void)
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{
203  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
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
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
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
231static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
232  *timeout = -1;
233  return FALSE;
234}
235
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);
241  len = owl_list_get_size(dl);
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;
246  }
247  return FALSE;
248}
249
250static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
251  int i, len;
252  const owl_list *dl;
253
254  dispatch_active = 1;
255  dl = owl_global_get_io_dispatch_list(&g);
256  len = owl_list_get_size(dl);
257  for (i = 0; i < len; i++) {
258    owl_io_dispatch *d = owl_list_get_element(dl, i);
259    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
260      d->callback(d, d->data);
261    }
262  }
263  dispatch_active = 0;
264  owl_select_io_dispatch_gc();
265
266  return TRUE;
267}
268
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
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
298int owl_select_aim_hack(fd_set *rfds, fd_set *wfds)
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);
306  for (cur = sess->connlist; cur; cur = cur->next) {
307    if (cur->fd != -1) {
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     
315      if (cur->fd > max_fd)
316        max_fd = cur->fd;
317    }
318  }
319  return max_fd;
320}
321
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
333#if 0
334/* FIXME: Reimplement the AIM hack and handle AIM events. */
335void owl_select(void)
336{
337  int i, max_fd, max_fd2, aim_done, ret;
338  fd_set r;
339  fd_set w;
340  fd_set e;
341  fd_set aim_rfds, aim_wfds;
342  struct timespec timeout;
343  sigset_t mask;
344
345  owl_select_process_timers(&timeout);
346
347  owl_select_mask_signals(&mask);
348
349  if(owl_global_is_interrupted(&g)) {
350    owl_select_handle_intr(&mask);
351    return;
352  }
353  FD_ZERO(&r);
354  FD_ZERO(&w);
355  FD_ZERO(&e);
356
357  max_fd = owl_select_prepare_io_dispatch_fd_sets(&r, &w, &e);
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;
370  FD_ZERO(&aim_rfds);
371  FD_ZERO(&aim_wfds);
372  if (owl_global_is_doaimevents(&g)) {
373    aim_done = 0;
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++) {
377      if (FD_ISSET(i, &aim_rfds)) {
378        FD_SET(i, &r);
379        FD_SET(i, &e);
380      }
381      if (FD_ISSET(i, &aim_wfds)) {
382        FD_SET(i, &w);
383        FD_SET(i, &e);
384      }
385    }
386  }
387  /* END AIM HACK */
388
389  if (owl_select_do_pre_select_actions()) {
390    timeout.tv_sec = 0;
391    timeout.tv_nsec = 0;
392  }
393
394  ret = pselect(max_fd+1, &r, &w, &e, &timeout, &mask);
395
396  if(ret < 0 && errno == EINTR) {
397    if(owl_global_is_interrupted(&g)) {
398      owl_select_handle_intr(NULL);
399    }
400    sigprocmask(SIG_SETMASK, &mask, NULL);
401    return;
402  }
403
404  sigprocmask(SIG_SETMASK, &mask, NULL);
405
406  if(ret > 0) {
407    /* AIM HACK: process all AIM events at once. */
408    for(i = 0; !aim_done && i <= max_fd; i++) {
409      if (FD_ISSET(i, &r) || FD_ISSET(i, &w) || FD_ISSET(i, &e)) {
410        if (FD_ISSET(i, &aim_rfds) || FD_ISSET(i, &aim_wfds)) {
411          owl_process_aim();
412          aim_done = 1;
413        }
414      }
415    }
416    owl_select_io_dispatch(&r, &w, &e, max_fd);
417  }
418}
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}
429
430void owl_select_run_loop(void)
431{
432  loop = g_main_loop_new(NULL, FALSE);
433  g_main_loop_run(loop);
434}
435
436void owl_select_quit_loop(void)
437{
438  if (loop) {
439    g_main_loop_quit(loop);
440    loop = NULL;
441  }
442}
Note: See TracBrowser for help on using the repository browser.