source: select.c @ ea68035

release-1.10release-1.8release-1.9
Last change on this file since ea68035 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
RevLine 
[9c7a701]1#include "owl.h"
2
[2c79eae]3static GMainLoop *loop = NULL;
[44976fe]4static GMainContext *main_context;
[1895c29]5static int dispatch_active = 0;
6
[2c79eae]7static GSource *owl_timer_source;
8static GSource *owl_io_dispatch_source;
[b7bb454]9
[ebb8498]10static int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
[ca1fc33a]11  return t1->time - t2->time;
[b7bb454]12}
13
[c6adf17]14owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
[b7bb454]15{
[96828e4]16  owl_timer *t = g_new(owl_timer, 1);
[58d1f8a]17  GList **timers = owl_global_get_timerlist(&g);
[b7bb454]18
[ca1fc33a]19  t->time = time(NULL) + after;
20  t->interval = interval;
21  t->callback = cb;
[c675b39]22  t->destroy = destroy;
[ca1fc33a]23  t->data = data;
[d4927a7]24  t->name = name ? g_strdup(name) : NULL;
[b7bb454]25
[58d1f8a]26  *timers = g_list_insert_sorted(*timers, t,
27                                 (GCompareFunc)_owl_select_timer_cmp);
[ca1fc33a]28  return t;
[b7bb454]29}
30
31void owl_select_remove_timer(owl_timer *t)
32{
[58d1f8a]33  GList **timers = owl_global_get_timerlist(&g);
34  if (t && g_list_find(*timers, t)) {
35    *timers = g_list_remove(*timers, t);
[c675b39]36    if(t->destroy) {
37      t->destroy(t);
38    }
[ddbbcffa]39    g_free(t->name);
40    g_free(t);
[ca1fc33a]41  }
[b7bb454]42}
43
[2c79eae]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) {
[58d1f8a]69  GList **timers = owl_global_get_timerlist(&g);
[2c79eae]70  GTimeVal now;
[b7bb454]71
[2c79eae]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? */
[58d1f8a]94  while(*timers) {
95    owl_timer *t = (*timers)->data;
[c675b39]96    int remove = 0;
[b7bb454]97
[2c79eae]98    if(t->time > now.tv_sec)
[ca1fc33a]99      break;
[b7bb454]100
[ca1fc33a]101    /* Reschedule if appropriate */
102    if(t->interval > 0) {
[2c79eae]103      t->time = now.tv_sec + t->interval;
[58d1f8a]104      *timers = g_list_remove(*timers, t);
105      *timers = g_list_insert_sorted(*timers, t,
106                                     (GCompareFunc)_owl_select_timer_cmp);
[ca1fc33a]107    } else {
[c675b39]108      remove = 1;
[ca1fc33a]109    }
[b7bb454]110
[ca1fc33a]111    /* Do the callback */
[c675b39]112    t->callback(t, t->data);
113    if(remove) {
114      owl_select_remove_timer(t);
115    }
[ca1fc33a]116  }
[2c79eae]117  return TRUE;
118}
[b7bb454]119
[2c79eae]120static GSourceFuncs owl_timer_funcs = {
121  owl_timer_prepare,
122  owl_timer_check,
123  owl_timer_dispatch,
124  NULL
125};
[b7bb454]126
[33b6431b]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)
[df0138f]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);
[33b6431b]137    if (d->fd == fd && d->valid) return d;
[df0138f]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
[33b6431b]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
[df0138f]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 {
[33b6431b]177        owl_select_invalidate_io_dispatch(d);
[df0138f]178        owl_list_remove_element(dl, elt);
179        if (d->destroy)
180          d->destroy(d);
[ddbbcffa]181        g_free(d);
[df0138f]182      }
183    }
184  }
185}
186
[ebb8498]187static void owl_select_io_dispatch_gc(void)
[df0138f]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
[33b6431b]205/* Each FD may have at most one valid dispatcher.
[df0138f]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{
[96828e4]212  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
[df0138f]213  owl_list *dl = owl_global_get_io_dispatch_list(&g);
[33b6431b]214  owl_io_dispatch *other;
[df0138f]215
216  d->fd = fd;
[33b6431b]217  d->valid = true;
[df0138f]218  d->needs_gc = 0;
219  d->mode = mode;
220  d->callback = cb;
221  d->destroy = destroy;
222  d->data = data;
223
[2c79eae]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
[33b6431b]236  other = owl_select_find_valid_io_dispatch_by_fd(fd);
237  if (other)
238    owl_select_invalidate_io_dispatch(other);
[df0138f]239  owl_list_append_element(dl, d);
240
241  return d;
242}
243
[2c79eae]244static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
245  *timeout = -1;
246  return FALSE;
247}
[df0138f]248
[2c79eae]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);
[df0138f]254  len = owl_list_get_size(dl);
[2c79eae]255  for(i = 0; i < len; i++) {
[33b6431b]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    }
[2c79eae]262    if (d->pollfd.revents & d->pollfd.events)
263      return TRUE;
[df0138f]264  }
[2c79eae]265  return FALSE;
[df0138f]266}
267
[2c79eae]268static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
[df0138f]269  int i, len;
[2c79eae]270  const owl_list *dl;
[df0138f]271
272  dispatch_active = 1;
[2c79eae]273  dl = owl_global_get_io_dispatch_list(&g);
[df0138f]274  len = owl_list_get_size(dl);
275  for (i = 0; i < len; i++) {
[2c79eae]276    owl_io_dispatch *d = owl_list_get_element(dl, i);
[33b6431b]277    if (!d->valid) continue;
[2c79eae]278    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
[df0138f]279      d->callback(d, d->data);
280    }
281  }
282  dispatch_active = 0;
283  owl_select_io_dispatch_gc();
[2c79eae]284
285  return TRUE;
[df0138f]286}
287
[2c79eae]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
[df0138f]295int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
296{
[33b6431b]297  const owl_io_dispatch *d = owl_select_find_valid_io_dispatch_by_fd(fd);
[df0138f]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  }
[33b6431b]302  /* Also remove any invalidated perl dispatch functions that may have
303   * stuck around. */
304  owl_select_remove_perl_io_dispatch(fd);
[df0138f]305  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
306  return 0;
307}
308
[33b6431b]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
[df0138f]324int owl_select_remove_perl_io_dispatch(int fd)
325{
[33b6431b]326  owl_io_dispatch *d = owl_select_find_perl_io_dispatch(fd);
327  if (d != NULL) {
[df0138f]328    /* Only remove perl io dispatchers from here. */
329    owl_select_remove_io_dispatch(d);
330    return 0;
331  }
332  return 1;
333}
334
[2c79eae]335void owl_select_init(void)
[4f2166b]336{
[2c79eae]337  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
338  g_source_attach(owl_timer_source, NULL);
[4f2166b]339
[2c79eae]340  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
341  g_source_attach(owl_io_dispatch_source, NULL);
[4f2166b]342}
343
[3ecd78b]344void owl_select_run_loop(void)
[4f2166b]345{
[44976fe]346  main_context = g_main_context_default();
347  loop = g_main_loop_new(main_context, FALSE);
[2c79eae]348  g_main_loop_run(loop);
[4f2166b]349}
350
[3ecd78b]351void owl_select_quit_loop(void)
[4f2166b]352{
[2c79eae]353  if (loop) {
354    g_main_loop_quit(loop);
[f0781ba]355    g_main_loop_unref(loop);
[2c79eae]356    loop = NULL;
[4f2166b]357  }
358}
359
[ba12b44]360typedef struct _owl_task { /*noproto*/
361  void (*cb)(void *);
362  void *cbdata;
363  void (*destroy_cbdata)(void *);
364} owl_task;
[3a84694]365
[ba12b44]366static gboolean _run_task(gpointer data)
367{
368  owl_task *t = data;
369  if (t->cb)
370    t->cb(t->cbdata);
371  return FALSE;
[9c7a701]372}
[3ecd78b]373
[ba12b44]374static void _destroy_task(void *data)
[3ecd78b]375{
[ba12b44]376  owl_task *t = data;
377  if (t->destroy_cbdata)
378    t->destroy_cbdata(t->cbdata);
379  g_free(t);
[3ecd78b]380}
381
[44976fe]382void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*), GMainContext *context)
[3ecd78b]383{
[ba12b44]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);
[3ecd78b]393}
Note: See TracBrowser for help on using the repository browser.