]> code.delx.au - pulseaudio/blob - src/pulse/glib-mainloop.c
big s/polyp/pulse/g
[pulseaudio] / src / pulse / glib-mainloop.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <assert.h>
27
28 #include <pulse/xmalloc.h>
29 #include <pulse/timeval.h>
30
31 #include <pulsecore/idxset.h>
32 #include <pulsecore/core-util.h>
33
34 #include "glib.h"
35 #include "glib-mainloop.h"
36
37 struct pa_io_event {
38 pa_glib_mainloop *mainloop;
39 int dead;
40 GIOChannel *io_channel;
41 GSource *source;
42 GIOCondition io_condition;
43 int fd;
44 void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
45 void *userdata;
46 void (*destroy_callback) (pa_mainloop_api *m, pa_io_event *e, void *userdata);
47 pa_io_event *next, *prev;
48 };
49
50 struct pa_time_event {
51 pa_glib_mainloop *mainloop;
52 int dead;
53 GSource *source;
54 struct timeval timeval;
55 void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata);
56 void *userdata;
57 void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata);
58 pa_time_event *next, *prev;
59 };
60
61 struct pa_defer_event {
62 pa_glib_mainloop *mainloop;
63 int dead;
64 GSource *source;
65 void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata);
66 void *userdata;
67 void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata);
68 pa_defer_event *next, *prev;
69 };
70
71 struct pa_glib_mainloop {
72 GMainContext *glib_main_context;
73 pa_mainloop_api api;
74 GSource *cleanup_source;
75 pa_io_event *io_events, *dead_io_events;
76 pa_time_event *time_events, *dead_time_events;
77 pa_defer_event *defer_events, *dead_defer_events;
78 };
79
80 static void schedule_free_dead_events(pa_glib_mainloop *g);
81
82 static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f);
83
84 static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) {
85 pa_io_event *e;
86 pa_glib_mainloop *g;
87
88 assert(m && m->userdata && fd >= 0 && callback);
89 g = m->userdata;
90
91 e = pa_xmalloc(sizeof(pa_io_event));
92 e->mainloop = m->userdata;
93 e->dead = 0;
94 e->fd = fd;
95 e->callback = callback;
96 e->userdata = userdata;
97 e->destroy_callback = NULL;
98
99 e->io_channel = g_io_channel_unix_new(e->fd);
100 assert(e->io_channel);
101 e->source = NULL;
102 e->io_condition = 0;
103
104 glib_io_enable(e, f);
105
106 e->next = g->io_events;
107 if (e->next) e->next->prev = e;
108 g->io_events = e;
109 e->prev = NULL;
110
111 return e;
112 }
113
114 /* The callback GLIB calls whenever an IO condition is met */
115 static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
116 pa_io_event *e = data;
117 pa_io_event_flags_t f;
118 assert(source && e && e->io_channel == source);
119
120 f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
121 (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
122 (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
123 (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
124
125 e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
126 return TRUE;
127 }
128
129 static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
130 GIOCondition c;
131 assert(e && !e->dead);
132
133 c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
134
135 if (c == e->io_condition)
136 return;
137
138 if (e->source) {
139 g_source_destroy(e->source);
140 g_source_unref(e->source);
141 }
142
143 e->source = g_io_create_watch(e->io_channel, c | G_IO_ERR | G_IO_HUP);
144 assert(e->source);
145
146 g_source_set_callback(e->source, (GSourceFunc) io_cb, e, NULL);
147 g_source_attach(e->source, e->mainloop->glib_main_context);
148 g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
149
150 e->io_condition = c;
151 }
152
153 static void glib_io_free(pa_io_event*e) {
154 assert(e && !e->dead);
155
156 if (e->source) {
157 g_source_destroy(e->source);
158 g_source_unref(e->source);
159 e->source = NULL;
160 }
161
162 if (e->prev)
163 e->prev->next = e->next;
164 else
165 e->mainloop->io_events = e->next;
166
167 if (e->next)
168 e->next->prev = e->prev;
169
170 if ((e->next = e->mainloop->dead_io_events))
171 e->next->prev = e;
172
173 e->mainloop->dead_io_events = e;
174 e->prev = NULL;
175
176 e->dead = 1;
177 schedule_free_dead_events(e->mainloop);
178 }
179
180 static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) {
181 assert(e);
182 e->destroy_callback = callback;
183 }
184
185 /* Time sources */
186
187 static void glib_time_restart(pa_time_event*e, const struct timeval *tv);
188
189 static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
190 pa_glib_mainloop *g;
191 pa_time_event *e;
192
193 assert(m && m->userdata && tv && callback);
194 g = m->userdata;
195
196 e = pa_xmalloc(sizeof(pa_time_event));
197 e->mainloop = g;
198 e->dead = 0;
199 e->callback = callback;
200 e->userdata = userdata;
201 e->destroy_callback = NULL;
202 e->source = NULL;
203
204 glib_time_restart(e, tv);
205
206 e->next = g->time_events;
207 if (e->next) e->next->prev = e;
208 g->time_events = e;
209 e->prev = NULL;
210
211 return e;
212 }
213
214 static guint msec_diff(const struct timeval *a, const struct timeval *b) {
215 guint r;
216 assert(a && b);
217
218 if (a->tv_sec < b->tv_sec)
219 return 0;
220
221 if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
222 return 0;
223
224 r = (a->tv_sec-b->tv_sec)*1000;
225
226 if (a->tv_usec >= b->tv_usec)
227 r += (a->tv_usec - b->tv_usec) / 1000;
228 else
229 r -= (b->tv_usec - a->tv_usec) / 1000;
230
231 return r;
232 }
233
234 static gboolean time_cb(gpointer data) {
235 pa_time_event* e = data;
236 assert(e && e->mainloop && e->source);
237
238 g_source_unref(e->source);
239 e->source = NULL;
240
241 e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
242 return FALSE;
243 }
244
245 static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
246 struct timeval now;
247 assert(e && e->mainloop && !e->dead);
248
249 pa_gettimeofday(&now);
250 if (e->source) {
251 g_source_destroy(e->source);
252 g_source_unref(e->source);
253 }
254
255 if (tv) {
256 e->timeval = *tv;
257 e->source = g_timeout_source_new(msec_diff(tv, &now));
258 assert(e->source);
259 g_source_set_callback(e->source, time_cb, e, NULL);
260 g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
261 g_source_attach(e->source, e->mainloop->glib_main_context);
262 } else
263 e->source = NULL;
264 }
265
266 static void glib_time_free(pa_time_event *e) {
267 assert(e && e->mainloop && !e->dead);
268
269 if (e->source) {
270 g_source_destroy(e->source);
271 g_source_unref(e->source);
272 e->source = NULL;
273 }
274
275 if (e->prev)
276 e->prev->next = e->next;
277 else
278 e->mainloop->time_events = e->next;
279
280 if (e->next)
281 e->next->prev = e->prev;
282
283 if ((e->next = e->mainloop->dead_time_events))
284 e->next->prev = e;
285
286 e->mainloop->dead_time_events = e;
287 e->prev = NULL;
288
289 e->dead = 1;
290 schedule_free_dead_events(e->mainloop);
291 }
292
293 static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) {
294 assert(e);
295 e->destroy_callback = callback;
296 }
297
298 /* Deferred sources */
299
300 static void glib_defer_enable(pa_defer_event *e, int b);
301
302 static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) {
303 pa_defer_event *e;
304 pa_glib_mainloop *g;
305
306 assert(m && m->userdata && callback);
307 g = m->userdata;
308
309 e = pa_xmalloc(sizeof(pa_defer_event));
310 e->mainloop = g;
311 e->dead = 0;
312 e->callback = callback;
313 e->userdata = userdata;
314 e->destroy_callback = NULL;
315 e->source = NULL;
316
317 glib_defer_enable(e, 1);
318
319 e->next = g->defer_events;
320 if (e->next) e->next->prev = e;
321 g->defer_events = e;
322 e->prev = NULL;
323 return e;
324 }
325
326 static gboolean idle_cb(gpointer data) {
327 pa_defer_event* e = data;
328 assert(e && e->mainloop && e->source);
329
330 e->callback(&e->mainloop->api, e, e->userdata);
331 return TRUE;
332 }
333
334 static void glib_defer_enable(pa_defer_event *e, int b) {
335 assert(e && e->mainloop);
336
337 if (e->source && !b) {
338 g_source_destroy(e->source);
339 g_source_unref(e->source);
340 e->source = NULL;
341 } else if (!e->source && b) {
342 e->source = g_idle_source_new();
343 assert(e->source);
344 g_source_set_callback(e->source, idle_cb, e, NULL);
345 g_source_attach(e->source, e->mainloop->glib_main_context);
346 g_source_set_priority(e->source, G_PRIORITY_HIGH);
347 }
348 }
349
350 static void glib_defer_free(pa_defer_event *e) {
351 assert(e && e->mainloop && !e->dead);
352
353 if (e->source) {
354 g_source_destroy(e->source);
355 g_source_unref(e->source);
356 e->source = NULL;
357 }
358
359 if (e->prev)
360 e->prev->next = e->next;
361 else
362 e->mainloop->defer_events = e->next;
363
364 if (e->next)
365 e->next->prev = e->prev;
366
367 if ((e->next = e->mainloop->dead_defer_events))
368 e->next->prev = e;
369
370 e->mainloop->dead_defer_events = e;
371 e->prev = NULL;
372
373 e->dead = 1;
374 schedule_free_dead_events(e->mainloop);
375 }
376
377 static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) {
378 assert(e);
379 e->destroy_callback = callback;
380 }
381
382 /* quit() */
383
384 static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) {
385 pa_glib_mainloop *g;
386 assert(a && a->userdata);
387 g = a->userdata;
388
389 /* NOOP */
390 }
391
392 static const pa_mainloop_api vtable = {
393 .userdata = NULL,
394
395 .io_new = glib_io_new,
396 .io_enable = glib_io_enable,
397 .io_free = glib_io_free,
398 .io_set_destroy= glib_io_set_destroy,
399
400 .time_new = glib_time_new,
401 .time_restart = glib_time_restart,
402 .time_free = glib_time_free,
403 .time_set_destroy = glib_time_set_destroy,
404
405 .defer_new = glib_defer_new,
406 .defer_enable = glib_defer_enable,
407 .defer_free = glib_defer_free,
408 .defer_set_destroy = glib_defer_set_destroy,
409
410 .quit = glib_quit,
411 };
412
413 pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
414 pa_glib_mainloop *g;
415
416 g = pa_xmalloc(sizeof(pa_glib_mainloop));
417 if (c) {
418 g->glib_main_context = c;
419 g_main_context_ref(c);
420 } else
421 g->glib_main_context = g_main_context_default();
422
423 g->api = vtable;
424 g->api.userdata = g;
425
426 g->io_events = g->dead_io_events = NULL;
427 g->time_events = g->dead_time_events = NULL;
428 g->defer_events = g->dead_defer_events = NULL;
429
430 g->cleanup_source = NULL;
431 return g;
432 }
433
434 static void free_io_events(pa_io_event *e) {
435 while (e) {
436 pa_io_event *r = e;
437 e = r->next;
438
439 if (r->source) {
440 g_source_destroy(r->source);
441 g_source_unref(r->source);
442 }
443
444 if (r->io_channel)
445 g_io_channel_unref(r->io_channel);
446
447 if (r->destroy_callback)
448 r->destroy_callback(&r->mainloop->api, r, r->userdata);
449
450 pa_xfree(r);
451 }
452 }
453
454 static void free_time_events(pa_time_event *e) {
455 while (e) {
456 pa_time_event *r = e;
457 e = r->next;
458
459 if (r->source) {
460 g_source_destroy(r->source);
461 g_source_unref(r->source);
462 }
463
464 if (r->destroy_callback)
465 r->destroy_callback(&r->mainloop->api, r, r->userdata);
466
467 pa_xfree(r);
468 }
469 }
470
471 static void free_defer_events(pa_defer_event *e) {
472 while (e) {
473 pa_defer_event *r = e;
474 e = r->next;
475
476 if (r->source) {
477 g_source_destroy(r->source);
478 g_source_unref(r->source);
479 }
480
481 if (r->destroy_callback)
482 r->destroy_callback(&r->mainloop->api, r, r->userdata);
483
484 pa_xfree(r);
485 }
486 }
487
488 void pa_glib_mainloop_free(pa_glib_mainloop* g) {
489 assert(g);
490
491 free_io_events(g->io_events);
492 free_io_events(g->dead_io_events);
493 free_defer_events(g->defer_events);
494 free_defer_events(g->dead_defer_events);
495 free_time_events(g->time_events);
496 free_time_events(g->dead_time_events);
497
498 if (g->cleanup_source) {
499 g_source_destroy(g->cleanup_source);
500 g_source_unref(g->cleanup_source);
501 }
502
503 g_main_context_unref(g->glib_main_context);
504 pa_xfree(g);
505 }
506
507 pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
508 assert(g);
509 return &g->api;
510 }
511
512 static gboolean free_dead_events(gpointer p) {
513 pa_glib_mainloop *g = p;
514 assert(g);
515
516 free_io_events(g->dead_io_events);
517 free_defer_events(g->dead_defer_events);
518 free_time_events(g->dead_time_events);
519
520 g->dead_io_events = NULL;
521 g->dead_defer_events = NULL;
522 g->dead_time_events = NULL;
523
524 g_source_destroy(g->cleanup_source);
525 g_source_unref(g->cleanup_source);
526 g->cleanup_source = NULL;
527
528 return FALSE;
529 }
530
531 static void schedule_free_dead_events(pa_glib_mainloop *g) {
532 assert(g && g->glib_main_context);
533
534 if (g->cleanup_source)
535 return;
536
537 g->cleanup_source = g_idle_source_new();
538 assert(g->cleanup_source);
539 g_source_set_callback(g->cleanup_source, free_dead_events, g, NULL);
540 g_source_attach(g->cleanup_source, g->glib_main_context);
541 }