source: select.c @ dc1edbd

release-1.8release-1.9
Last change on this file since dc1edbd was dc1edbd, checked in by David Benjamin <davidben@mit.edu>, 10 years ago
Add a GSource for AIM events The AIM file descriptor hack is somewhat less of a hack now. Remove code related to the old AIM implementation now that it's been superceded.
  • Property mode set to 100644
File size: 8.4 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
298void owl_process_input_char(owl_input j)
299{
300  int ret;
301
302  owl_global_set_lastinputtime(&g, time(NULL));
303  ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
304  if (ret!=0 && ret!=1) {
305    owl_function_makemsg("Unable to handle keypress");
306  }
307}
308
309void owl_select_init(void)
310{
311  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
312  g_source_attach(owl_timer_source, NULL);
313
314  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
315  g_source_attach(owl_io_dispatch_source, NULL);
316}
317
318void owl_select_run_loop(void)
319{
320  loop = g_main_loop_new(NULL, FALSE);
321  g_main_loop_run(loop);
322}
323
324void owl_select_quit_loop(void)
325{
326  if (loop) {
327    g_main_loop_quit(loop);
328    loop = NULL;
329  }
330}
Note: See TracBrowser for help on using the repository browser.