threads: add vlc_cond_timedwait_daytime()

This separates waiting on UTC deadline from waiting on monotonic clock.
That way, storing the reference clock in vlc_cond_t is no longer
necessary. There was only one single condition variable using the UTC
clock (in VLM), so vlc_cond_t.clock was rather wasteful.
This commit is contained in:
Rémi Denis-Courmont 2016-05-23 22:03:45 +03:00
parent a07a959f53
commit 0ef03f60c0
8 changed files with 164 additions and 188 deletions

View File

@ -72,9 +72,8 @@ typedef struct
{
HANDLE semaphore;
LONG waiters;
unsigned clock;
} vlc_cond_t;
#define VLC_STATIC_COND { NULL, 0, 0 }
#define VLC_STATIC_COND { NULL, 0 }
typedef HANDLE vlc_sem_t;
#define LIBVLC_NEED_RWLOCK
typedef struct vlc_threadvar *vlc_threadvar_t;
@ -124,9 +123,8 @@ typedef struct
unsigned waiters;
HEV hevAck;
unsigned signaled;
unsigned clock;
} vlc_cond_t;
#define VLC_STATIC_COND { NULLHANDLE, 0, NULLHANDLE, 0, 0 }
#define VLC_STATIC_COND { NULLHANDLE, 0, NULLHANDLE, 0 }
#define LIBVLC_NEED_SEMAPHORE
#define LIBVLC_NEED_RWLOCK
typedef struct vlc_threadvar *vlc_threadvar_t;
@ -174,14 +172,9 @@ static inline int vlc_poll (struct pollfd *fds, unsigned nfds, int timeout)
typedef struct vlc_thread *vlc_thread_t;
#define VLC_THREAD_CANCELED NULL
typedef pthread_mutex_t vlc_mutex_t;
#define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
typedef struct
{
pthread_cond_t cond;
unsigned clock;
} vlc_cond_t;
#define VLC_STATIC_COND { PTHREAD_COND_INITIALIZER, CLOCK_REALTIME }
typedef pthread_cond_t vlc_cond_t;
#define VLC_STATIC_COND PTHREAD_COND_INITIALIZER
typedef pthread_key_t vlc_threadvar_t;
typedef struct vlc_timer *vlc_timer_t;
@ -227,12 +220,8 @@ typedef pthread_t vlc_thread_t;
#define VLC_THREAD_CANCELED PTHREAD_CANCELED
typedef pthread_mutex_t vlc_mutex_t;
#define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
typedef struct
{
pthread_cond_t cond;
unsigned clock;
} vlc_cond_t;
#define VLC_STATIC_COND { PTHREAD_COND_INITIALIZER, 0 }
typedef pthread_cond_t vlc_cond_t;
#define VLC_STATIC_COND PTHREAD_COND_INITIALIZER
typedef semaphore_t vlc_sem_t;
typedef pthread_rwlock_t vlc_rwlock_t;
#define VLC_STATIC_RWLOCK PTHREAD_RWLOCK_INITIALIZER
@ -292,6 +281,12 @@ typedef pthread_cond_t vlc_cond_t;
/**
* Static initializer for (static) condition variable.
*
* \note
* The condition variable will use the default clock, which is OS-dependent.
* Therefore, where timed waits are necessary the condition variable should
* always be initialized dynamically explicit instead of using this
* initializer.
*/
#define VLC_STATIC_COND PTHREAD_COND_INITIALIZER
@ -419,10 +414,13 @@ VLC_API void vlc_cond_init(vlc_cond_t *);
/**
* Initializes a condition variable (wall clock).
*
* Contrary to vlc_cond_init(), the wall clock will be used as a reference for
* the vlc_cond_timedwait() time-out parameter.
* This function initializes a condition variable for timed waiting using the
* UTC wall clock time. The time reference is the same as with time() and with
* timespec_get() and TIME_UTC.
* vlc_cond_timedwait_daytime() must be instead of
* vlc_cond_timedwait() for actual waiting.
*/
VLC_API void vlc_cond_init_daytime(vlc_cond_t *);
void vlc_cond_init_daytime(vlc_cond_t *);
/**
* Deinitializes a condition variable.
@ -485,23 +483,26 @@ VLC_API void vlc_cond_wait(vlc_cond_t *cond, vlc_mutex_t *mutex);
* Waits on a condition variable up to a certain date.
*
* This works like vlc_cond_wait() but with an additional time-out.
* The time-out is expressed as an absolute timestamp using the same arbitrary
* time reference as the mdate() and mwait() functions.
*
* If the variable was initialized with vlc_cond_init(), the timeout has the
* same arbitrary origin as mdate(). If the variable was initialized with
* vlc_cond_init_daytime(), or was statically initialized with
* \ref VLC_STATIC_COND, the timeout is expressed from the Unix epoch (i.e.
* the wall clock).
*
* \param p_condvar condition variable to wait on
* \param p_mutex mutex which is unlocked while waiting,
* then locked again when waking up.
* \param cond condition variable to wait on
* \param mutex mutex which is unlocked while waiting,
* then locked again when waking up
* \param deadline <b>absolute</b> timeout
*
* \warning If the variable was initialized with vlc_cond_init_daytime(), or
* was statically initialized with \ref VLC_STATIC_COND, the time reference
* used by this function is unspecified (depending on the implementation, it
* might be the Unix epoch or the mdate() clock).
*
* \return 0 if the condition was signaled, an error code in case of timeout.
*/
VLC_API int vlc_cond_timedwait(vlc_cond_t *cond, vlc_mutex_t *mutex,
mtime_t deadline);
int vlc_cond_timedwait_daytime(vlc_cond_t *, vlc_mutex_t *, time_t);
/**
* Initializes a semaphore.
*

View File

@ -190,7 +190,7 @@ void vlc_threads_setup (libvlc_int_t *p_libvlc)
void vlc_cond_init (vlc_cond_t *condvar)
{
#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
if (unlikely(pthread_cond_init (&condvar->cond, NULL)))
if (unlikely(pthread_cond_init (condvar, NULL)))
abort ();
#else
pthread_condattr_t attr;
@ -198,34 +198,32 @@ void vlc_cond_init (vlc_cond_t *condvar)
pthread_condattr_init (&attr);
pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
if (unlikely(pthread_cond_init (&condvar->cond, &attr)))
if (unlikely(pthread_cond_init (condvar, &attr)))
abort ();
#endif
condvar->clock = CLOCK_MONOTONIC;
}
void vlc_cond_init_daytime (vlc_cond_t *condvar)
{
if (unlikely(pthread_cond_init (&condvar->cond, NULL)))
if (unlikely(pthread_cond_init (condvar, NULL)))
abort ();
condvar->clock = CLOCK_REALTIME;
}
void vlc_cond_destroy (vlc_cond_t *condvar)
{
int val = pthread_cond_destroy (&condvar->cond);
int val = pthread_cond_destroy (condvar);
VLC_THREAD_ASSERT ("destroying condition");
}
void vlc_cond_signal (vlc_cond_t *condvar)
{
int val = pthread_cond_signal (&condvar->cond);
int val = pthread_cond_signal (condvar);
VLC_THREAD_ASSERT ("signaling condition variable");
}
void vlc_cond_broadcast (vlc_cond_t *condvar)
{
pthread_cond_broadcast (&condvar->cond);
pthread_cond_broadcast (condvar);
}
void vlc_cond_wait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex)
@ -237,7 +235,7 @@ void vlc_cond_wait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex)
vlc_testcancel ();
if (vlc_mutex_trylock (&th->lock) == 0)
{
th->cond = &condvar->cond;
th->cond = condvar;
vlc_mutex_unlock (&th->lock);
}
else
@ -250,7 +248,7 @@ void vlc_cond_wait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex)
}
}
int val = pthread_cond_wait (&condvar->cond, p_mutex);
int val = pthread_cond_wait (condvar, p_mutex);
VLC_THREAD_ASSERT ("waiting on condition");
if (th != NULL)
@ -262,21 +260,21 @@ void vlc_cond_wait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex)
}
}
int vlc_cond_timedwait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex,
mtime_t deadline)
typedef int (*vlc_cond_wait_cb)(pthread_cond_t *, pthread_mutex_t *,
const struct timespec *);
static int vlc_cond_timedwait_common(vlc_cond_t *condvar, vlc_mutex_t *mutex,
const struct timespec *ts,
vlc_cond_wait_cb cb)
{
struct timespec ts = mtime_to_ts (deadline);
vlc_thread_t th = thread;
#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
int (*cb)(pthread_cond_t *, pthread_mutex_t *, const struct timespec *);
#endif
if (th != NULL)
{
vlc_testcancel ();
if (vlc_mutex_trylock (&th->lock) == 0)
{
th->cond = &condvar->cond;
th->cond = condvar;
vlc_mutex_unlock (&th->lock);
}
else
@ -289,24 +287,7 @@ int vlc_cond_timedwait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex,
}
}
#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
switch (condvar->clock)
{
case CLOCK_REALTIME:
cb = pthread_cond_timedwait;
break;
case CLOCK_MONOTONIC:
cb = pthread_cond_timedwait_monotonic_np;
break;
default:
vlc_assert_unreachable ();
}
int val = cb (&condvar->cond, p_mutex, &ts);
#else
int val = pthread_cond_timedwait(&condvar->cond, p_mutex, &ts);
#endif
int val = cb(condvar, p_mutex, ts);
if (val != ETIMEDOUT)
VLC_THREAD_ASSERT ("timed-waiting on condition");
@ -320,6 +301,26 @@ int vlc_cond_timedwait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex,
return val;
}
int vlc_cond_timedwait(vlc_cond_t *cond, vlc_mutex_t *mutex, mtime_t deadline)
{
struct timespec ts = mtime_to_ts(deadline);
#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
return vlc_cond_timedwait_common(cond, mutex, &ts,
pthread_cond_timedwait_monotonic_np);
#else
return vlc_cond_timedwait_common(cond, mutex, &ts, pthread_cond_timedwait);
#endif
}
int vlc_cond_timedwait_daytime(vlc_cond_t *cond, vlc_mutex_t *mutex,
time_t deadline)
{
struct timespec ts = { deadline, 0 };
return vlc_cond_timedwait_common(cond, mutex, &ts, pthread_cond_timedwait);
}
/* pthread */
static void clean_detached_thread(void *data)
{

View File

@ -193,30 +193,21 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
VLC_THREAD_ASSERT ("unlocking mutex");
}
enum
{
VLC_CLOCK_MONOTONIC = 0,
VLC_CLOCK_REALTIME,
};
void vlc_cond_init (vlc_cond_t *p_condvar)
{
if (unlikely(pthread_cond_init (&p_condvar->cond, NULL)))
if (unlikely(pthread_cond_init (p_condvar, NULL)))
abort ();
p_condvar->clock = VLC_CLOCK_MONOTONIC;
}
void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
{
if (unlikely(pthread_cond_init (&p_condvar->cond, NULL)))
if (unlikely(pthread_cond_init (p_condvar, NULL)))
abort ();
p_condvar->clock = VLC_CLOCK_REALTIME;
}
void vlc_cond_destroy (vlc_cond_t *p_condvar)
{
int val = pthread_cond_destroy (&p_condvar->cond);
int val = pthread_cond_destroy (p_condvar);
/* due to a faulty pthread implementation within Darwin 11 and
* later condition variables cannot be destroyed without
@ -244,26 +235,48 @@ void vlc_cond_destroy (vlc_cond_t *p_condvar)
void vlc_cond_signal (vlc_cond_t *p_condvar)
{
int val = pthread_cond_signal (&p_condvar->cond);
int val = pthread_cond_signal (p_condvar);
VLC_THREAD_ASSERT ("signaling condition variable");
}
void vlc_cond_broadcast (vlc_cond_t *p_condvar)
{
pthread_cond_broadcast (&p_condvar->cond);
pthread_cond_broadcast (p_condvar);
}
void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
{
int val = pthread_cond_wait (&p_condvar->cond, p_mutex);
int val = pthread_cond_wait (p_condvar, p_mutex);
VLC_THREAD_ASSERT ("waiting on condition");
}
int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
mtime_t deadline)
{
int val = 0;
/* according to POSIX standards, cond_timedwait should be a cancellation point
* Of course, Darwin does not care */
pthread_testcancel();
/*
* mdate() is the monotonic clock, pthread_cond_timedwait expects
* origin of gettimeofday(). Use timedwait_relative_np() instead.
*/
mtime_t base = mdate();
deadline -= base;
if (deadline < 0)
deadline = 0;
struct timespec ts = mtime_to_ts(deadline);
int val = pthread_cond_timedwait_relative_np(p_condvar, p_mutex, &ts);
if (val != ETIMEDOUT)
VLC_THREAD_ASSERT ("timed-waiting on condition");
return val;
}
/* variant for vlc_cond_init_daytime */
int vlc_cond_timedwait_daytime (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
time_t deadline)
{
/*
* Note that both pthread_cond_timedwait_relative_np and pthread_cond_timedwait
* convert the given timeout to a mach absolute deadline, with system startup
@ -272,43 +285,22 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
* For more details, see: https://devforums.apple.com/message/931605
*/
/* according to POSIX standards, cond_timedwait should be a cancellation point
* Of course, Darwin does not care */
pthread_testcancel();
if (p_condvar->clock == VLC_CLOCK_MONOTONIC) {
/*
* FIXME: It is assumed, that in this case the system waits until the real
* time deadline is passed, even if the real time is adjusted in between.
* This is not fulfilled, as described above.
*/
struct timespec ts = mtime_to_ts(deadline);
int val = pthread_cond_timedwait(p_condvar, p_mutex, &ts);
/*
* mdate() is the monotonic clock, pthread_cond_timedwait expects
* origin of gettimeofday(). Use timedwait_relative_np() instead.
*/
mtime_t base = mdate();
deadline -= base;
if (deadline < 0)
deadline = 0;
struct timespec ts = mtime_to_ts(deadline);
val = pthread_cond_timedwait_relative_np(&p_condvar->cond, p_mutex, &ts);
} else {
/* variant for vlc_cond_init_daytime */
assert (p_condvar->clock == VLC_CLOCK_REALTIME);
/*
* FIXME: It is assumed, that in this case the system waits until the real
* time deadline is passed, even if the real time is adjusted in between.
* This is not fulfilled, as described above.
*/
struct timespec ts = mtime_to_ts(deadline);
val = pthread_cond_timedwait(&p_condvar->cond, p_mutex, &ts);
}
if (val != ETIMEDOUT)
VLC_THREAD_ASSERT ("timed-waiting on condition");
return val;
}
/* Initialize a semaphore. */
void vlc_sem_init (vlc_sem_t *sem, unsigned value)
{

View File

@ -395,7 +395,7 @@ static void* Manage( void* p_object )
while( !vlm->input_state_changed && !scheduled_command )
{
if( nextschedule != 0 )
scheduled_command = vlc_cond_timedwait( &vlm->wait_manage, &vlm->lock_manage, nextschedule * CLOCK_FREQ ) != 0;
scheduled_command = vlc_cond_timedwait_daytime( &vlm->wait_manage, &vlm->lock_manage, nextschedule ) != 0;
else
vlc_cond_wait( &vlm->wait_manage, &vlm->lock_manage );
}

View File

@ -517,7 +517,6 @@ VLC_Compiler
vlc_cond_broadcast
vlc_cond_destroy
vlc_cond_init
vlc_cond_init_daytime
vlc_cond_signal
vlc_cond_timedwait
vlc_cond_wait

View File

@ -256,25 +256,6 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
}
/*** Condition variables ***/
#undef CLOCK_REALTIME
#undef CLOCK_MONOTONIC
enum
{
CLOCK_REALTIME=0, /* must be zero for VLC_STATIC_COND */
CLOCK_MONOTONIC,
};
static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
{
if (DosCreateEventSem (NULL, &p_condvar->hev, 0, FALSE) ||
DosCreateEventSem (NULL, &p_condvar->hevAck, 0, FALSE))
abort();
p_condvar->waiters = 0;
p_condvar->signaled = 0;
p_condvar->clock = clock;
}
typedef struct vlc_static_cond_t vlc_static_cond_t;
struct vlc_static_cond_t
@ -291,7 +272,7 @@ static void vlc_static_cond_init (vlc_cond_t *p_condvar)
if (p_condvar->hev == NULLHANDLE)
{
vlc_cond_init_common (p_condvar, p_condvar->clock);
vlc_cond_init (p_condvar);
vlc_static_cond_t *new_static_condvar;
@ -325,12 +306,17 @@ static void vlc_static_cond_destroy_all (void)
void vlc_cond_init (vlc_cond_t *p_condvar)
{
vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
if (DosCreateEventSem (NULL, &p_condvar->hev, 0, FALSE) ||
DosCreateEventSem (NULL, &p_condvar->hevAck, 0, FALSE))
abort();
p_condvar->waiters = 0;
p_condvar->signaled = 0;
}
void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
{
vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
vlc_cond_init (p_condvar);
}
void vlc_cond_destroy (vlc_cond_t *p_condvar)
@ -371,6 +357,8 @@ static int vlc_cond_wait_common (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
ULONG ulPost;
ULONG rc;
assert(p_condvar->hev != NULLHANDLE);
do
{
vlc_testcancel();
@ -408,28 +396,29 @@ void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
mtime_t deadline)
{
ULONG ulTimeout;
ULONG ulTimeout;
if (p_condvar->hev == NULLHANDLE)
vlc_static_cond_init (p_condvar);
mtime_t total = mdate();
total = (deadline - total) / 1000;
if( total < 0 )
total = 0;
ulTimeout = ( total > 0x7fffffff ) ? 0x7fffffff : total;
return vlc_cond_wait_common (p_condvar, p_mutex, ulTimeout);
}
int vlc_cond_timedwait_daytime (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
time_t deadline)
{
ULONG ulTimeout;
mtime_t total;
switch (p_condvar->clock)
{
case CLOCK_REALTIME:
{
struct timeval tv;
gettimeofday (&tv, NULL);
struct timeval tv;
total = CLOCK_FREQ * tv.tv_sec +
CLOCK_FREQ * tv.tv_usec / 1000000L;
break;
}
default:
assert (p_condvar->clock == CLOCK_MONOTONIC);
total = mdate();
break;
}
gettimeofday (&tv, NULL);
total = CLOCK_FREQ * tv.tv_sec +
CLOCK_FREQ * tv.tv_usec / 1000000L;
total = (deadline - total) / 1000;
if( total < 0 )
total = 0;

View File

@ -303,6 +303,16 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
return val;
}
int vlc_cond_timedwait_daytime (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
time_t deadline)
{
struct timespec ts = { deadline, 0 };
int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
if (val != ETIMEDOUT)
VLC_THREAD_ASSERT ("timed-waiting on condition");
return val;
}
void vlc_sem_init (vlc_sem_t *sem, unsigned value)
{
if (unlikely(sem_init (sem, 0, value)))

View File

@ -195,29 +195,17 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
}
/*** Condition variables ***/
enum
{
VLC_CLOCK_REALTIME=0, /* must be zero for VLC_STATIC_COND */
VLC_CLOCK_MONOTONIC,
};
static void vlc_cond_init_common(vlc_cond_t *wait, unsigned clock)
void vlc_cond_init(vlc_cond_t *wait)
{
wait->semaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
if (unlikely(wait->semaphore == NULL))
abort();
wait->waiters = 0;
wait->clock = clock;
}
void vlc_cond_init (vlc_cond_t *p_condvar)
{
vlc_cond_init_common (p_condvar, VLC_CLOCK_MONOTONIC);
}
void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
{
vlc_cond_init_common (p_condvar, VLC_CLOCK_REALTIME);
vlc_cond_init (p_condvar);
}
void vlc_cond_destroy(vlc_cond_t *wait)
@ -260,9 +248,15 @@ void vlc_cond_broadcast(vlc_cond_t *wait)
ReleaseSemaphore(wait->semaphore, waiters, NULL);
}
static DWORD vlc_cond_wait_delay(vlc_cond_t *wait, vlc_mutex_t *lock,
DWORD delay)
static int vlc_cond_wait_delay(vlc_cond_t *wait, vlc_mutex_t *lock,
mtime_t us)
{
if (us < 0)
us = 0;
if (us > 0x7fffffff)
us = 0x7fffffff;
DWORD delay = us;
DWORD result;
vlc_testcancel();
@ -282,7 +276,7 @@ static DWORD vlc_cond_wait_delay(vlc_cond_t *wait, vlc_mutex_t *lock,
if (result == WAIT_IO_COMPLETION)
vlc_testcancel();
return result;
return result == WAIT_TIMEOUT ? ETIMEDOUT : 0;
}
void vlc_cond_wait(vlc_cond_t *wait, vlc_mutex_t *lock)
@ -292,29 +286,19 @@ void vlc_cond_wait(vlc_cond_t *wait, vlc_mutex_t *lock)
int vlc_cond_timedwait(vlc_cond_t *wait, vlc_mutex_t *lock, mtime_t deadline)
{
mtime_t total;
return vlc_cond_wait_delay(wait, lock, deadline - mdate());
}
switch (wait->clock)
{
case VLC_CLOCK_REALTIME: /* FIXME? sub-second precision */
total = CLOCK_FREQ * time(NULL);
break;
case VLC_CLOCK_MONOTONIC:
total = mdate();
break;
default:
vlc_assert_unreachable();
}
int vlc_cond_timedwait_daytime(vlc_cond_t *wait, vlc_mutex_t *lock,
time_t deadline)
{
time_t now;
mtime_t delay;
total = (deadline - total) / 1000;
if (total < 0)
total = 0;
time(&now);
delay = ((mtime_t)deadline - (mtime_t)now) * CLOCK_FREQ;
DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
if (vlc_cond_wait_delay(wait, lock, delay) == WAIT_TIMEOUT)
return ETIMEDOUT;
return 0;
return vlc_cond_wait_delay(wait, lock, delay);
}
/*** Semaphore ***/