mirror of
https://github.com/mpv-player/mpv
synced 2025-04-08 10:32:51 +00:00
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.
This commit is contained in:
parent
e209e44ca2
commit
f47a4fc3d9
@ -274,7 +274,7 @@ static void *playthread(void *arg)
|
|||||||
MP_STATS(ao, "start audio wait");
|
MP_STATS(ao, "start audio wait");
|
||||||
pthread_mutex_lock(&p->wakeup_lock);
|
pthread_mutex_lock(&p->wakeup_lock);
|
||||||
if (!p->need_wakeup)
|
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;
|
p->need_wakeup = false;
|
||||||
pthread_mutex_unlock(&p->wakeup_lock);
|
pthread_mutex_unlock(&p->wakeup_lock);
|
||||||
MP_STATS(ao, "end audio wait");
|
MP_STATS(ao, "end audio wait");
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "osdep/threads.h"
|
#include "osdep/threads.h"
|
||||||
|
#include "osdep/timer.h"
|
||||||
|
|
||||||
#include "dispatch.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.)
|
// still process the remaining queue items, and wait for unsuspend.)
|
||||||
void mp_dispatch_queue_process(struct mp_dispatch_queue *queue, double timeout)
|
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);
|
pthread_mutex_lock(&queue->lock);
|
||||||
queue->suspended = true;
|
queue->suspended = true;
|
||||||
// Wake up thread which called mp_dispatch_suspend().
|
// Wake up thread which called mp_dispatch_suspend().
|
||||||
pthread_cond_broadcast(&queue->cond);
|
pthread_cond_broadcast(&queue->cond);
|
||||||
while (queue->head || queue->suspend_requested || timeout > 0) {
|
while (queue->head || queue->suspend_requested || wait > 0) {
|
||||||
if (queue->head) {
|
if (queue->head) {
|
||||||
struct mp_dispatch_item *item = queue->head;
|
struct mp_dispatch_item *item = queue->head;
|
||||||
queue->head = item->next;
|
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);
|
pthread_cond_broadcast(&queue->cond);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (timeout > 0) {
|
if (wait > 0) {
|
||||||
mpthread_cond_timedwait(&queue->cond, &queue->lock, timeout);
|
mpthread_cond_timedwait(&queue->cond, &queue->lock, wait);
|
||||||
} else {
|
} else {
|
||||||
pthread_cond_wait(&queue->cond, &queue->lock);
|
pthread_cond_wait(&queue->cond, &queue->lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timeout = 0;
|
wait = 0;
|
||||||
}
|
}
|
||||||
queue->suspended = false;
|
queue->suspended = false;
|
||||||
pthread_mutex_unlock(&queue->lock);
|
pthread_mutex_unlock(&queue->lock);
|
||||||
|
@ -15,61 +15,22 @@
|
|||||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
* 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 "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)
|
|
||||||
{
|
|
||||||
struct timespec ts;
|
|
||||||
get_pthread_time(&ts);
|
|
||||||
timespec_add_seconds(&ts, timeout);
|
|
||||||
return ts;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call pthread_cond_timedwait() with a relative timeout in seconds
|
|
||||||
int mpthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
int mpthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||||
double timeout)
|
int64_t abstime)
|
||||||
{
|
{
|
||||||
struct timespec ts = mpthread_get_deadline(timeout);
|
struct timespec ts = mp_time_us_to_timespec(abstime);
|
||||||
return pthread_cond_timedwait(cond, mutex, &ts);
|
return pthread_cond_timedwait(cond, mutex, &ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to reduce boiler plate.
|
int mpthread_cond_timedwait_rel(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||||
|
double s)
|
||||||
|
{
|
||||||
|
return mpthread_cond_timedwait(cond, mutex, mp_add_timeout(mp_time_us(), s));
|
||||||
|
}
|
||||||
|
|
||||||
int mpthread_mutex_init_recursive(pthread_mutex_t *mutex)
|
int mpthread_mutex_init_recursive(pthread_mutex_t *mutex)
|
||||||
{
|
{
|
||||||
pthread_mutexattr_t attr;
|
pthread_mutexattr_t attr;
|
||||||
|
@ -2,12 +2,18 @@
|
|||||||
#define MP_OSDEP_THREADS_H_
|
#define MP_OSDEP_THREADS_H_
|
||||||
|
|
||||||
#include <pthread.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,
|
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);
|
int mpthread_mutex_init_recursive(pthread_mutex_t *mutex);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,9 +17,15 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <pthread.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 "common/msg.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
static uint64_t raw_time_offset;
|
static uint64_t raw_time_offset;
|
||||||
pthread_once_t timer_init_once = PTHREAD_ONCE_INIT;
|
pthread_once_t timer_init_once = PTHREAD_ONCE_INIT;
|
||||||
@ -60,21 +66,75 @@ int64_t mp_time_relative_us(int64_t *t)
|
|||||||
return r;
|
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
|
#if 0
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "threads.h"
|
||||||
|
|
||||||
|
#define TEST_SLEEP 1
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
int c = 200;
|
int c = 2000000;
|
||||||
int64_t j, r, t = 0;
|
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();
|
mp_time_init();
|
||||||
|
|
||||||
for (int i = 0; i < c; i++) {
|
for (int i = 0; i < c; i++) {
|
||||||
const int delay = rand() / (RAND_MAX / 1e5);
|
const int delay = rand() / (RAND_MAX / 1e5);
|
||||||
r = mp_time_us();
|
r = mp_time_us();
|
||||||
|
#if TEST_SLEEP
|
||||||
mp_sleep_us(delay);
|
mp_sleep_us(delay);
|
||||||
|
#else
|
||||||
|
mpthread_cond_timedwait(&cnd, &mtx, r + delay);
|
||||||
|
#endif
|
||||||
j = (mp_time_us() - r) - delay;
|
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;
|
t += j;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "average error:\t%i\n", (int)(t / c));
|
fprintf(stderr, "average error:\t%i\n", (int)(t / c));
|
||||||
|
@ -44,4 +44,11 @@ void mp_sleep_us(int64_t us);
|
|||||||
// first call will return 0, instead of the absolute current time.)
|
// first call will return 0, instead of the absolute current time.)
|
||||||
int64_t mp_time_relative_us(int64_t *t);
|
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 */
|
#endif /* MPLAYER_TIMER_H */
|
||||||
|
@ -477,7 +477,7 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
|
|||||||
{
|
{
|
||||||
mpv_event *event = ctx->cur_event;
|
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);
|
pthread_mutex_lock(&ctx->lock);
|
||||||
|
|
||||||
@ -519,7 +519,7 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
|
|||||||
break;
|
break;
|
||||||
if (timeout <= 0)
|
if (timeout <= 0)
|
||||||
break;
|
break;
|
||||||
int r = pthread_cond_timedwait(&ctx->wakeup, &ctx->lock, &deadline);
|
int r = mpthread_cond_timedwait(&ctx->wakeup, &ctx->lock, deadline);
|
||||||
if (r == ETIMEDOUT)
|
if (r == ETIMEDOUT)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ static int cache_wakeup_and_wait(struct priv *s, double *retry_time)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pthread_cond_signal(&s->wakeup);
|
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;
|
*retry_time += mp_time_sec() - start;
|
||||||
|
|
||||||
@ -529,7 +529,7 @@ static void *cache_thread(void *arg)
|
|||||||
s->control = CACHE_CTRL_NONE;
|
s->control = CACHE_CTRL_NONE;
|
||||||
}
|
}
|
||||||
if (s->idle && 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_cond_signal(&s->wakeup);
|
||||||
pthread_mutex_unlock(&s->mutex);
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
Loading…
Reference in New Issue
Block a user