source: select.c @ 7df7be2

Last change on this file since 7df7be2 was 7df7be2, checked in by David Benjamin <davidben@mit.edu>, 11 years ago
Replace BarnOwl::Timer with a perl wrapper over AnyEvent This also allows us to kill owl_timer altogether.
  • Property mode set to 100644
File size: 6.9 KB
Line 
1#include "owl.h"
2
3static GMainLoop *loop = NULL;
4static GMainContext *main_context;
5static int dispatch_active = 0;
6
7static GSource *owl_io_dispatch_source;
8
9/* Returns the valid owl_io_dispatch for a given file descriptor. */
10static owl_io_dispatch *owl_select_find_valid_io_dispatch_by_fd(const int fd)
11{
12  int i, len;
13  const owl_list *dl;
14  owl_io_dispatch *d;
15  dl = owl_global_get_io_dispatch_list(&g);
16  len = owl_list_get_size(dl);
17  for(i = 0; i < len; i++) {
18    d = owl_list_get_element(dl, i);
19    if (d->fd == fd && d->valid) return d;
20  }
21  return NULL;
22}
23
24static int owl_select_find_io_dispatch(const owl_io_dispatch *in)
25{
26  int i, len;
27  const owl_list *dl;
28
29  if (in != NULL) {
30    dl = owl_global_get_io_dispatch_list(&g);
31    len = owl_list_get_size(dl);
32    for(i = 0; i < len; i++) {
33      const owl_io_dispatch *d = owl_list_get_element(dl, i);
34      if (d == in) return i;
35    }
36  }
37  return -1;
38}
39
40static void owl_select_invalidate_io_dispatch(owl_io_dispatch *d)
41{
42  if (d == NULL || !d->valid)
43    return;
44  d->valid = false;
45  g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
46}
47
48void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
49{
50  int elt;
51  if (in != NULL) {
52    elt = owl_select_find_io_dispatch(in);
53    if (elt != -1) {
54      owl_list *dl = owl_global_get_io_dispatch_list(&g);
55      owl_io_dispatch *d = owl_list_get_element(dl, elt);
56      if (dispatch_active)
57        d->needs_gc = 1;
58      else {
59        owl_select_invalidate_io_dispatch(d);
60        owl_list_remove_element(dl, elt);
61        if (d->destroy)
62          d->destroy(d);
63        g_free(d);
64      }
65    }
66  }
67}
68
69static void owl_select_io_dispatch_gc(void)
70{
71  int i;
72  owl_list *dl;
73
74  dl = owl_global_get_io_dispatch_list(&g);
75  /*
76   * Count down so we aren't set off by removing items from the list
77   * during the iteration.
78   */
79  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
80    owl_io_dispatch *d = owl_list_get_element(dl, i);
81    if(d->needs_gc) {
82      owl_select_remove_io_dispatch(d);
83    }
84  }
85}
86
87/* Each FD may have at most one valid dispatcher.
88 * If a new dispatch is added for an FD, the old one is removed.
89 * mode determines what types of events are watched for, and may be any combination of:
90 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
91 */
92const 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)
93{
94  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
95  owl_list *dl = owl_global_get_io_dispatch_list(&g);
96  owl_io_dispatch *other;
97
98  d->fd = fd;
99  d->valid = true;
100  d->needs_gc = 0;
101  d->mode = mode;
102  d->callback = cb;
103  d->destroy = destroy;
104  d->data = data;
105
106  /* TODO: Allow changing fd and mode in the middle? Probably don't care... */
107  d->pollfd.fd = fd;
108  d->pollfd.events = 0;
109  if (d->mode & OWL_IO_READ)
110    d->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
111  if (d->mode & OWL_IO_WRITE)
112    d->pollfd.events |= G_IO_OUT | G_IO_ERR;
113  if (d->mode & OWL_IO_EXCEPT)
114    d->pollfd.events |= G_IO_PRI | G_IO_ERR;
115  g_source_add_poll(owl_io_dispatch_source, &d->pollfd);
116
117
118  other = owl_select_find_valid_io_dispatch_by_fd(fd);
119  if (other)
120    owl_select_invalidate_io_dispatch(other);
121  owl_list_append_element(dl, d);
122
123  return d;
124}
125
126static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
127  *timeout = -1;
128  return FALSE;
129}
130
131static gboolean owl_io_dispatch_check(GSource *source) {
132  int i, len;
133  const owl_list *dl;
134
135  dl = owl_global_get_io_dispatch_list(&g);
136  len = owl_list_get_size(dl);
137  for(i = 0; i < len; i++) {
138    owl_io_dispatch *d = owl_list_get_element(dl, i);
139    if (!d->valid) continue;
140    if (d->pollfd.revents & G_IO_NVAL) {
141      owl_function_debugmsg("Pruning defunct dispatch on fd %d.", d->fd);
142      owl_select_invalidate_io_dispatch(d);
143    }
144    if (d->pollfd.revents & d->pollfd.events)
145      return TRUE;
146  }
147  return FALSE;
148}
149
150static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
151  int i, len;
152  const owl_list *dl;
153
154  dispatch_active = 1;
155  dl = owl_global_get_io_dispatch_list(&g);
156  len = owl_list_get_size(dl);
157  for (i = 0; i < len; i++) {
158    owl_io_dispatch *d = owl_list_get_element(dl, i);
159    if (!d->valid) continue;
160    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
161      d->callback(d, d->data);
162    }
163  }
164  dispatch_active = 0;
165  owl_select_io_dispatch_gc();
166
167  return TRUE;
168}
169
170static GSourceFuncs owl_io_dispatch_funcs = {
171  owl_io_dispatch_prepare,
172  owl_io_dispatch_check,
173  owl_io_dispatch_dispatch,
174  NULL
175};
176
177int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
178{
179  const owl_io_dispatch *d = owl_select_find_valid_io_dispatch_by_fd(fd);
180  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
181    /* Don't mess with non-perl dispatch functions from here. */
182    return 1;
183  }
184  /* Also remove any invalidated perl dispatch functions that may have
185   * stuck around. */
186  owl_select_remove_perl_io_dispatch(fd);
187  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
188  return 0;
189}
190
191static owl_io_dispatch *owl_select_find_perl_io_dispatch(int fd)
192{
193  int i, len;
194  const owl_list *dl;
195  owl_io_dispatch *d;
196  dl = owl_global_get_io_dispatch_list(&g);
197  len = owl_list_get_size(dl);
198  for(i = 0; i < len; i++) {
199    d = owl_list_get_element(dl, i);
200    if (d->fd == fd && d->callback == owl_perlconfig_io_dispatch)
201      return d;
202  }
203  return NULL;
204}
205
206int owl_select_remove_perl_io_dispatch(int fd)
207{
208  owl_io_dispatch *d = owl_select_find_perl_io_dispatch(fd);
209  if (d != NULL) {
210    /* Only remove perl io dispatchers from here. */
211    owl_select_remove_io_dispatch(d);
212    return 0;
213  }
214  return 1;
215}
216
217void owl_select_init(void)
218{
219  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
220  g_source_attach(owl_io_dispatch_source, NULL);
221}
222
223void owl_select_run_loop(void)
224{
225  main_context = g_main_context_default();
226  loop = g_main_loop_new(main_context, FALSE);
227  g_main_loop_run(loop);
228}
229
230void owl_select_quit_loop(void)
231{
232  if (loop) {
233    g_main_loop_quit(loop);
234    loop = NULL;
235  }
236}
237
238typedef struct _owl_task { /*noproto*/
239  void (*cb)(void *);
240  void *cbdata;
241  void (*destroy_cbdata)(void *);
242} owl_task;
243
244static gboolean _run_task(gpointer data)
245{
246  owl_task *t = data;
247  if (t->cb)
248    t->cb(t->cbdata);
249  return FALSE;
250}
251
252static void _destroy_task(void *data)
253{
254  owl_task *t = data;
255  if (t->destroy_cbdata)
256    t->destroy_cbdata(t->cbdata);
257  g_free(t);
258}
259
260void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*), GMainContext *context)
261{
262  GSource *source = g_idle_source_new();
263  owl_task *t = g_new0(owl_task, 1);
264  t->cb = cb;
265  t->cbdata = cbdata;
266  t->destroy_cbdata = destroy_cbdata;
267  g_source_set_priority(source, G_PRIORITY_DEFAULT);
268  g_source_set_callback(source, _run_task, t, _destroy_task);
269  g_source_attach(source, context);
270  g_source_unref(source);
271}
Note: See TracBrowser for help on using the repository browser.