]> code.delx.au - pulseaudio/blob - src/pulse/glib12-mainloop.c
add two more \since
[pulseaudio] / src / pulse / glib12-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/timeval.h>
29 #include <pulse/xmalloc.h>
30
31 #include <pulsecore/idxset.h>
32 #include <pulsecore/core-util.h>
33
34 #include "glib-mainloop.h"
35
36 /* A mainloop implementation based on GLIB 1.2 */
37
38 struct pa_io_event {
39 pa_glib_mainloop *mainloop;
40 int dead;
41 GIOChannel *io_channel;
42 guint source;
43 GIOCondition io_condition;
44 int fd;
45 void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
46 void *userdata;
47 void (*destroy_callback) (pa_mainloop_api *m, pa_io_event*e, void *userdata);
48 pa_io_event *next, *prev;
49 };
50
51 struct pa_time_event {
52 pa_glib_mainloop *mainloop;
53 int dead;
54 guint source;
55 struct timeval timeval;
56 void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata);
57 void *userdata;
58 void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata);
59 pa_time_event *next, *prev;
60 };
61
62 struct pa_defer_event {
63 pa_glib_mainloop *mainloop;
64 int dead;
65 guint source;
66 void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata);
67 void *userdata;
68 void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata);
69 pa_defer_event *next, *prev;
70 };
71
72 struct pa_glib_mainloop {
73 pa_mainloop_api api;
74 guint 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 = (guint) -1;
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 static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
115 pa_io_event *e = data;
116 pa_io_event_flags_t f;
117 assert(source && e && e->io_channel == source);
118
119 f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
120 (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
121 (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
122 (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
123
124 e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
125 return TRUE;
126 }
127
128 static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
129 GIOCondition c;
130 assert(e && !e->dead);
131
132 c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
133
134 if (c == e->io_condition)
135 return;
136
137 if (e->source != (guint) -1)
138 g_source_remove(e->source);
139
140 e->source = g_io_add_watch_full(e->io_channel, G_PRIORITY_DEFAULT, c | G_IO_ERR | G_IO_HUP, io_cb, e, NULL);
141 assert(e->source != (guint) -1);
142 e->io_condition = c;
143 }
144
145 static void glib_io_free(pa_io_event*e) {
146 assert(e && !e->dead);
147
148 if (e->source != (guint) -1) {
149 g_source_remove(e->source);
150 e->source = (guint) -1;
151 }
152
153 if (e->prev)
154 e->prev->next = e->next;
155 else
156 e->mainloop->io_events = e->next;
157
158 if (e->next)
159 e->next->prev = e->prev;
160
161 if ((e->next = e->mainloop->dead_io_events))
162 e->next->prev = e;
163
164 e->mainloop->dead_io_events = e;
165 e->prev = NULL;
166
167 e->dead = 1;
168 schedule_free_dead_events(e->mainloop);
169 }
170
171 static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) {
172 assert(e);
173 e->destroy_callback = callback;
174 }
175
176 /* Time sources */
177
178 static void glib_time_restart(pa_time_event*e, const struct timeval *tv);
179
180 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) {
181 pa_glib_mainloop *g;
182 pa_time_event *e;
183
184 assert(m && m->userdata && tv && callback);
185 g = m->userdata;
186
187 e = pa_xmalloc(sizeof(pa_time_event));
188 e->mainloop = g;
189 e->dead = 0;
190 e->callback = callback;
191 e->userdata = userdata;
192 e->destroy_callback = NULL;
193 e->source = (guint) -1;
194
195 glib_time_restart(e, tv);
196
197 e->next = g->time_events;
198 if (e->next) e->next->prev = e;
199 g->time_events = e;
200 e->prev = NULL;
201
202 return e;
203 }
204
205 static guint msec_diff(const struct timeval *a, const struct timeval *b) {
206 guint r;
207 assert(a && b);
208
209 if (a->tv_sec < b->tv_sec)
210 return 0;
211
212 if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
213 return 0;
214
215 r = (a->tv_sec-b->tv_sec)*1000;
216
217 if (a->tv_usec >= b->tv_usec)
218 r += (a->tv_usec - b->tv_usec) / 1000;
219 else
220 r -= (b->tv_usec - a->tv_usec) / 1000;
221
222 return r;
223 }
224
225 static gboolean time_cb(gpointer data) {
226 pa_time_event* e = data;
227 assert(e && e->mainloop && e->source != (guint) -1);
228
229 g_source_remove(e->source);
230 e->source = (guint) -1;
231
232 e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
233 return FALSE;
234 }
235
236 static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
237 struct timeval now;
238 assert(e && e->mainloop && !e->dead);
239
240 pa_gettimeofday(&now);
241 if (e->source != (guint) -1)
242 g_source_remove(e->source);
243
244 if (tv) {
245 e->timeval = *tv;
246 e->source = g_timeout_add_full(G_PRIORITY_DEFAULT, msec_diff(tv, &now), time_cb, e, NULL);
247 assert(e->source != (guint) -1);
248 } else
249 e->source = (guint) -1;
250 }
251
252 static void glib_time_free(pa_time_event *e) {
253 assert(e && e->mainloop && !e->dead);
254
255 if (e->source != (guint) -1) {
256 g_source_remove(e->source);
257 e->source = (guint) -1;
258 }
259
260 if (e->prev)
261 e->prev->next = e->next;
262 else
263 e->mainloop->time_events = e->next;
264
265 if (e->next)
266 e->next->prev = e->prev;
267
268 if ((e->next = e->mainloop->dead_time_events))
269 e->next->prev = e;
270
271 e->mainloop->dead_time_events = e;
272 e->prev = NULL;
273
274 e->dead = 1;
275 schedule_free_dead_events(e->mainloop);
276 }
277
278 static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) {
279 assert(e);
280 e->destroy_callback = callback;
281 }
282
283 /* Deferred sources */
284
285 static void glib_defer_enable(pa_defer_event *e, int b);
286
287 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) {
288 pa_defer_event *e;
289 pa_glib_mainloop *g;
290
291 assert(m && m->userdata && callback);
292 g = m->userdata;
293
294 e = pa_xmalloc(sizeof(pa_defer_event));
295 e->mainloop = g;
296 e->dead = 0;
297 e->callback = callback;
298 e->userdata = userdata;
299 e->destroy_callback = NULL;
300 e->source = (guint) -1;
301
302 glib_defer_enable(e, 1);
303
304 e->next = g->defer_events;
305 if (e->next) e->next->prev = e;
306 g->defer_events = e;
307 e->prev = NULL;
308 return e;
309 }
310
311 static gboolean idle_cb(gpointer data) {
312 pa_defer_event* e = data;
313 assert(e && e->mainloop && e->source != (guint) -1);
314
315 e->callback(&e->mainloop->api, e, e->userdata);
316 return TRUE;
317 }
318
319 static void glib_defer_enable(pa_defer_event *e, int b) {
320 assert(e && e->mainloop);
321
322 if (e->source != (guint) -1 && !b) {
323 g_source_remove(e->source);
324 e->source = (guint) -1;
325 } else if (e->source == (guint) -1 && b) {
326 e->source = g_idle_add_full(G_PRIORITY_HIGH, idle_cb, e, NULL);
327 assert(e->source != (guint) -1);
328 }
329 }
330
331 static void glib_defer_free(pa_defer_event *e) {
332 assert(e && e->mainloop && !e->dead);
333
334 if (e->source != (guint) -1) {
335 g_source_remove(e->source);
336 e->source = (guint) -1;
337 }
338
339 if (e->prev)
340 e->prev->next = e->next;
341 else
342 e->mainloop->defer_events = e->next;
343
344 if (e->next)
345 e->next->prev = e->prev;
346
347 if ((e->next = e->mainloop->dead_defer_events))
348 e->next->prev = e;
349
350 e->mainloop->dead_defer_events = e;
351 e->prev = NULL;
352
353 e->dead = 1;
354 schedule_free_dead_events(e->mainloop);
355 }
356
357 static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) {
358 assert(e);
359 e->destroy_callback = callback;
360 }
361
362 /* quit() */
363
364 static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) {
365 pa_glib_mainloop *g;
366 assert(a && a->userdata);
367 g = a->userdata;
368
369 /* NOOP */
370 }
371
372 static const pa_mainloop_api vtable = {
373 .userdata = NULL,
374
375 .io_new = glib_io_new,
376 .io_enable = glib_io_enable,
377 .io_free = glib_io_free,
378 .io_set_destroy= glib_io_set_destroy,
379
380 .time_new = glib_time_new,
381 .time_restart = glib_time_restart,
382 .time_free = glib_time_free,
383 .time_set_destroy = glib_time_set_destroy,
384
385 .defer_new = glib_defer_new,
386 .defer_enable = glib_defer_enable,
387 .defer_free = glib_defer_free,
388 .defer_set_destroy = glib_defer_set_destroy,
389
390 .quit = glib_quit,
391 };
392
393 pa_glib_mainloop *pa_glib_mainloop_new(void) {
394 pa_glib_mainloop *g;
395
396 g = pa_xmalloc(sizeof(pa_glib_mainloop));
397
398 g->api = vtable;
399 g->api.userdata = g;
400
401 g->io_events = g->dead_io_events = NULL;
402 g->time_events = g->dead_time_events = NULL;
403 g->defer_events = g->dead_defer_events = NULL;
404
405 g->cleanup_source = (guint) -1;
406 return g;
407 }
408
409 static void free_io_events(pa_io_event *e) {
410 while (e) {
411 pa_io_event *r = e;
412 e = r->next;
413
414 if (r->source != (guint) -1)
415 g_source_remove(r->source);
416
417 if (r->io_channel)
418 g_io_channel_unref(r->io_channel);
419
420 if (r->destroy_callback)
421 r->destroy_callback(&r->mainloop->api, r, r->userdata);
422
423 pa_xfree(r);
424 }
425 }
426
427 static void free_time_events(pa_time_event *e) {
428 while (e) {
429 pa_time_event *r = e;
430 e = r->next;
431
432 if (r->source != (guint) -1)
433 g_source_remove(r->source);
434
435 if (r->destroy_callback)
436 r->destroy_callback(&r->mainloop->api, r, r->userdata);
437
438 pa_xfree(r);
439 }
440 }
441
442 static void free_defer_events(pa_defer_event *e) {
443 while (e) {
444 pa_defer_event *r = e;
445 e = r->next;
446
447 if (r->source != (guint) -1)
448 g_source_remove(r->source);
449
450 if (r->destroy_callback)
451 r->destroy_callback(&r->mainloop->api, r, r->userdata);
452
453 pa_xfree(r);
454 }
455 }
456
457 void pa_glib_mainloop_free(pa_glib_mainloop* g) {
458 assert(g);
459
460 free_io_events(g->io_events);
461 free_io_events(g->dead_io_events);
462 free_defer_events(g->defer_events);
463 free_defer_events(g->dead_defer_events);
464 free_time_events(g->time_events);
465 free_time_events(g->dead_time_events);
466
467 if (g->cleanup_source != (guint) -1)
468 g_source_remove(g->cleanup_source);
469
470 pa_xfree(g);
471 }
472
473 pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
474 assert(g);
475 return &g->api;
476 }
477
478 static gboolean free_dead_events(gpointer p) {
479 pa_glib_mainloop *g = p;
480 assert(g);
481
482 free_io_events(g->dead_io_events);
483 free_defer_events(g->dead_defer_events);
484 free_time_events(g->dead_time_events);
485
486 g->dead_io_events = NULL;
487 g->dead_defer_events = NULL;
488 g->dead_time_events = NULL;
489
490 g_source_remove(g->cleanup_source);
491 g->cleanup_source = (guint) -1;
492
493 return FALSE;
494 }
495
496 static void schedule_free_dead_events(pa_glib_mainloop *g) {
497 assert(g);
498
499 if (g->cleanup_source != (guint) -1)
500 return;
501
502 g->cleanup_source = g_idle_add_full(G_PRIORITY_HIGH, free_dead_events, g, NULL);
503 }