Commit 66c47c31 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

vlc_timer_schedule: fix resetting the timer from itself

This also fixes error handling: if the thread cannot be created, we do
fail cleanly.
parent b1766037
...@@ -800,6 +800,7 @@ void vlc_control_cancel (int cmd, ...) ...@@ -800,6 +800,7 @@ void vlc_control_cancel (int cmd, ...)
struct vlc_timer struct vlc_timer
{ {
vlc_thread_t thread; vlc_thread_t thread;
vlc_cond_t reschedule;
vlc_mutex_t lock; vlc_mutex_t lock;
void (*func) (void *); void (*func) (void *);
void *data; void *data;
...@@ -810,37 +811,43 @@ struct vlc_timer ...@@ -810,37 +811,43 @@ struct vlc_timer
static void *vlc_timer_thread (void *data) static void *vlc_timer_thread (void *data)
{ {
struct vlc_timer *timer = data; struct vlc_timer *timer = data;
mtime_t value, interval;
vlc_mutex_lock (&timer->lock); vlc_mutex_lock (&timer->lock);
value = timer->value; mutex_cleanup_push (&timer->lock);
interval = timer->interval;
vlc_mutex_unlock (&timer->lock);
for (;;) for (;;)
{ {
mwait (value); while (timer->value == 0)
vlc_cond_wait (&timer->reschedule, &timer->lock);
if (vlc_cond_timedwait (&timer->reschedule, &timer->lock,
timer->value) == 0)
continue;
vlc_mutex_unlock (&timer->lock);
int canc = vlc_savecancel (); int canc = vlc_savecancel ();
timer->func (timer->data); timer->func (timer->data);
vlc_restorecancel (canc); vlc_restorecancel (canc);
if (interval == 0)
return NULL;
mtime_t now = mdate (); mtime_t now = mdate ();
unsigned misses = (now - value) / interval; unsigned misses;
vlc_mutex_lock (&timer->lock);
misses = (now - timer->value) / timer->interval;
timer->value += timer->interval;
/* Try to compensate for one miss (mwait() will return immediately) /* Try to compensate for one miss (mwait() will return immediately)
* but no more. Otherwise, we might busy loop, after extended periods * but no more. Otherwise, we might busy loop, after extended periods
* without scheduling (suspend, SIGSTOP, RT preemption, ...). */ * without scheduling (suspend, SIGSTOP, RT preemption, ...). */
if (misses > 1) if (misses > 1)
{ {
misses--; misses--;
timer->value += misses * timer->interval;
vlc_atomic_add (&timer->overruns, misses); vlc_atomic_add (&timer->overruns, misses);
value += misses * interval;
} }
value += interval;
} }
vlc_cleanup_pop ();
assert (0);
} }
/** /**
...@@ -861,12 +868,23 @@ int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data) ...@@ -861,12 +868,23 @@ int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
if (unlikely(timer == NULL)) if (unlikely(timer == NULL))
return ENOMEM; return ENOMEM;
vlc_mutex_init (&timer->lock); vlc_mutex_init (&timer->lock);
vlc_cond_init (&timer->reschedule);
assert (func); assert (func);
timer->func = func; timer->func = func;
timer->data = data; timer->data = data;
timer->value = 0; timer->value = 0;
timer->interval = 0; timer->interval = 0;
vlc_atomic_set(&timer->overruns, 0); vlc_atomic_set(&timer->overruns, 0);
if (vlc_clone (&timer->thread, vlc_timer_thread, timer,
VLC_THREAD_PRIORITY_INPUT))
{
vlc_cond_destroy (&timer->reschedule);
vlc_mutex_destroy (&timer->lock);
free (timer);
return ENOMEM;
}
*id = timer; *id = timer;
return 0; return 0;
} }
...@@ -882,7 +900,9 @@ int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data) ...@@ -882,7 +900,9 @@ int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
*/ */
void vlc_timer_destroy (vlc_timer_t timer) void vlc_timer_destroy (vlc_timer_t timer)
{ {
vlc_timer_schedule (timer, false, 0, 0); vlc_cancel (timer->thread);
vlc_join (timer->thread, NULL);
vlc_cond_destroy (&timer->reschedule);
vlc_mutex_destroy (&timer->lock); vlc_mutex_destroy (&timer->lock);
free (timer); free (timer);
} }
...@@ -907,25 +927,13 @@ void vlc_timer_destroy (vlc_timer_t timer) ...@@ -907,25 +927,13 @@ void vlc_timer_destroy (vlc_timer_t timer)
void vlc_timer_schedule (vlc_timer_t timer, bool absolute, void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
mtime_t value, mtime_t interval) mtime_t value, mtime_t interval)
{ {
vlc_mutex_lock (&timer->lock); if (!absolute && value != 0)
while (timer->value) value += mdate();
{
vlc_thread_t thread = timer->thread;
timer->value = 0; vlc_mutex_lock (&timer->lock);
vlc_mutex_unlock (&timer->lock); timer->value = value;
vlc_cancel (thread); timer->interval = interval;
/* cannot keep the lock during vlc_join X( */ vlc_cond_signal (&timer->reschedule);
vlc_join (thread, NULL);
vlc_mutex_lock (&timer->lock);
}
if ((value != 0)
&& (vlc_clone (&timer->thread, vlc_timer_thread, timer,
VLC_THREAD_PRIORITY_INPUT) == 0))
{
timer->value = (absolute ? 0 : mdate ()) + value;
timer->interval = interval;
}
vlc_mutex_unlock (&timer->lock); vlc_mutex_unlock (&timer->lock);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment