source: select.c @ bcde7926

release-1.8release-1.9
Last change on this file since bcde7926 was bcde7926, 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    g_main_loop_unref(loop);
195    loop = NULL;
196  }
197}
198
199typedef struct _owl_task { /*noproto*/
200  void (*cb)(void *);
201  void *cbdata;
202  void (*destroy_cbdata)(void *);
203} owl_task;
204
205static gboolean _run_task(gpointer data)
206{
207  owl_task *t = data;
208  if (t->cb)
209    t->cb(t->cbdata);
210  return FALSE;
211}
212
213static void _destroy_task(void *data)
214{
215  owl_task *t = data;
216  if (t->destroy_cbdata)
217    t->destroy_cbdata(t->cbdata);
218  g_free(t);
219}
220
221void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*), GMainContext *context)
222{
223  GSource *source = g_idle_source_new();
224  owl_task *t = g_new0(owl_task, 1);
225  t->cb = cb;
226  t->cbdata = cbdata;
227  t->destroy_cbdata = destroy_cbdata;
228  g_source_set_priority(source, G_PRIORITY_DEFAULT);
229  g_source_set_callback(source, _run_task, t, _destroy_task);
230  g_source_attach(source, context);
231  g_source_unref(source);
232}
Note: See TracBrowser for help on using the repository browser.