Base mainloop on pa_rtclock_now()

Move the mainloop to monotonic based time events.

Introduces 4 helper functions:
pa_{context,core}_rttime_{new,restart}(), that fill correctly a
timeval with the rtclock flag set if the mainloop supports it.

Both mainloop-test and mainloop-test-glib works with rt and timeval
based time events. PulseAudio and clients should be fully functional.

This patch has received several iterations, and this one as been
largely untested.

Signed-off-by: Marc-André Lureau <marca-andre.lureau@nokia.com>
This commit is contained in:
Marc-André Lureau 2009-04-05 02:13:43 +03:00 committed by Marc-André Lureau
parent 125c528896
commit 0955e3d45b
42 changed files with 311 additions and 233 deletions

View file

@ -42,10 +42,12 @@
#include <pulsecore/pipe.h>
#endif
#include <pulse/i18n.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulse/i18n.h>
#include <pulsecore/core-rtclock.h>
#include <pulsecore/core-util.h>
#include <pulsecore/llist.h>
#include <pulsecore/log.h>
@ -75,7 +77,7 @@ struct pa_time_event {
pa_bool_t dead:1;
pa_bool_t enabled:1;
struct timeval timeval;
pa_usec_t time;
pa_time_event_cb_t callback;
void *userdata;
@ -317,6 +319,21 @@ static void mainloop_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy
}
/* Time events */
static pa_usec_t timeval_load(struct timeval *tv) {
pa_bool_t is_rtclock;
if (!tv)
return PA_USEC_INVALID;
is_rtclock = !!(tv->tv_usec & PA_TIMEVAL_RTCLOCK);
tv->tv_usec &= ~PA_TIMEVAL_RTCLOCK;
if (!is_rtclock)
pa_rtclock_from_wallclock(tv);
return pa_timeval_load(tv);
}
static pa_time_event* mainloop_time_new(
pa_mainloop_api*a,
const struct timeval *tv,
@ -325,11 +342,14 @@ static pa_time_event* mainloop_time_new(
pa_mainloop *m;
pa_time_event *e;
pa_usec_t t;
struct timeval ttv;
pa_assert(a);
pa_assert(a->userdata);
pa_assert(callback);
t = timeval_load(tv? ttv = *tv, &ttv : NULL);
m = a->userdata;
pa_assert(a == &m->api);
@ -337,15 +357,15 @@ static pa_time_event* mainloop_time_new(
e->mainloop = m;
e->dead = FALSE;
if ((e->enabled = !!tv)) {
e->timeval = *tv;
if ((e->enabled = (t != PA_USEC_INVALID))) {
e->time = t;
m->n_enabled_time_events++;
if (m->cached_next_time_event) {
pa_assert(m->cached_next_time_event->enabled);
if (pa_timeval_cmp(tv, &m->cached_next_time_event->timeval) < 0)
if (t < m->cached_next_time_event->time)
m->cached_next_time_event = e;
}
}
@ -363,24 +383,30 @@ static pa_time_event* mainloop_time_new(
}
static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {
pa_bool_t valid;
pa_usec_t t;
struct timeval ttv;
pa_assert(e);
pa_assert(!e->dead);
if (e->enabled && !tv) {
t = timeval_load(tv? ttv = *tv, &ttv : NULL);
valid = (t != PA_USEC_INVALID);
if (e->enabled && !valid) {
pa_assert(e->mainloop->n_enabled_time_events > 0);
e->mainloop->n_enabled_time_events--;
} else if (!e->enabled && tv)
} else if (!e->enabled && valid)
e->mainloop->n_enabled_time_events++;
if ((e->enabled = !!tv)) {
e->timeval = *tv;
if ((e->enabled = valid)) {
e->time = t;
pa_mainloop_wakeup(e->mainloop);
}
if (e->mainloop->cached_next_time_event && e->enabled) {
pa_assert(e->mainloop->cached_next_time_event->enabled);
if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0)
if (t < e->mainloop->cached_next_time_event->time)
e->mainloop->cached_next_time_event = e;
} else if (e->mainloop->cached_next_time_event == e)
e->mainloop->cached_next_time_event = NULL;
@ -721,11 +747,11 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) {
if (t->dead || !t->enabled)
continue;
if (!n || pa_timeval_cmp(&t->timeval, &n->timeval) < 0) {
if (!n || t->time < n->time) {
n = t;
/* Shortcut for tv = { 0, 0 } */
if (n->timeval.tv_sec <= 0)
/* Shortcut for time == 0 */
if (n->time == 0)
break;
}
}
@ -736,7 +762,6 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) {
static int calc_next_timeout(pa_mainloop *m) {
pa_time_event *t;
struct timeval now;
pa_usec_t usec;
if (!m->n_enabled_time_events)
@ -745,41 +770,41 @@ static int calc_next_timeout(pa_mainloop *m) {
t = find_next_time_event(m);
pa_assert(t);
if (t->timeval.tv_sec <= 0)
if (t->time == 0)
return 0;
pa_gettimeofday(&now);
usec = t->time - pa_rtclock_now();
if (pa_timeval_cmp(&t->timeval, &now) <= 0)
if (usec <= 0)
return 0;
usec = pa_timeval_diff(&t->timeval, &now);
return (int) (usec / 1000);
return (int) (usec / 1000); /* in milliseconds */
}
static int dispatch_timeout(pa_mainloop *m) {
pa_time_event *e;
struct timeval now;
pa_usec_t now;
int r = 0;
pa_assert(m);
if (m->n_enabled_time_events <= 0)
return 0;
pa_gettimeofday(&now);
now = pa_rtclock_now();
for (e = m->time_events; e && !m->quit; e = e->next) {
if (e->dead || !e->enabled)
continue;
if (pa_timeval_cmp(&e->timeval, &now) <= 0) {
if (e->time <= now) {
struct timeval tv;
pa_assert(e->callback);
/* Disable time event */
mainloop_time_restart(e, NULL);
e->callback(&m->api, e, &e->timeval, e->userdata);
e->callback(&m->api, e, pa_timeval_rtstore(&tv, e->time, TRUE), e->userdata);
r++;
}