source: select.c @ 567de81

Last change on this file since 567de81 was 567de81, checked in by David Benjamin <davidben@mit.edu>, 14 years ago
Move owl_process_input_char to owl.c It would be nice to move some functions out of there too, but it really makes no sense in select.c.
  • Property mode set to 100644
File size: 8.2 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);
10static void owl_select_io_dispatch_gc(void);
11
12static gboolean owl_timer_prepare(GSource *source, int *timeout) {
13  GList **timers = owl_global_get_timerlist(&g);
14  GTimeVal now;
15
16  /* TODO: In the far /far/ future, g_source_get_time is what the cool
17   * kids use to get system monotonic time. */
18  g_source_get_current_time(source, &now);
19
20  /* FIXME: bother with millisecond accuracy now that we can? */
21  if (*timers) {
22    owl_timer *t = (*timers)->data;
23    *timeout = t->time - now.tv_sec;
24    if (*timeout <= 0) {
25      *timeout = 0;
26      return TRUE;
27    }
28    if (*timeout > 60 * 1000)
29      *timeout = 60 * 1000;
30  } else {
31    *timeout = 60 * 1000;
32  }
33  return FALSE;
34}
35
36static gboolean owl_timer_check(GSource *source) {
37  GList **timers = owl_global_get_timerlist(&g);
38  GTimeVal now;
39
40  /* TODO: In the far /far/ future, g_source_get_time is what the cool
41   * kids use to get system monotonic time. */
42  g_source_get_current_time(source, &now);
43
44  /* FIXME: bother with millisecond accuracy now that we can? */
45  if (*timers) {
46    owl_timer *t = (*timers)->data;
47    return t->time >= now.tv_sec;
48  }
49  return FALSE;
50}
51
52
53static gboolean owl_timer_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
54  GList **timers = owl_global_get_timerlist(&g);
55  GTimeVal now;
56
57  /* TODO: In the far /far/ future, g_source_get_time is what the cool
58   * kids use to get system monotonic time. */
59  g_source_get_current_time(source, &now);
60
61  /* FIXME: bother with millisecond accuracy now that we can? */
62  while(*timers) {
63    owl_timer *t = (*timers)->data;
64    int remove = 0;
65
66    if(t->time > now.tv_sec)
67      break;
68
69    /* Reschedule if appropriate */
70    if(t->interval > 0) {
71      t->time = now.tv_sec + t->interval;
72      *timers = g_list_remove(*timers, t);
73      *timers = g_list_insert_sorted(*timers, t,
74                                     (GCompareFunc)_owl_select_timer_cmp);
75    } else {
76      remove = 1;
77    }
78
79    /* Do the callback */
80    t->callback(t, t->data);
81    if(remove) {
82      owl_select_remove_timer(t);
83    }
84  }
85  return TRUE;
86}
87
88static GSourceFuncs owl_timer_funcs = {
89  owl_timer_prepare,
90  owl_timer_check,
91  owl_timer_dispatch,
92  NULL
93};
94
95static int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
96  return t1->time - t2->time;
97}
98
99owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
100{
101  owl_timer *t = g_new(owl_timer, 1);
102  GList **timers = owl_global_get_timerlist(&g);
103
104  t->time = time(NULL) + after;
105  t->interval = interval;
106  t->callback = cb;
107  t->destroy = destroy;
108  t->data = data;
109  t->name = name ? g_strdup(name) : NULL;
110
111  *timers = g_list_insert_sorted(*timers, t,
112                                 (GCompareFunc)_owl_select_timer_cmp);
113  return t;
114}
115
116void owl_select_remove_timer(owl_timer *t)
117{
118  GList **timers = owl_global_get_timerlist(&g);
119  if (t && g_list_find(*timers, t)) {
120    *timers = g_list_remove(*timers, t);
121    if(t->destroy) {
122      t->destroy(t);
123    }
124    g_free(t->name);
125    g_free(t);
126  }
127}
128
129
130static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
131  *timeout = -1;
132  return FALSE;
133}
134
135static gboolean owl_io_dispatch_check(GSource *source) {
136  int i, len;
137  const owl_list *dl;
138
139  dl = owl_global_get_io_dispatch_list(&g);
140  len = owl_list_get_size(dl);
141  for(i = 0; i < len; i++) {
142    const owl_io_dispatch *d = owl_list_get_element(dl, i);
143    if (d->pollfd.revents & d->pollfd.events)
144      return TRUE;
145  }
146  return FALSE;
147}
148
149static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
150  int i, len;
151  const owl_list *dl;
152 
153  dispatch_active = 1;
154  dl = owl_global_get_io_dispatch_list(&g);
155  len = owl_list_get_size(dl);
156  for (i = 0; i < len; i++) {
157    owl_io_dispatch *d = owl_list_get_element(dl, i);
158    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
159      d->callback(d, d->data);
160    }
161  }
162  dispatch_active = 0;
163  owl_select_io_dispatch_gc();
164
165  return TRUE;
166}
167
168static GSourceFuncs owl_io_dispatch_funcs = {
169  owl_io_dispatch_prepare,
170  owl_io_dispatch_check,
171  owl_io_dispatch_dispatch,
172  NULL
173};
174
175static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
176{
177  int i, len;
178  const owl_list *dl;
179  owl_io_dispatch *d;
180  dl = owl_global_get_io_dispatch_list(&g);
181  len = owl_list_get_size(dl);
182  for(i = 0; i < len; i++) {
183    d = owl_list_get_element(dl, i);
184    if (d->fd == fd) return d;
185  }
186  return NULL;
187}
188
189static int owl_select_find_io_dispatch(const owl_io_dispatch *in)
190{
191  int i, len;
192  const owl_list *dl;
193
194  if (in != NULL) {
195    dl = owl_global_get_io_dispatch_list(&g);
196    len = owl_list_get_size(dl);
197    for(i = 0; i < len; i++) {
198      const owl_io_dispatch *d = owl_list_get_element(dl, i);
199      if (d == in) return i;
200    }
201  }
202  return -1;
203}
204
205void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
206{
207  int elt;
208  if (in != NULL) {
209    elt = owl_select_find_io_dispatch(in);
210    if (elt != -1) {
211      owl_list *dl = owl_global_get_io_dispatch_list(&g);
212      owl_io_dispatch *d = owl_list_get_element(dl, elt);
213      if (dispatch_active)
214        d->needs_gc = 1;
215      else {
216        owl_list_remove_element(dl, elt);
217        if (d->destroy)
218          d->destroy(d);
219        g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
220        g_free(d);
221      }
222    }
223  }
224}
225
226static void owl_select_io_dispatch_gc(void)
227{
228  int i;
229  owl_list *dl;
230
231  dl = owl_global_get_io_dispatch_list(&g);
232  /*
233   * Count down so we aren't set off by removing items from the list
234   * during the iteration.
235   */
236  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
237    owl_io_dispatch *d = owl_list_get_element(dl, i);
238    if(d->needs_gc) {
239      owl_select_remove_io_dispatch(d);
240    }
241  }
242}
243
244/* Each FD may have at most one dispatcher.
245 * If a new dispatch is added for an FD, the old one is removed.
246 * mode determines what types of events are watched for, and may be any combination of:
247 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
248 */
249const 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)
250{
251  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
252  owl_list *dl = owl_global_get_io_dispatch_list(&g);
253
254  d->fd = fd;
255  d->needs_gc = 0;
256  d->mode = mode;
257  d->callback = cb;
258  d->destroy = destroy;
259  d->data = data;
260
261  /* TODO: Allow changing fd and mode in the middle? Probably don't care... */
262  d->pollfd.fd = fd;
263  d->pollfd.events = 0;
264  if (d->mode & OWL_IO_READ)
265    d->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
266  if (d->mode & OWL_IO_WRITE)
267    d->pollfd.events |= G_IO_OUT | G_IO_ERR;
268  if (d->mode & OWL_IO_EXCEPT)
269    d->pollfd.events |= G_IO_PRI | G_IO_ERR;
270  g_source_add_poll(owl_io_dispatch_source, &d->pollfd);
271
272
273  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
274  owl_list_append_element(dl, d);
275
276  return d;
277}
278
279int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
280{
281  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
282  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
283    /* Don't mess with non-perl dispatch functions from here. */
284    return 1;
285  }
286  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
287  return 0;
288}
289
290int owl_select_remove_perl_io_dispatch(int fd)
291{
292  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
293  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
294    /* Only remove perl io dispatchers from here. */
295    owl_select_remove_io_dispatch(d);
296    return 0;
297  }
298  return 1;
299}
300
301void owl_select_init(void)
302{
303  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
304  g_source_attach(owl_timer_source, NULL);
305
306  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
307  g_source_attach(owl_io_dispatch_source, NULL);
308}
309
310void owl_select_run_loop(void)
311{
312  loop = g_main_loop_new(NULL, FALSE);
313  g_main_loop_run(loop);
314}
315
316void owl_select_quit_loop(void)
317{
318  if (loop) {
319    g_main_loop_quit(loop);
320    loop = NULL;
321  }
322}
Note: See TracBrowser for help on using the repository browser.