source: select.c @ 9d43dcc

release-1.10release-1.8release-1.9
Last change on this file since 9d43dcc was f0781ba, checked in by David Benjamin <davidben@mit.edu>, 13 years ago
Release reference to GMainLoop when done It's only done at shutdown, but we may as well appease valgrind.
  • Property mode set to 100644
File size: 10.0 KB
Line 
1#include "owl.h"
2
3static GMainLoop *loop = NULL;
4static GMainContext *main_context;
5static int dispatch_active = 0;
6
7static GSource *owl_timer_source;
8static GSource *owl_io_dispatch_source;
9
10static int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
11  return t1->time - t2->time;
12}
13
14owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
15{
16  owl_timer *t = g_new(owl_timer, 1);
17  GList **timers = owl_global_get_timerlist(&g);
18
19  t->time = time(NULL) + after;
20  t->interval = interval;
21  t->callback = cb;
22  t->destroy = destroy;
23  t->data = data;
24  t->name = name ? g_strdup(name) : NULL;
25
26  *timers = g_list_insert_sorted(*timers, t,
27                                 (GCompareFunc)_owl_select_timer_cmp);
28  return t;
29}
30
31void owl_select_remove_timer(owl_timer *t)
32{
33  GList **timers = owl_global_get_timerlist(&g);
34  if (t && g_list_find(*timers, t)) {
35    *timers = g_list_remove(*timers, t);
36    if(t->destroy) {
37      t->destroy(t);
38    }
39    g_free(t->name);
40    g_free(t);
41  }
42}
43
44static gboolean owl_timer_prepare(GSource *source, int *timeout) {
45  GList **timers = owl_global_get_timerlist(&g);
46  GTimeVal now;
47
48  /* TODO: In the far /far/ future, g_source_get_time is what the cool
49   * kids use to get system monotonic time. */
50  g_source_get_current_time(source, &now);
51
52  /* FIXME: bother with millisecond accuracy now that we can? */
53  if (*timers) {
54    owl_timer *t = (*timers)->data;
55    *timeout = t->time - now.tv_sec;
56    if (*timeout <= 0) {
57      *timeout = 0;
58      return TRUE;
59    }
60    if (*timeout > 60 * 1000)
61      *timeout = 60 * 1000;
62  } else {
63    *timeout = 60 * 1000;
64  }
65  return FALSE;
66}
67
68static gboolean owl_timer_check(GSource *source) {
69  GList **timers = owl_global_get_timerlist(&g);
70  GTimeVal now;
71
72  /* TODO: In the far /far/ future, g_source_get_time is what the cool
73   * kids use to get system monotonic time. */
74  g_source_get_current_time(source, &now);
75
76  /* FIXME: bother with millisecond accuracy now that we can? */
77  if (*timers) {
78    owl_timer *t = (*timers)->data;
79    return t->time >= now.tv_sec;
80  }
81  return FALSE;
82}
83
84
85static gboolean owl_timer_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
86  GList **timers = owl_global_get_timerlist(&g);
87  GTimeVal now;
88
89  /* TODO: In the far /far/ future, g_source_get_time is what the cool
90   * kids use to get system monotonic time. */
91  g_source_get_current_time(source, &now);
92
93  /* FIXME: bother with millisecond accuracy now that we can? */
94  while(*timers) {
95    owl_timer *t = (*timers)->data;
96    int remove = 0;
97
98    if(t->time > now.tv_sec)
99      break;
100
101    /* Reschedule if appropriate */
102    if(t->interval > 0) {
103      t->time = now.tv_sec + t->interval;
104      *timers = g_list_remove(*timers, t);
105      *timers = g_list_insert_sorted(*timers, t,
106                                     (GCompareFunc)_owl_select_timer_cmp);
107    } else {
108      remove = 1;
109    }
110
111    /* Do the callback */
112    t->callback(t, t->data);
113    if(remove) {
114      owl_select_remove_timer(t);
115    }
116  }
117  return TRUE;
118}
119
120static GSourceFuncs owl_timer_funcs = {
121  owl_timer_prepare,
122  owl_timer_check,
123  owl_timer_dispatch,
124  NULL
125};
126
127/* Returns the valid owl_io_dispatch for a given file descriptor. */
128static owl_io_dispatch *owl_select_find_valid_io_dispatch_by_fd(const int fd)
129{
130  int i, len;
131  const owl_list *dl;
132  owl_io_dispatch *d;
133  dl = owl_global_get_io_dispatch_list(&g);
134  len = owl_list_get_size(dl);
135  for(i = 0; i < len; i++) {
136    d = owl_list_get_element(dl, i);
137    if (d->fd == fd && d->valid) return d;
138  }
139  return NULL;
140}
141
142static int owl_select_find_io_dispatch(const owl_io_dispatch *in)
143{
144  int i, len;
145  const owl_list *dl;
146
147  if (in != NULL) {
148    dl = owl_global_get_io_dispatch_list(&g);
149    len = owl_list_get_size(dl);
150    for(i = 0; i < len; i++) {
151      const owl_io_dispatch *d = owl_list_get_element(dl, i);
152      if (d == in) return i;
153    }
154  }
155  return -1;
156}
157
158static void owl_select_invalidate_io_dispatch(owl_io_dispatch *d)
159{
160  if (d == NULL || !d->valid)
161    return;
162  d->valid = false;
163  g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
164}
165
166void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
167{
168  int elt;
169  if (in != NULL) {
170    elt = owl_select_find_io_dispatch(in);
171    if (elt != -1) {
172      owl_list *dl = owl_global_get_io_dispatch_list(&g);
173      owl_io_dispatch *d = owl_list_get_element(dl, elt);
174      if (dispatch_active)
175        d->needs_gc = 1;
176      else {
177        owl_select_invalidate_io_dispatch(d);
178        owl_list_remove_element(dl, elt);
179        if (d->destroy)
180          d->destroy(d);
181        g_free(d);
182      }
183    }
184  }
185}
186
187static void owl_select_io_dispatch_gc(void)
188{
189  int i;
190  owl_list *dl;
191
192  dl = owl_global_get_io_dispatch_list(&g);
193  /*
194   * Count down so we aren't set off by removing items from the list
195   * during the iteration.
196   */
197  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
198    owl_io_dispatch *d = owl_list_get_element(dl, i);
199    if(d->needs_gc) {
200      owl_select_remove_io_dispatch(d);
201    }
202  }
203}
204
205/* Each FD may have at most one valid dispatcher.
206 * If a new dispatch is added for an FD, the old one is removed.
207 * mode determines what types of events are watched for, and may be any combination of:
208 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
209 */
210const 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)
211{
212  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
213  owl_list *dl = owl_global_get_io_dispatch_list(&g);
214  owl_io_dispatch *other;
215
216  d->fd = fd;
217  d->valid = true;
218  d->needs_gc = 0;
219  d->mode = mode;
220  d->callback = cb;
221  d->destroy = destroy;
222  d->data = data;
223
224  /* TODO: Allow changing fd and mode in the middle? Probably don't care... */
225  d->pollfd.fd = fd;
226  d->pollfd.events = 0;
227  if (d->mode & OWL_IO_READ)
228    d->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
229  if (d->mode & OWL_IO_WRITE)
230    d->pollfd.events |= G_IO_OUT | G_IO_ERR;
231  if (d->mode & OWL_IO_EXCEPT)
232    d->pollfd.events |= G_IO_PRI | G_IO_ERR;
233  g_source_add_poll(owl_io_dispatch_source, &d->pollfd);
234
235
236  other = owl_select_find_valid_io_dispatch_by_fd(fd);
237  if (other)
238    owl_select_invalidate_io_dispatch(other);
239  owl_list_append_element(dl, d);
240
241  return d;
242}
243
244static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
245  *timeout = -1;
246  return FALSE;
247}
248
249static gboolean owl_io_dispatch_check(GSource *source) {
250  int i, len;
251  const owl_list *dl;
252
253  dl = owl_global_get_io_dispatch_list(&g);
254  len = owl_list_get_size(dl);
255  for(i = 0; i < len; i++) {
256    owl_io_dispatch *d = owl_list_get_element(dl, i);
257    if (!d->valid) continue;
258    if (d->pollfd.revents & G_IO_NVAL) {
259      owl_function_debugmsg("Pruning defunct dispatch on fd %d.", d->fd);
260      owl_select_invalidate_io_dispatch(d);
261    }
262    if (d->pollfd.revents & d->pollfd.events)
263      return TRUE;
264  }
265  return FALSE;
266}
267
268static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
269  int i, len;
270  const owl_list *dl;
271
272  dispatch_active = 1;
273  dl = owl_global_get_io_dispatch_list(&g);
274  len = owl_list_get_size(dl);
275  for (i = 0; i < len; i++) {
276    owl_io_dispatch *d = owl_list_get_element(dl, i);
277    if (!d->valid) continue;
278    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
279      d->callback(d, d->data);
280    }
281  }
282  dispatch_active = 0;
283  owl_select_io_dispatch_gc();
284
285  return TRUE;
286}
287
288static GSourceFuncs owl_io_dispatch_funcs = {
289  owl_io_dispatch_prepare,
290  owl_io_dispatch_check,
291  owl_io_dispatch_dispatch,
292  NULL
293};
294
295int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
296{
297  const owl_io_dispatch *d = owl_select_find_valid_io_dispatch_by_fd(fd);
298  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
299    /* Don't mess with non-perl dispatch functions from here. */
300    return 1;
301  }
302  /* Also remove any invalidated perl dispatch functions that may have
303   * stuck around. */
304  owl_select_remove_perl_io_dispatch(fd);
305  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
306  return 0;
307}
308
309static owl_io_dispatch *owl_select_find_perl_io_dispatch(int fd)
310{
311  int i, len;
312  const owl_list *dl;
313  owl_io_dispatch *d;
314  dl = owl_global_get_io_dispatch_list(&g);
315  len = owl_list_get_size(dl);
316  for(i = 0; i < len; i++) {
317    d = owl_list_get_element(dl, i);
318    if (d->fd == fd && d->callback == owl_perlconfig_io_dispatch)
319      return d;
320  }
321  return NULL;
322}
323
324int owl_select_remove_perl_io_dispatch(int fd)
325{
326  owl_io_dispatch *d = owl_select_find_perl_io_dispatch(fd);
327  if (d != NULL) {
328    /* Only remove perl io dispatchers from here. */
329    owl_select_remove_io_dispatch(d);
330    return 0;
331  }
332  return 1;
333}
334
335void owl_select_init(void)
336{
337  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
338  g_source_attach(owl_timer_source, NULL);
339
340  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
341  g_source_attach(owl_io_dispatch_source, NULL);
342}
343
344void owl_select_run_loop(void)
345{
346  main_context = g_main_context_default();
347  loop = g_main_loop_new(main_context, FALSE);
348  g_main_loop_run(loop);
349}
350
351void owl_select_quit_loop(void)
352{
353  if (loop) {
354    g_main_loop_quit(loop);
355    g_main_loop_unref(loop);
356    loop = NULL;
357  }
358}
359
360typedef struct _owl_task { /*noproto*/
361  void (*cb)(void *);
362  void *cbdata;
363  void (*destroy_cbdata)(void *);
364} owl_task;
365
366static gboolean _run_task(gpointer data)
367{
368  owl_task *t = data;
369  if (t->cb)
370    t->cb(t->cbdata);
371  return FALSE;
372}
373
374static void _destroy_task(void *data)
375{
376  owl_task *t = data;
377  if (t->destroy_cbdata)
378    t->destroy_cbdata(t->cbdata);
379  g_free(t);
380}
381
382void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*), GMainContext *context)
383{
384  GSource *source = g_idle_source_new();
385  owl_task *t = g_new0(owl_task, 1);
386  t->cb = cb;
387  t->cbdata = cbdata;
388  t->destroy_cbdata = destroy_cbdata;
389  g_source_set_priority(source, G_PRIORITY_DEFAULT);
390  g_source_set_callback(source, _run_task, t, _destroy_task);
391  g_source_attach(source, context);
392  g_source_unref(source);
393}
Note: See TracBrowser for help on using the repository browser.