From 0955e3d45b6e992308e2d51fcbf28a9f9376f788 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Sun, 5 Apr 2009 02:13:43 +0300 Subject: Base mainloop on pa_rtclock_now() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/pulse/context.c | 29 ++++++++++++++++++-- src/pulse/context.h | 8 ++++++ src/pulse/internal.h | 1 + src/pulse/mainloop-api.h | 1 + src/pulse/mainloop.c | 71 ++++++++++++++++++++++++++++++++---------------- src/pulse/stream.c | 13 ++------- src/pulse/timeval.h | 3 ++ 7 files changed, 90 insertions(+), 36 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/context.c b/src/pulse/context.c index 3b7bf08d..0c1810f3 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -540,7 +541,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) { pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); pa_assert(!c->pdispatch); - c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); + c->pdispatch = pa_pdispatch_new(c->mainloop, c->use_rtclock, command_table, PA_COMMAND_MAX); if (!c->conf->cookie_valid) pa_log_info(_("No cookie loaded. Attempting to connect without.")); @@ -757,7 +758,7 @@ static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wr pa_assert(conn); dbus_error_init(&error); - if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, type, &error)) || dbus_error_is_set(&error)) { + if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, c->use_rtclock, type, &error)) || dbus_error_is_set(&error)) { pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message); goto finish; } @@ -827,7 +828,7 @@ static int try_next_connection(pa_context *c) { pa_xfree(c->server); c->server = pa_xstrdup(u); - if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) + if (!(c->client = pa_socket_client_new_string(c->mainloop, c->use_rtclock, u, PA_NATIVE_DEFAULT_PORT))) continue; c->is_local = !!pa_socket_client_is_local(c->client); @@ -1443,3 +1444,25 @@ finish: if (pl) pa_proplist_free(pl); } + +pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) { + struct timeval tv; + + pa_assert(c); + pa_assert(c->mainloop); + + pa_timeval_rtstore(&tv, usec, c->use_rtclock); + + return c->mainloop->time_new(c->mainloop, &tv, cb, userdata); +} + +void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec) { + struct timeval tv; + + pa_assert(c); + pa_assert(c->mainloop); + + pa_timeval_rtstore(&tv, usec, c->use_rtclock); + + c->mainloop->time_restart(e, &tv); +} diff --git a/src/pulse/context.h b/src/pulse/context.h index 139d0e0b..cd129313 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -260,6 +260,14 @@ pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[] * introspection functions, such as pa_context_get_client_info(). \since 0.9.11 */ uint32_t pa_context_get_index(pa_context *s); +/** Create a new timer event source for the specified time (wrapper + for mainloop->time_new). \since 0.9.16 */ +pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata); +/** Restart a running or expired timer event source (wrapper + for mainloop->time_restart). \since 0.9.16 */ +void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec); + + PA_C_DECL_END #endif diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 28a989b3..fa5efd7d 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -89,6 +89,7 @@ struct pa_context { pa_bool_t server_specified:1; pa_bool_t no_fail:1; pa_bool_t do_autospawn:1; + pa_bool_t use_rtclock:1; pa_spawn_api spawn_api; pa_strlist *server_list; diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h index e353ed96..aa0d5e73 100644 --- a/src/pulse/mainloop-api.h +++ b/src/pulse/mainloop-api.h @@ -27,6 +27,7 @@ #include #include +#include #include /** \file diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c index 225fd098..1b779468 100644 --- a/src/pulse/mainloop.c +++ b/src/pulse/mainloop.c @@ -42,10 +42,12 @@ #include #endif +#include +#include #include #include -#include +#include #include #include #include @@ -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++; } diff --git a/src/pulse/stream.c b/src/pulse/stream.c index dd44ff40..40556329 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -320,14 +320,10 @@ static void request_auto_timing_update(pa_stream *s, pa_bool_t force) { } if (s->auto_timing_update_event) { - struct timeval next; - if (force) s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC; - pa_gettimeofday(&next); - pa_timeval_add(&next, s->auto_timing_interval_usec); - s->mainloop->time_restart(s->auto_timing_update_event, &next); + pa_context_rttime_restart(s->context, s->auto_timing_update_event, pa_rtclock_now() + s->auto_timing_interval_usec); s->auto_timing_interval_usec = PA_MIN(AUTO_TIMING_INTERVAL_END_USEC, s->auto_timing_interval_usec*2); } @@ -801,7 +797,7 @@ static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) { request_auto_timing_update(s, TRUE); } -static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) { +static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { pa_stream *s = userdata; pa_assert(s); @@ -823,12 +819,9 @@ static void create_stream_complete(pa_stream *s) { s->write_callback(s, (size_t) s->requested_bytes, s->write_userdata); if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { - struct timeval tv; - pa_gettimeofday(&tv); s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC; - pa_timeval_add(&tv, s->auto_timing_interval_usec); pa_assert(!s->auto_timing_update_event); - s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s); + s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s); request_auto_timing_update(s, TRUE); } diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index 651da953..6307735c 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -51,6 +51,9 @@ PA_C_DECL_BEGIN /** The number of nanoseconds in a microsecond */ #define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL) +/** Invalid time in usec */ +#define PA_USEC_INVALID ((pa_usec_t) -1) + struct timeval; /** Return the current timestamp, just like UNIX gettimeofday() */ -- cgit