summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-05-18 16:36:08 +0200
committerwm4 <wm4@nowhere>2014-05-18 19:20:32 +0200
commitf47a4fc3d900e14653bc059717e2805ad4964a67 (patch)
treea8ca8944a10c15bbb46239db62a56940a0d3f7b1
parente209e44ca2fef86ec2ec5513bbb63d4d68ff97b5 (diff)
downloadmpv-f47a4fc3d900e14653bc059717e2805ad4964a67.tar.bz2
mpv-f47a4fc3d900e14653bc059717e2805ad4964a67.tar.xz
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.
-rw-r--r--audio/out/push.c2
-rw-r--r--misc/dispatch.c10
-rw-r--r--osdep/threads.c55
-rw-r--r--osdep/threads.h12
-rw-r--r--osdep/timer.c66
-rw-r--r--osdep/timer.h7
-rw-r--r--player/client.c4
-rw-r--r--stream/cache.c4
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 <http://www.gnu.org/licenses/>.
*/
-#include <time.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include <limits.h>
-
-#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 <pthread.h>
+#include <inttypes.h>
-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 <stdlib.h>
#include <pthread.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <limits.h>
+#include <assert.h>
-#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 <stdio.h>
+#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);