source: select.c @ 78f6c35

release-1.8release-1.9
Last change on this file since 78f6c35 was 33b6431b, checked in by David Benjamin <davidben@mit.edu>, 10 years ago
Clarify owl_io_dispatch ownership A created owl_io_dispatch is owned by whoever created. For instances where owl_select needs to remove one of them, we instead invalidate them. In the case of perl IO dispatches, they are owned by the nebulous blob that implicitly maps fd to IO dispatch. This is completely insane, but should avoid leaving people with invalid owl_io_dispatch pointers. Use this same mechanism to restore owl_select_prune_bad_fds logic. We really need to kill this and owl_timer in favor of glib's things.
  • Property mode set to 100644
File size: 10.0 KB
Line 
1#include "owl.h"
2
3static GMainLoop *loop = NULL;
4static GMainContext *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  context = g_main_context_default();
347  loop = g_main_loop_new(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    loop = NULL;
356  }
357}
358
359typedef struct _owl_task { /*noproto*/
360  void (*cb)(void *);
361  void *cbdata;
362  void (*destroy_cbdata)(void *);
363} owl_task;
364
365static gboolean _run_task(gpointer data)
366{
367  owl_task *t = data;
368  if (t->cb)
369    t->cb(t->cbdata);
370  return FALSE;
371}
372
373static void _destroy_task(void *data)
374{
375  owl_task *t = data;
376  if (t->destroy_cbdata)
377    t->destroy_cbdata(t->cbdata);
378  g_free(t);
379}
380
381void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*))
382{
383  GSource *source = g_idle_source_new();
384  owl_task *t = g_new0(owl_task, 1);
385  t->cb = cb;
386  t->cbdata = cbdata;
387  t->destroy_cbdata = destroy_cbdata;
388  g_source_set_priority(source, G_PRIORITY_DEFAULT);
389  g_source_set_callback(source, _run_task, t, _destroy_task);
390  g_source_attach(source, context);
391  g_source_unref(source);
392}
Note: See TracBrowser for help on using the repository browser.