source: select.c @ 7655e08

Last change on this file since 7655e08 was 111850c, checked in by David Benjamin <davidben@mit.edu>, 13 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.1 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);
11static void owl_select_io_dispatch_gc(void);
12
13static gboolean owl_timer_prepare(GSource *source, int *timeout) {
14  GList **timers = owl_global_get_timerlist(&g);
15  GTimeVal now;
16
17  /* TODO: In the far /far/ future, g_source_get_time is what the cool
18   * kids use to get system monotonic time. */
19  g_source_get_current_time(source, &now);
20
21  /* FIXME: bother with millisecond accuracy now that we can? */
22  if (*timers) {
23    owl_timer *t = (*timers)->data;
24    *timeout = t->time - now.tv_sec;
25    if (*timeout <= 0) {
26      *timeout = 0;
27      return TRUE;
28    }
29    if (*timeout > 60 * 1000)
30      *timeout = 60 * 1000;
31  } else {
32    *timeout = 60 * 1000;
33  }
34  return FALSE;
35}
36
37static gboolean owl_timer_check(GSource *source) {
38  GList **timers = owl_global_get_timerlist(&g);
39  GTimeVal now;
40
41  /* TODO: In the far /far/ future, g_source_get_time is what the cool
42   * kids use to get system monotonic time. */
43  g_source_get_current_time(source, &now);
44
45  /* FIXME: bother with millisecond accuracy now that we can? */
46  if (*timers) {
47    owl_timer *t = (*timers)->data;
48    return t->time >= now.tv_sec;
49  }
50  return FALSE;
51}
52
53
54static gboolean owl_timer_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
55  GList **timers = owl_global_get_timerlist(&g);
56  GTimeVal now;
57
58  /* TODO: In the far /far/ future, g_source_get_time is what the cool
59   * kids use to get system monotonic time. */
60  g_source_get_current_time(source, &now);
61
62  /* FIXME: bother with millisecond accuracy now that we can? */
63  while(*timers) {
64    owl_timer *t = (*timers)->data;
65    int remove = 0;
66
67    if(t->time > now.tv_sec)
68      break;
69
70    /* Reschedule if appropriate */
71    if(t->interval > 0) {
72      t->time = now.tv_sec + t->interval;
73      *timers = g_list_remove(*timers, t);
74      *timers = g_list_insert_sorted(*timers, t,
75                                     (GCompareFunc)_owl_select_timer_cmp);
76    } else {
77      remove = 1;
78    }
79
80    /* Do the callback */
81    t->callback(t, t->data);
82    if(remove) {
83      owl_select_remove_timer(t);
84    }
85  }
86  return TRUE;
87}
88
89static GSourceFuncs owl_timer_funcs = {
90  owl_timer_prepare,
91  owl_timer_check,
92  owl_timer_dispatch,
93  NULL
94};
95
96static int _owl_select_timer_cmp(const owl_timer *t1, const owl_timer *t2) {
97  return t1->time - t2->time;
98}
99
100owl_timer *owl_select_add_timer(const char* name, int after, int interval, void (*cb)(owl_timer *, void *), void (*destroy)(owl_timer*), void *data)
101{
102  owl_timer *t = g_new(owl_timer, 1);
103  GList **timers = owl_global_get_timerlist(&g);
104
105  t->time = time(NULL) + after;
106  t->interval = interval;
107  t->callback = cb;
108  t->destroy = destroy;
109  t->data = data;
110  t->name = name ? g_strdup(name) : NULL;
111
112  *timers = g_list_insert_sorted(*timers, t,
113                                 (GCompareFunc)_owl_select_timer_cmp);
114  return t;
115}
116
117void owl_select_remove_timer(owl_timer *t)
118{
119  GList **timers = owl_global_get_timerlist(&g);
120  if (t && g_list_find(*timers, t)) {
121    *timers = g_list_remove(*timers, t);
122    if(t->destroy) {
123      t->destroy(t);
124    }
125    g_free(t->name);
126    g_free(t);
127  }
128}
129
130
131static gboolean owl_io_dispatch_prepare(GSource *source, int *timeout) {
132  *timeout = -1;
133  return FALSE;
134}
135
136static gboolean owl_io_dispatch_check(GSource *source) {
137  int i, len;
138  const owl_list *dl;
139
140  dl = owl_global_get_io_dispatch_list(&g);
141  len = owl_list_get_size(dl);
142  for(i = 0; i < len; i++) {
143    const owl_io_dispatch *d = owl_list_get_element(dl, i);
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->pollfd.revents & d->pollfd.events) && d->callback != NULL) {
160      d->callback(d, d->data);
161    }
162  }
163  dispatch_active = 0;
164  owl_select_io_dispatch_gc();
165
166  return TRUE;
167}
168
169static GSourceFuncs owl_io_dispatch_funcs = {
170  owl_io_dispatch_prepare,
171  owl_io_dispatch_check,
172  owl_io_dispatch_dispatch,
173  NULL
174};
175
176static const owl_io_dispatch *owl_select_find_io_dispatch_by_fd(const int fd)
177{
178  int i, len;
179  const owl_list *dl;
180  owl_io_dispatch *d;
181  dl = owl_global_get_io_dispatch_list(&g);
182  len = owl_list_get_size(dl);
183  for(i = 0; i < len; i++) {
184    d = owl_list_get_element(dl, i);
185    if (d->fd == fd) return d;
186  }
187  return NULL;
188}
189
190static int owl_select_find_io_dispatch(const owl_io_dispatch *in)
191{
192  int i, len;
193  const owl_list *dl;
194
195  if (in != NULL) {
196    dl = owl_global_get_io_dispatch_list(&g);
197    len = owl_list_get_size(dl);
198    for(i = 0; i < len; i++) {
199      const owl_io_dispatch *d = owl_list_get_element(dl, i);
200      if (d == in) return i;
201    }
202  }
203  return -1;
204}
205
206void owl_select_remove_io_dispatch(const owl_io_dispatch *in)
207{
208  int elt;
209  if (in != NULL) {
210    elt = owl_select_find_io_dispatch(in);
211    if (elt != -1) {
212      owl_list *dl = owl_global_get_io_dispatch_list(&g);
213      owl_io_dispatch *d = owl_list_get_element(dl, elt);
214      if (dispatch_active)
215        d->needs_gc = 1;
216      else {
217        owl_list_remove_element(dl, elt);
218        if (d->destroy)
219          d->destroy(d);
220        g_source_remove_poll(owl_io_dispatch_source, &d->pollfd);
221        g_free(d);
222      }
223    }
224  }
225}
226
227static void owl_select_io_dispatch_gc(void)
228{
229  int i;
230  owl_list *dl;
231
232  dl = owl_global_get_io_dispatch_list(&g);
233  /*
234   * Count down so we aren't set off by removing items from the list
235   * during the iteration.
236   */
237  for(i = owl_list_get_size(dl) - 1; i >= 0; i--) {
238    owl_io_dispatch *d = owl_list_get_element(dl, i);
239    if(d->needs_gc) {
240      owl_select_remove_io_dispatch(d);
241    }
242  }
243}
244
245/* Each FD may have at most one dispatcher.
246 * If a new dispatch is added for an FD, the old one is removed.
247 * mode determines what types of events are watched for, and may be any combination of:
248 * OWL_IO_READ, OWL_IO_WRITE, OWL_IO_EXCEPT
249 */
250const 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)
251{
252  owl_io_dispatch *d = g_new(owl_io_dispatch, 1);
253  owl_list *dl = owl_global_get_io_dispatch_list(&g);
254
255  d->fd = fd;
256  d->needs_gc = 0;
257  d->mode = mode;
258  d->callback = cb;
259  d->destroy = destroy;
260  d->data = data;
261
262  /* TODO: Allow changing fd and mode in the middle? Probably don't care... */
263  d->pollfd.fd = fd;
264  d->pollfd.events = 0;
265  if (d->mode & OWL_IO_READ)
266    d->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
267  if (d->mode & OWL_IO_WRITE)
268    d->pollfd.events |= G_IO_OUT | G_IO_ERR;
269  if (d->mode & OWL_IO_EXCEPT)
270    d->pollfd.events |= G_IO_PRI | G_IO_ERR;
271  g_source_add_poll(owl_io_dispatch_source, &d->pollfd);
272
273
274  owl_select_remove_io_dispatch(owl_select_find_io_dispatch_by_fd(fd));
275  owl_list_append_element(dl, d);
276
277  return d;
278}
279
280int owl_select_add_perl_io_dispatch(int fd, int mode, SV *cb)
281{
282  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
283  if (d != NULL && d->callback != owl_perlconfig_io_dispatch) {
284    /* Don't mess with non-perl dispatch functions from here. */
285    return 1;
286  }
287  owl_select_add_io_dispatch(fd, mode, owl_perlconfig_io_dispatch, owl_perlconfig_io_dispatch_destroy, cb);
288  return 0;
289}
290
291int owl_select_remove_perl_io_dispatch(int fd)
292{
293  const owl_io_dispatch *d = owl_select_find_io_dispatch_by_fd(fd);
294  if (d != NULL && d->callback == owl_perlconfig_io_dispatch) {
295    /* Only remove perl io dispatchers from here. */
296    owl_select_remove_io_dispatch(d);
297    return 0;
298  }
299  return 1;
300}
301
302void owl_select_init(void)
303{
304  owl_timer_source = g_source_new(&owl_timer_funcs, sizeof(GSource));
305  g_source_attach(owl_timer_source, NULL);
306
307  owl_io_dispatch_source = g_source_new(&owl_io_dispatch_funcs, sizeof(GSource));
308  g_source_attach(owl_io_dispatch_source, NULL);
309}
310
311void owl_select_run_loop(void)
312{
313  context = g_main_context_default();
314  loop = g_main_loop_new(context, FALSE);
315  g_main_loop_run(loop);
316}
317
318void owl_select_quit_loop(void)
319{
320  if (loop) {
321    g_main_loop_quit(loop);
322    loop = NULL;
323  }
324}
325
326typedef struct _owl_task { /*noproto*/
327  void (*cb)(void *);
328  void *cbdata;
329  void (*destroy_cbdata)(void *);
330} owl_task;
331
332static gboolean _run_task(gpointer data)
333{
334  owl_task *t = data;
335  if (t->cb)
336    t->cb(t->cbdata);
337  return FALSE;
338}
339
340static void _destroy_task(void *data)
341{
342  owl_task *t = data;
343  if (t->destroy_cbdata)
344    t->destroy_cbdata(t->cbdata);
345  g_free(t);
346}
347
348void owl_select_post_task(void (*cb)(void*), void *cbdata, void (*destroy_cbdata)(void*))
349{
350  GSource *source = g_idle_source_new();
351  owl_task *t = g_new0(owl_task, 1);
352  t->cb = cb;
353  t->cbdata = cbdata;
354  t->destroy_cbdata = destroy_cbdata;
355  g_source_set_priority(source, G_PRIORITY_DEFAULT);
356  g_source_set_callback(source, _run_task, t, _destroy_task);
357  g_source_attach(source, context);
358  g_source_unref(source);
359}
Note: See TracBrowser for help on using the repository browser.