From f47a4fc3d900e14653bc059717e2805ad4964a67 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 18 May 2014 16:36:08 +0200 Subject: threads: use mpv time for mpthread_cond_timedwait wrapper Use the time as returned by mp_time_us() for mpthread_cond_timedwait(), instead of calculating the struct timespec value based on a timeout. This (probably) makes it easier to wait for a specific deadline. --- audio/out/push.c | 2 +- misc/dispatch.c | 10 +++++---- osdep/threads.c | 55 +++++++--------------------------------------- osdep/threads.h | 12 ++++++++--- osdep/timer.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- osdep/timer.h | 7 ++++++ player/client.c | 4 ++-- stream/cache.c | 4 ++-- 8 files changed, 98 insertions(+), 62 deletions(-) diff --git a/audio/out/push.c b/audio/out/push.c index 086536001d..cc37cbf13e 100644 --- a/audio/out/push.c +++ b/audio/out/push.c @@ -274,7 +274,7 @@ static void *playthread(void *arg) MP_STATS(ao, "start audio wait"); pthread_mutex_lock(&p->wakeup_lock); if (!p->need_wakeup) - mpthread_cond_timedwait(&p->wakeup, &p->wakeup_lock, timeout); + mpthread_cond_timedwait_rel(&p->wakeup, &p->wakeup_lock, timeout); p->need_wakeup = false; pthread_mutex_unlock(&p->wakeup_lock); MP_STATS(ao, "end audio wait"); diff --git a/misc/dispatch.c b/misc/dispatch.c index 74576ff469..04def9a24c 100644 --- a/misc/dispatch.c +++ b/misc/dispatch.c @@ -20,6 +20,7 @@ #include "common/common.h" #include "osdep/threads.h" +#include "osdep/timer.h" #include "dispatch.h" @@ -175,11 +176,12 @@ void mp_dispatch_run(struct mp_dispatch_queue *queue, // still process the remaining queue items, and wait for unsuspend.) void mp_dispatch_queue_process(struct mp_dispatch_queue *queue, double timeout) { + int64_t wait = timeout > 0 ? mp_add_timeout(mp_time_us(), timeout) : 0; pthread_mutex_lock(&queue->lock); queue->suspended = true; // Wake up thread which called mp_dispatch_suspend(). pthread_cond_broadcast(&queue->cond); - while (queue->head || queue->suspend_requested || timeout > 0) { + while (queue->head || queue->suspend_requested || wait > 0) { if (queue->head) { struct mp_dispatch_item *item = queue->head; queue->head = item->next; @@ -203,13 +205,13 @@ void mp_dispatch_queue_process(struct mp_dispatch_queue *queue, double timeout) pthread_cond_broadcast(&queue->cond); } } else { - if (timeout > 0) { - mpthread_cond_timedwait(&queue->cond, &queue->lock, timeout); + if (wait > 0) { + mpthread_cond_timedwait(&queue->cond, &queue->lock, wait); } else { pthread_cond_wait(&queue->cond, &queue->lock); } } - timeout = 0; + wait = 0; } queue->suspended = false; pthread_mutex_unlock(&queue->lock); diff --git a/osdep/threads.c b/osdep/threads.c index 84a65feddd..8cb03045e4 100644 --- a/osdep/threads.c +++ b/osdep/threads.c @@ -15,61 +15,22 @@ * with mpv. If not, see . */ -#include -#include -#include -#include - -#include "common/common.h" #include "threads.h" +#include "timer.h" -static void get_pthread_time(struct timespec *out_ts) -{ -#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 - clock_gettime(CLOCK_REALTIME, out_ts); -#else - // OSX - struct timeval tv; - gettimeofday(&tv, NULL); - out_ts->tv_sec = tv.tv_sec; - out_ts->tv_nsec = tv.tv_usec * 1000UL; -#endif -} - -static void timespec_add_seconds(struct timespec *ts, double seconds) -{ - // clamp to 1 week to avoid tv_sec overflows - seconds = MPMIN(seconds, 60 * 60 * 24 * 7); - unsigned long secs = (int)seconds; - unsigned long nsecs = (seconds - secs) * 1000000000UL; - if (nsecs + ts->tv_nsec >= 1000000000UL) { - secs += 1; - nsecs -= 1000000000UL; - } - ts->tv_sec += secs; - ts->tv_nsec += nsecs; -} - -// Return the argument to pass to e.g. pthread_cond_timedwait(). -// (Note that pthread_cond_t supports multiple clocks; this function computes -// the time value needed by the default clock.) -struct timespec mpthread_get_deadline(double timeout) +int mpthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + int64_t abstime) { - struct timespec ts; - get_pthread_time(&ts); - timespec_add_seconds(&ts, timeout); - return ts; + struct timespec ts = mp_time_us_to_timespec(abstime); + return pthread_cond_timedwait(cond, mutex, &ts); } -// Call pthread_cond_timedwait() with a relative timeout in seconds -int mpthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, - double timeout) +int mpthread_cond_timedwait_rel(pthread_cond_t *cond, pthread_mutex_t *mutex, + double s) { - struct timespec ts = mpthread_get_deadline(timeout); - return pthread_cond_timedwait(cond, mutex, &ts); + return mpthread_cond_timedwait(cond, mutex, mp_add_timeout(mp_time_us(), s)); } -// Helper to reduce boiler plate. int mpthread_mutex_init_recursive(pthread_mutex_t *mutex) { pthread_mutexattr_t attr; diff --git a/osdep/threads.h b/osdep/threads.h index 02f6ac1489..fa9199d63d 100644 --- a/osdep/threads.h +++ b/osdep/threads.h @@ -2,12 +2,18 @@ #define MP_OSDEP_THREADS_H_ #include +#include -struct timespec mpthread_get_deadline(double timeout); - +// Call pthread_cond_timedwait() with an absolute timeout using the same +// time source/unit as mp_time_us() (microseconds). int mpthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, - double timeout); + int64_t abstime); + +// Wait by a relative amount of time in seconds. +int mpthread_cond_timedwait_rel(pthread_cond_t *cond, pthread_mutex_t *mutex, + double seconds); +// Helper to reduce boiler plate. int mpthread_mutex_init_recursive(pthread_mutex_t *mutex); #endif diff --git a/osdep/timer.c b/osdep/timer.c index 4f506b3d46..de37653a67 100644 --- a/osdep/timer.c +++ b/osdep/timer.c @@ -17,9 +17,15 @@ #include #include +#include +#include +#include +#include +#include -#include "timer.h" +#include "common/common.h" #include "common/msg.h" +#include "timer.h" static uint64_t raw_time_offset; pthread_once_t timer_init_once = PTHREAD_ONCE_INIT; @@ -60,21 +66,75 @@ int64_t mp_time_relative_us(int64_t *t) return r; } +int64_t mp_add_timeout(int64_t time_us, double timeout_sec) +{ + assert(time_us > 0); // mp_time_us() returns strictly positive values + double t = timeout_sec * 1000 * 1000; + if (t >= (double)(INT64_MAX - time_us)) + return INT64_MAX; + if (t <= (double)time_us) + return 1; + return time_us + (int64_t)t; +} + +static void get_realtime(struct timespec *out_ts) +{ +#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 + clock_gettime(CLOCK_REALTIME, out_ts); +#else + // OSX + struct timeval tv; + gettimeofday(&tv, NULL); + out_ts->tv_sec = tv.tv_sec; + out_ts->tv_nsec = tv.tv_usec * 1000UL; +#endif +} + +struct timespec mp_time_us_to_timespec(int64_t time_us) +{ + struct timespec ts; + get_realtime(&ts); + // We don't know what time source mp_time_us() uses, but usually it's not + // CLOCK_REALTIME - so we have to remap the times. + int64_t unow = mp_time_us(); + int64_t diff_us = time_us - unow; + long diff_secs = diff_us / (1000L * 1000L); + unsigned long diff_nsecs = (diff_us - diff_secs * (1000L * 1000L)) * 1000UL; + if (diff_nsecs + ts.tv_nsec >= 1000000000UL) { + diff_secs += 1; + diff_nsecs -= 1000000000UL; + } + ts.tv_sec += diff_secs; + ts.tv_nsec += diff_nsecs; + return ts; +} + #if 0 #include +#include "threads.h" + +#define TEST_SLEEP 1 int main(void) { - int c = 200; + int c = 2000000; int64_t j, r, t = 0; + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, NULL); + pthread_cond_t cnd; + pthread_cond_init(&cnd, NULL); mp_time_init(); for (int i = 0; i < c; i++) { const int delay = rand() / (RAND_MAX / 1e5); r = mp_time_us(); +#if TEST_SLEEP mp_sleep_us(delay); +#else + mpthread_cond_timedwait(&cnd, &mtx, r + delay); +#endif j = (mp_time_us() - r) - delay; - printf("sleep time: sleep=%8i err=%5i\n", delay, (int)j); + printf("sleep time: t=%"PRId64" sleep=%8i err=%5i\n", r, delay, (int)j); t += j; } fprintf(stderr, "average error:\t%i\n", (int)(t / c)); diff --git a/osdep/timer.h b/osdep/timer.h index efb596765a..bc0e5252c9 100644 --- a/osdep/timer.h +++ b/osdep/timer.h @@ -44,4 +44,11 @@ void mp_sleep_us(int64_t us); // first call will return 0, instead of the absolute current time.) int64_t mp_time_relative_us(int64_t *t); +// Add a time in seconds to the given time in microseconds, and return it. +// Takes care of possible overflows. Never returns a negative or 0 time. +int64_t mp_add_timeout(int64_t time_us, double timeout_sec); + +// Convert the mp time in microseconds to a timespec using CLOCK_REALTIME. +struct timespec mp_time_us_to_timespec(int64_t time_us); + #endif /* MPLAYER_TIMER_H */ diff --git a/player/client.c b/player/client.c index a867681d34..88ebabafaa 100644 --- a/player/client.c +++ b/player/client.c @@ -477,7 +477,7 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout) { mpv_event *event = ctx->cur_event; - struct timespec deadline = mpthread_get_deadline(timeout); + int64_t deadline = mp_add_timeout(mp_time_us(), timeout); pthread_mutex_lock(&ctx->lock); @@ -519,7 +519,7 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout) break; if (timeout <= 0) break; - int r = pthread_cond_timedwait(&ctx->wakeup, &ctx->lock, &deadline); + int r = mpthread_cond_timedwait(&ctx->wakeup, &ctx->lock, deadline); if (r == ETIMEDOUT) break; } diff --git a/stream/cache.c b/stream/cache.c index f0d132ad64..6079f6b976 100644 --- a/stream/cache.c +++ b/stream/cache.c @@ -167,7 +167,7 @@ static int cache_wakeup_and_wait(struct priv *s, double *retry_time) } pthread_cond_signal(&s->wakeup); - mpthread_cond_timedwait(&s->wakeup, &s->mutex, CACHE_WAIT_TIME); + mpthread_cond_timedwait_rel(&s->wakeup, &s->mutex, CACHE_WAIT_TIME); *retry_time += mp_time_sec() - start; @@ -529,7 +529,7 @@ static void *cache_thread(void *arg) s->control = CACHE_CTRL_NONE; } if (s->idle && s->control == CACHE_CTRL_NONE) - mpthread_cond_timedwait(&s->wakeup, &s->mutex, CACHE_IDLE_SLEEP_TIME); + mpthread_cond_timedwait_rel(&s->wakeup, &s->mutex, CACHE_IDLE_SLEEP_TIME); } pthread_cond_signal(&s->wakeup); pthread_mutex_unlock(&s->mutex); -- cgit v1.2.3