1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
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.1 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, see <http://www.gnu.org/licenses/>.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <pulse/xmalloc.h>
25#include <pulse/timeval.h>
26
27#include <pulsecore/core-util.h>
28#include <pulsecore/log.h>
29#include <pulsecore/llist.h>
30
31#include <glib.h>
32#include "glib-mainloop.h"
33
34struct pa_io_event {
35    pa_glib_mainloop *mainloop;
36    int dead;
37
38    GPollFD poll_fd;
39    int poll_fd_added;
40
41    pa_io_event_cb_t callback;
42    void *userdata;
43    pa_io_event_destroy_cb_t destroy_callback;
44
45    PA_LLIST_FIELDS(pa_io_event);
46};
47
48struct pa_time_event {
49    pa_glib_mainloop *mainloop;
50    int dead;
51
52    int enabled;
53    struct timeval timeval;
54
55    pa_time_event_cb_t callback;
56    void *userdata;
57    pa_time_event_destroy_cb_t destroy_callback;
58
59    PA_LLIST_FIELDS(pa_time_event);
60};
61
62struct pa_defer_event {
63    pa_glib_mainloop *mainloop;
64    int dead;
65
66    int enabled;
67
68    pa_defer_event_cb_t callback;
69    void *userdata;
70    pa_defer_event_destroy_cb_t destroy_callback;
71
72    PA_LLIST_FIELDS(pa_defer_event);
73};
74
75struct pa_glib_mainloop {
76    GSource source;
77
78    pa_mainloop_api api;
79    GMainContext *context;
80
81    PA_LLIST_HEAD(pa_io_event, io_events);
82    PA_LLIST_HEAD(pa_time_event, time_events);
83    PA_LLIST_HEAD(pa_defer_event, defer_events);
84
85    int n_enabled_defer_events, n_enabled_time_events;
86    int io_events_please_scan, time_events_please_scan, defer_events_please_scan;
87
88    pa_time_event *cached_next_time_event;
89};
90
91static void cleanup_io_events(pa_glib_mainloop *g, int force) {
92    pa_io_event *e;
93
94    e = g->io_events;
95    while (e) {
96        pa_io_event *n = e->next;
97
98        if (!force && g->io_events_please_scan <= 0)
99            break;
100
101        if (force || e->dead) {
102            PA_LLIST_REMOVE(pa_io_event, g->io_events, e);
103
104            if (e->dead) {
105                g_assert(g->io_events_please_scan > 0);
106                g->io_events_please_scan--;
107            }
108
109            if (e->poll_fd_added)
110                g_source_remove_poll(&g->source, &e->poll_fd);
111
112            if (e->destroy_callback)
113                e->destroy_callback(&g->api, e, e->userdata);
114
115            pa_xfree(e);
116        }
117
118        e = n;
119    }
120
121    g_assert(g->io_events_please_scan == 0);
122}
123
124static void cleanup_time_events(pa_glib_mainloop *g, int force) {
125    pa_time_event *e;
126
127    e = g->time_events;
128    while (e) {
129        pa_time_event *n = e->next;
130
131        if (!force && g->time_events_please_scan <= 0)
132            break;
133
134        if (force || e->dead) {
135            PA_LLIST_REMOVE(pa_time_event, g->time_events, e);
136
137            if (e->dead) {
138                g_assert(g->time_events_please_scan > 0);
139                g->time_events_please_scan--;
140            }
141
142            if (!e->dead && e->enabled) {
143                g_assert(g->n_enabled_time_events > 0);
144                g->n_enabled_time_events--;
145            }
146
147            if (e->destroy_callback)
148                e->destroy_callback(&g->api, e, e->userdata);
149
150            pa_xfree(e);
151        }
152
153        e = n;
154    }
155
156    g_assert(g->time_events_please_scan == 0);
157}
158
159static void cleanup_defer_events(pa_glib_mainloop *g, int force) {
160    pa_defer_event *e;
161
162    e = g->defer_events;
163    while (e) {
164        pa_defer_event *n = e->next;
165
166        if (!force && g->defer_events_please_scan <= 0)
167            break;
168
169        if (force || e->dead) {
170            PA_LLIST_REMOVE(pa_defer_event, g->defer_events, e);
171
172            if (e->dead) {
173                g_assert(g->defer_events_please_scan > 0);
174                g->defer_events_please_scan--;
175            }
176
177            if (!e->dead && e->enabled) {
178                g_assert(g->n_enabled_defer_events > 0);
179                g->n_enabled_defer_events--;
180            }
181
182            if (e->destroy_callback)
183                e->destroy_callback(&g->api, e, e->userdata);
184
185            pa_xfree(e);
186        }
187
188        e = n;
189    }
190
191    g_assert(g->defer_events_please_scan == 0);
192}
193
194static gushort map_flags_to_glib(pa_io_event_flags_t flags) {
195    return (gushort)
196        ((flags & PA_IO_EVENT_INPUT ? G_IO_IN : 0) |
197         (flags & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0) |
198         (flags & PA_IO_EVENT_ERROR ? G_IO_ERR : 0) |
199         (flags & PA_IO_EVENT_HANGUP ? G_IO_HUP : 0));
200}
201
202static pa_io_event_flags_t map_flags_from_glib(gushort flags) {
203    return
204        (flags & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
205        (flags & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
206        (flags & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
207        (flags & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
208}
209
210static pa_io_event* glib_io_new(
211        pa_mainloop_api*m,
212        int fd,
213        pa_io_event_flags_t f,
214        pa_io_event_cb_t cb,
215        void *userdata) {
216
217    pa_io_event *e;
218    pa_glib_mainloop *g;
219
220    g_assert(m);
221    g_assert(m->userdata);
222    g_assert(fd >= 0);
223    g_assert(cb);
224
225    g = m->userdata;
226
227    e = pa_xnew(pa_io_event, 1);
228    e->mainloop = g;
229    e->dead = 0;
230
231    e->poll_fd.fd = fd;
232    e->poll_fd.events = map_flags_to_glib(f);
233    e->poll_fd.revents = 0;
234
235    e->callback = cb;
236    e->userdata = userdata;
237    e->destroy_callback = NULL;
238
239    PA_LLIST_PREPEND(pa_io_event, g->io_events, e);
240
241    g_source_add_poll(&g->source, &e->poll_fd);
242    e->poll_fd_added = 1;
243
244    return e;
245}
246
247static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
248    g_assert(e);
249    g_assert(!e->dead);
250
251    e->poll_fd.events = map_flags_to_glib(f);
252}
253
254static void glib_io_free(pa_io_event*e) {
255    g_assert(e);
256    g_assert(!e->dead);
257
258    e->dead = 1;
259    e->mainloop->io_events_please_scan++;
260
261    if (e->poll_fd_added) {
262        g_source_remove_poll(&e->mainloop->source, &e->poll_fd);
263        e->poll_fd_added = 0;
264    }
265}
266
267static void glib_io_set_destroy(pa_io_event*e, pa_io_event_destroy_cb_t cb) {
268    g_assert(e);
269    g_assert(!e->dead);
270
271    e->destroy_callback = cb;
272}
273
274/* Time sources */
275
276static pa_time_event* glib_time_new(
277        pa_mainloop_api*m,
278        const struct timeval *tv,
279        pa_time_event_cb_t cb,
280        void *userdata) {
281
282    pa_glib_mainloop *g;
283    pa_time_event *e;
284
285    g_assert(m);
286    g_assert(m->userdata);
287    g_assert(cb);
288
289    g = m->userdata;
290
291    e = pa_xnew(pa_time_event, 1);
292    e->mainloop = g;
293    e->dead = 0;
294
295    if ((e->enabled = !!tv)) {
296        e->timeval = *tv;
297        g->n_enabled_time_events++;
298
299        if (g->cached_next_time_event) {
300            g_assert(g->cached_next_time_event->enabled);
301
302            if (pa_timeval_cmp(tv, &g->cached_next_time_event->timeval) < 0)
303                g->cached_next_time_event = e;
304        }
305    }
306
307    e->callback = cb;
308    e->userdata = userdata;
309    e->destroy_callback = NULL;
310
311    PA_LLIST_PREPEND(pa_time_event, g->time_events, e);
312
313    return e;
314}
315
316static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
317    g_assert(e);
318    g_assert(!e->dead);
319
320    if (e->enabled && !tv) {
321        g_assert(e->mainloop->n_enabled_time_events > 0);
322        e->mainloop->n_enabled_time_events--;
323    } else if (!e->enabled && tv)
324        e->mainloop->n_enabled_time_events++;
325
326    if ((e->enabled = !!tv))
327        e->timeval = *tv;
328
329    if (e->mainloop->cached_next_time_event == e)
330        e->mainloop->cached_next_time_event = NULL;
331
332    if (e->mainloop->cached_next_time_event && e->enabled) {
333        g_assert(e->mainloop->cached_next_time_event->enabled);
334
335        if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0)
336            e->mainloop->cached_next_time_event = e;
337    }
338}
339
340static void glib_time_free(pa_time_event *e) {
341    g_assert(e);
342    g_assert(!e->dead);
343
344    e->dead = 1;
345    e->mainloop->time_events_please_scan++;
346
347    if (e->enabled)
348        e->mainloop->n_enabled_time_events--;
349
350    if (e->mainloop->cached_next_time_event == e)
351        e->mainloop->cached_next_time_event = NULL;
352}
353
354static void glib_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) {
355    g_assert(e);
356    g_assert(!e->dead);
357
358    e->destroy_callback = cb;
359}
360
361/* Deferred sources */
362
363static pa_defer_event* glib_defer_new(
364        pa_mainloop_api*m,
365        pa_defer_event_cb_t cb,
366        void *userdata) {
367
368    pa_defer_event *e;
369    pa_glib_mainloop *g;
370
371    g_assert(m);
372    g_assert(m->userdata);
373    g_assert(cb);
374
375    g = m->userdata;
376
377    e = pa_xnew(pa_defer_event, 1);
378    e->mainloop = g;
379    e->dead = 0;
380
381    e->enabled = 1;
382    g->n_enabled_defer_events++;
383
384    e->callback = cb;
385    e->userdata = userdata;
386    e->destroy_callback = NULL;
387
388    PA_LLIST_PREPEND(pa_defer_event, g->defer_events, e);
389    return e;
390}
391
392static void glib_defer_enable(pa_defer_event *e, int b) {
393    g_assert(e);
394    g_assert(!e->dead);
395
396    if (e->enabled && !b) {
397        g_assert(e->mainloop->n_enabled_defer_events > 0);
398        e->mainloop->n_enabled_defer_events--;
399    } else if (!e->enabled && b)
400        e->mainloop->n_enabled_defer_events++;
401
402    e->enabled = b;
403}
404
405static void glib_defer_free(pa_defer_event *e) {
406    g_assert(e);
407    g_assert(!e->dead);
408
409    e->dead = 1;
410    e->mainloop->defer_events_please_scan++;
411
412    if (e->enabled) {
413        g_assert(e->mainloop->n_enabled_defer_events > 0);
414        e->mainloop->n_enabled_defer_events--;
415    }
416}
417
418static void glib_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) {
419    g_assert(e);
420    g_assert(!e->dead);
421
422    e->destroy_callback = cb;
423}
424
425/* quit() */
426
427static void glib_quit(pa_mainloop_api*a, int retval) {
428
429    g_warning("quit() ignored");
430
431    /* NOOP */
432}
433
434static pa_time_event* find_next_time_event(pa_glib_mainloop *g) {
435    pa_time_event *t, *n = NULL;
436    g_assert(g);
437
438    if (g->cached_next_time_event)
439        return g->cached_next_time_event;
440
441    for (t = g->time_events; t; t = t->next) {
442
443        if (t->dead || !t->enabled)
444            continue;
445
446        if (!n || pa_timeval_cmp(&t->timeval, &n->timeval) < 0) {
447            n = t;
448
449            /* Shortcut for tv = { 0, 0 } */
450            if (n->timeval.tv_sec <= 0)
451                break;
452        }
453    }
454
455    g->cached_next_time_event = n;
456    return n;
457}
458
459static void scan_dead(pa_glib_mainloop *g) {
460    g_assert(g);
461
462    if (g->io_events_please_scan)
463        cleanup_io_events(g, 0);
464
465    if (g->time_events_please_scan)
466        cleanup_time_events(g, 0);
467
468    if (g->defer_events_please_scan)
469        cleanup_defer_events(g, 0);
470}
471
472static gboolean prepare_func(GSource *source, gint *timeout) {
473    pa_glib_mainloop *g = (pa_glib_mainloop*) source;
474
475    g_assert(g);
476    g_assert(timeout);
477
478    scan_dead(g);
479
480    if (g->n_enabled_defer_events) {
481        *timeout = 0;
482        return TRUE;
483    } else if (g->n_enabled_time_events) {
484        pa_time_event *t;
485        gint64 now;
486        struct timeval tvnow;
487        pa_usec_t usec;
488
489        t = find_next_time_event(g);
490        g_assert(t);
491
492        now = g_get_real_time();
493        pa_timeval_store(&tvnow, now);
494
495        if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) {
496            *timeout = 0;
497            return TRUE;
498        }
499        usec = pa_timeval_diff(&t->timeval, &tvnow);
500        *timeout = (gint) (usec / 1000);
501    } else
502        *timeout = -1;
503
504    return FALSE;
505}
506static gboolean check_func(GSource *source) {
507    pa_glib_mainloop *g = (pa_glib_mainloop*) source;
508    pa_io_event *e;
509
510    g_assert(g);
511
512    if (g->n_enabled_defer_events)
513        return TRUE;
514    else if (g->n_enabled_time_events) {
515        pa_time_event *t;
516        gint64 now;
517        struct timeval tvnow;
518
519        t = find_next_time_event(g);
520        g_assert(t);
521
522        now = g_get_real_time();
523        pa_timeval_store(&tvnow, now);
524
525        if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0)
526            return TRUE;
527    }
528
529    for (e = g->io_events; e; e = e->next)
530        if (!e->dead && e->poll_fd.revents != 0)
531            return TRUE;
532
533    return FALSE;
534}
535
536static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer userdata) {
537    pa_glib_mainloop *g = (pa_glib_mainloop*) source;
538    pa_io_event *e;
539
540    g_assert(g);
541
542    if (g->n_enabled_defer_events) {
543        pa_defer_event *d;
544
545        for (d = g->defer_events; d; d = d->next) {
546            if (d->dead || !d->enabled)
547                continue;
548
549            break;
550        }
551
552        g_assert(d);
553
554        d->callback(&g->api, d, d->userdata);
555        return TRUE;
556    }
557
558    if (g->n_enabled_time_events) {
559        gint64 now;
560        struct timeval tvnow;
561        pa_time_event *t;
562
563        t = find_next_time_event(g);
564        g_assert(t);
565
566        now = g_get_real_time();
567        pa_timeval_store(&tvnow, now);
568
569        if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) {
570
571            /* Disable time event */
572            glib_time_restart(t, NULL);
573
574            t->callback(&g->api, t, &t->timeval, t->userdata);
575            return TRUE;
576        }
577    }
578
579    for (e = g->io_events; e; e = e->next)
580        if (!e->dead && e->poll_fd.revents != 0) {
581            e->callback(&g->api, e, e->poll_fd.fd, map_flags_from_glib(e->poll_fd.revents), e->userdata);
582            e->poll_fd.revents = 0;
583            return TRUE;
584        }
585
586    return FALSE;
587}
588
589static const pa_mainloop_api vtable = {
590    .userdata = NULL,
591
592    .io_new = glib_io_new,
593    .io_enable = glib_io_enable,
594    .io_free = glib_io_free,
595    .io_set_destroy = glib_io_set_destroy,
596
597    .time_new = glib_time_new,
598    .time_restart = glib_time_restart,
599    .time_free = glib_time_free,
600    .time_set_destroy = glib_time_set_destroy,
601
602    .defer_new = glib_defer_new,
603    .defer_enable = glib_defer_enable,
604    .defer_free = glib_defer_free,
605    .defer_set_destroy = glib_defer_set_destroy,
606
607    .quit = glib_quit,
608};
609
610pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
611    pa_glib_mainloop *g;
612
613    static GSourceFuncs source_funcs = {
614        prepare_func,
615        check_func,
616        dispatch_func,
617        NULL,
618        NULL,
619        NULL
620    };
621
622    g = (pa_glib_mainloop*) g_source_new(&source_funcs, sizeof(pa_glib_mainloop));
623    g_main_context_ref(g->context = c ? c : g_main_context_default());
624
625    g->api = vtable;
626    g->api.userdata = g;
627
628    PA_LLIST_HEAD_INIT(pa_io_event, g->io_events);
629    PA_LLIST_HEAD_INIT(pa_time_event, g->time_events);
630    PA_LLIST_HEAD_INIT(pa_defer_event, g->defer_events);
631
632    g->n_enabled_defer_events = g->n_enabled_time_events = 0;
633    g->io_events_please_scan = g->time_events_please_scan = g->defer_events_please_scan = 0;
634
635    g->cached_next_time_event = NULL;
636
637    g_source_attach(&g->source, g->context);
638    g_source_set_can_recurse(&g->source, FALSE);
639
640    return g;
641}
642
643void pa_glib_mainloop_free(pa_glib_mainloop* g) {
644    g_assert(g);
645
646    cleanup_io_events(g, 1);
647    cleanup_defer_events(g, 1);
648    cleanup_time_events(g, 1);
649
650    g_main_context_unref(g->context);
651    g_source_destroy(&g->source);
652    g_source_unref(&g->source);
653}
654
655pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
656    g_assert(g);
657
658    return &g->api;
659}
660