source: select.c @ ba12b44

release-1.8release-1.9
Last change on this file since ba12b44 was ba12b44, checked in by David Benjamin <davidben@mit.edu>, 10 years ago
Add owl_select_post_task helper function Honestly, the only thing you'd ever want to do to a message loop from another thread is post a one-off task to it. Why Glib makes this so difficult is beyond me.
  • Property mode set to 100644
File size: 9.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
128static const owl_io_dispatch *owl_select_find_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) 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
158void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
159{
160  int elt;
161  if (in != NULL) {
162    elt = owl_select_find_io_dispatch(in);
163    if (elt != -1) {
164      owl_list *dl = owl_global_get_io_dispatch_list(&g);
165      owl_io_dispatch *d = owl_list_get_element(dl, elt);
166      if (dispatch_active)
167        d->needs_gc = 1;
168      else {
169        owl_list_remove_element(dl, elt);
170        if (d->destroy)
171          d->destroy(d);
172        g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
173        g_free(d);
174      }
175    }
176  }
177}
178
179static void owl_select_io_dispatch_gc(void)
180{
181  int i;
182  owl_list *dl;
183
184  dl = owl_global_get_io_dispatch_list(&g);
185  /*
186   * Count down so we aren't set off by removing items from the list
187   * during the iteration.
188   */
189  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
190    owl_io_dispatch *d = owl_list_get_element(dl, i);
191    if(d->needs_gc) {
192      owl_select_remove_io_dispatch(d);
193    }
194  }
195}
196
197/* Each FD may have at most one dispatcher.
198 * If a new dispatch is added for an FD, the old one is removed.
199 * mode determines what types of events are watched for, and may be any combination of:
200 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
201 */
202const 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)
203{
204  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
205  owl_list *dl = owl_global_get_io_dispatch_list(&g);
206
207  d->fd = fd;
208  d->needs_gc = 0;
209  d->mode = mode;
210  d->callback = cb;
211  d->destroy = destroy;
212  d->data = data;
213
214  /* TODO: Allow changing fd and mode in the middle? Probably don't care... */
215  d->pollfd.fd = fd;
216  d->pollfd.events = 0;
217  if (d->mode & OWL_IO_READ)
218    d->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
219  if (d->mode & OWL_IO_WRITE)
220    d->pollfd.events |= G_IO_OUT | G_IO_ERR;
221  if (d->mode & OWL_IO_EXCEPT)
222    d->pollfd.events |= G_IO_PRI | G_IO_ERR;
223  g_source_add_poll(owl_io_dispatch_source, &d->pollfd);
224
225
226  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
227  owl_list_append_element(dl, d);
228
229  return d;
230}
231
232static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
233  *timeout = -1;
234  return FALSE;
235}
236
237static gboolean owl_io_dispatch_check(GSource *source) {
238  int i, len;
239  const owl_list *dl;
240
241  dl = owl_global_get_io_dispatch_list(&g);
242  len = owl_list_get_size(dl);
243  for(i = 0; i < len; i++) {
244    const owl_io_dispatch *d = owl_list_get_element(dl, i);
245    if (d->pollfd.revents & d->pollfd.events)
246      return TRUE;
247  }
248  return FALSE;
249}
250
251static gboolean owl_io_dispatch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
252  int i, len;
253  const owl_list *dl;
254
255  dispatch_active = 1;
256  dl = owl_global_get_io_dispatch_list(&g);
257  len = owl_list_get_size(dl);
258  for (i = 0; i < len; i++) {
259    owl_io_dispatch *d = owl_list_get_element(dl, i);
260    if ((d->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
261      d->callback(d, d->data);
262    }
263  }
264  dispatch_active = 0;
265  owl_select_io_dispatch_gc();
266
267  return TRUE;
268}
269
270static GSourceFuncs owl_io_dispatch_funcs = {
271  owl_io_dispatch_prepare,
272  owl_io_dispatch_check,
273  owl_io_dispatch_dispatch,
274  NULL
275};
276
277int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
278{
279  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
280  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
281    /* Don't mess with non-perl dispatch functions from here. */
282    return 1;
283  }
284  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
285  return 0;
286}
287
288int owl_select_remove_perl_io_dispatch(int fd)
289{
290  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
291  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
292    /* Only remove perl io dispatchers from here. */
293    owl_select_remove_io_dispatch(d);
294    return 0;
295  }
296  return 1;
297}
298
299void owl_select_init(void)
300{
301  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
302  g_source_attach(owl_timer_source, NULL);
303
304  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
305  g_source_attach(owl_io_dispatch_source, NULL);
306}
307
308void owl_select_run_loop(void)
309{
310  context = g_main_context_default();
311  loop = g_main_loop_new(context, FALSE);
312  g_main_loop_run(loop);
313}
314
315void owl_select_quit_loop(void)
316{
317  if (loop) {
318    g_main_loop_quit(loop);
319    loop = NULL;
320  }
321}
322
323typedef struct _owl_task { /*noproto*/
324  void (*cb)(void *);
325  void *cbdata;
326  void (*destroy_cbdata)(void *);
327} owl_task;
328
329static gboolean _run_task(gpointer data)
330{
331  owl_task *t = data;
332  if (t->cb)
333    t->cb(t->cbdata);
334  return FALSE;
335}
336
337static void _destroy_task(void *data)
338{
339  owl_task *t = data;
340  if (t->destroy_cbdata)
341    t->destroy_cbdata(t->cbdata);
342  g_free(t);
343}
344
345void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*))
346{
347  GSource *source = g_idle_source_new();
348  owl_task *t = g_new0(owl_task, 1);
349  t->cb = cb;
350  t->cbdata = cbdata;
351  t->destroy_cbdata = destroy_cbdata;
352  g_source_set_priority(source, G_PRIORITY_DEFAULT);
353  g_source_set_callback(source, _run_task, t, _destroy_task);
354  g_source_attach(source, context);
355  g_source_unref(source);
356}
Note: See TracBrowser for help on using the repository browser.