source: select.c @ f21bc36

Last change on this file since f21bc36 was f21bc36, checked in by David Benjamin <davidben@mit.edu>, 10 years ago
Reimplement BarnOwl::add_io_dispatch with AnyEvent We can emulate the interesting semantics with perl. The one difference is that perl code can now register an IO dispatch on file descriptors C code was interested in. This isn't a big deal was Glib can handle multiple watches on the same FD. Granted, more than one reader on an FD would cause trouble, but there was nothing stopping perl code from reading from an FD we cared about anyway. AnyEvent also does not support select's exceptfd, so this is a slight behavior change, but probably an uninteresting one.
  • Property mode set to 100644
File size: 5.7 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
177void owl_select_init(void)
178{
179  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
180  g_source_attach(owl_io_dispatch_source, NULL);
181}
182
183void owl_select_run_loop(void)
184{
185  main_context = g_main_context_default();
186  loop = g_main_loop_new(main_context, FALSE);
187  g_main_loop_run(loop);
188}
189
190void owl_select_quit_loop(void)
191{
192  if (loop) {
193    g_main_loop_quit(loop);
194    loop = NULL;
195  }
196}
197
198typedef struct _owl_task { /*noproto*/
199  void (*cb)(void *);
200  void *cbdata;
201  void (*destroy_cbdata)(void *);
202} owl_task;
203
204static gboolean _run_task(gpointer data)
205{
206  owl_task *t = data;
207  if (t->cb)
208    t->cb(t->cbdata);
209  return FALSE;
210}
211
212static void _destroy_task(void *data)
213{
214  owl_task *t = data;
215  if (t->destroy_cbdata)
216    t->destroy_cbdata(t->cbdata);
217  g_free(t);
218}
219
220void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*), GMainContext *context)
221{
222  GSource *source = g_idle_source_new();
223  owl_task *t = g_new0(owl_task, 1);
224  t->cb = cb;
225  t->cbdata = cbdata;
226  t->destroy_cbdata = destroy_cbdata;
227  g_source_set_priority(source, G_PRIORITY_DEFAULT);
228  g_source_set_callback(source, _run_task, t, _destroy_task);
229  g_source_attach(source, context);
230  g_source_unref(source);
231}
Note: See TracBrowser for help on using the repository browser.