Commit 35e2425f authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Asynchronous timer API

parent 1074e9da
......@@ -108,6 +108,13 @@ typedef pthread_mutex_t vlc_mutex_t;
#define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
typedef pthread_cond_t vlc_cond_t;
typedef pthread_key_t vlc_threadvar_t;
typedef struct vlc_timer_t vlc_timer_t;
struct vlc_timer_t
{
timer_t handle;
void (*func) (vlc_timer_t *, void *);
void *data;
};
#elif defined( WIN32 )
typedef struct
......@@ -129,6 +136,16 @@ typedef struct
typedef HANDLE vlc_cond_t;
typedef DWORD vlc_threadvar_t;
typedef struct vlc_timer_t vlc_timer_t;
struct vlc_timer_t
{
HANDLE handle;
void (*func) (vlc_timer_t *, void *);
void *data;
unsigned overrun;
CRITICAL_SECTION serializer;
LONG volatile counter;
};
#endif
......@@ -164,6 +181,11 @@ VLC_EXPORT( void, vlc_cancel, (vlc_thread_t) );
VLC_EXPORT( void, vlc_join, (vlc_thread_t, void **) );
VLC_EXPORT (void, vlc_control_cancel, (int cmd, ...));
VLC_EXPORT( int, vlc_timer_create, (vlc_timer_t *, void (*) (vlc_timer_t *, void *), void *) LIBVLC_USED );
VLC_EXPORT( void, vlc_timer_destroy, (vlc_timer_t *) );
VLC_EXPORT( void, vlc_timer_schedule, (vlc_timer_t *, bool, mtime_t, mtime_t) );
VLC_EXPORT( unsigned, vlc_timer_getoverrun, (const vlc_timer_t *) );
#ifndef LIBVLC_USE_PTHREAD_CANCEL
enum {
VLC_DO_CANCEL,
......
......@@ -522,6 +522,10 @@ vlc_threadvar_create
vlc_threadvar_delete
vlc_threadvar_get
vlc_threadvar_set
vlc_timer_create
vlc_timer_destroy
vlc_timer_getoverrun
vlc_timer_schedule
vlc_ureduce
VLC_Version
vlc_wclosedir
......
......@@ -583,3 +583,110 @@ void vlc_control_cancel (int cmd, ...)
(void) cmd;
assert (0);
}
static void vlc_timer_do (union sigval val)
{
vlc_timer_t *id = val.sival_ptr;
id->func (id, id->data);
}
/**
* Initializes an asynchronous timer.
* @warning Asynchronous timers are processed from an unspecified thread, and
* a timer is only serialized against itself.
*
* @param id pointer to timer to be initialized
* @param func function that the timer will call
* @param data parameter for the timer function
* @return 0 on success, a system error code otherwise.
*/
int vlc_timer_create (vlc_timer_t *id, void (*func) (vlc_timer_t *, void *),
void *data)
{
struct sigevent ev;
memset (&ev, 0, sizeof (ev));
ev.sigev_notify = SIGEV_THREAD;
ev.sigev_value.sival_ptr = id;
ev.sigev_notify_function = vlc_timer_do;
ev.sigev_notify_attributes = NULL;
id->func = func;
id->data = data;
if (timer_create (CLOCK_MONOTONIC, &ev, &id->handle))
return errno;
return 0;
}
/**
* Destroys an initialized timer. If needed, the timer is first disarmed.
* This function is undefined if the specified timer is not initialized.
*
* @warning This function <b>must</b> be called before the timer data can be
* freed and before the timer callback function can be unloaded.
*
* @param timer to destroy
*/
void vlc_timer_destroy (vlc_timer_t *id)
{
int val = timer_delete (id->handle);
VLC_THREAD_ASSERT ("deleting timer");
}
/**
* Arm or disarm an initialized timer.
* This functions overrides any previous call to itself.
*
* @note A timer can fire later than requested due to system scheduling
* limitations. An interval timer can fail to trigger sometimes, either because
* the system is busy or suspended, or because a previous iteration of the
* timer is still running. See also vlc_timer_getoverrun().
*
* @param id initialized timer pointer
* @param absolute the timer value origin is the same as mdate() if true,
* the timer value is relative to now if false.
* @param value zero to disarm the timer, otherwise the initial time to wait
* before firing the timer.
* @param interval zero to fire the timer just once, otherwise the timer
* repetition interval.
*/
void vlc_timer_schedule (vlc_timer_t *id, bool absolute,
mtime_t value, mtime_t interval)
{
lldiv_t vad = lldiv (value, CLOCK_FREQ);
lldiv_t itd = lldiv (interval, CLOCK_FREQ);
struct itimerspec it = {
.it_interval = {
.tv_sec = itd.quot,
.tv_nsec = (1000000000 / CLOCK_FREQ) * itd.rem,
},
.it_value = {
.tv_sec = vad.quot,
.tv_nsec = (1000000000 / CLOCK_FREQ) * vad.rem,
},
};
int flags = absolute ? TIMER_ABSTIME : 0;
int val = timer_settime (id->handle, flags, &it, NULL);
VLC_THREAD_ASSERT ("scheduling timer");
}
/**
* @param id initialized timer pointer
* @return the timer overrun counter, i.e. the number of times that the timer
* should have run but did not since the last actual run. If all is well, this
* is zero.
*/
unsigned vlc_timer_getoverrun (const vlc_timer_t *id)
{
int val = timer_getoverrun (id->handle);
#ifndef NDEBUG
if (val == -1)
{
val = errno;
VLC_THREAD_ASSERT ("fetching timer overrun counter");
}
#endif
return val;
}
......@@ -510,3 +510,63 @@ void vlc_control_cancel (int cmd, ...)
}
va_end (ap);
}
/*** Timers ***/
static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
{
vlc_timer_t *id = val;
assert (timeout);
if (TryEnterCriticalSection (&id->serializer))
{
id->overrun = InterlockedExchange (&id->counter, 0);
id->func (id, id->data);
LeaveCriticalSection (&id->serializer);
}
else /* Overrun */
InterlockedIncrement (&id->counter);
}
int vlc_timer_create (vlc_timer_t *id, void (*func) (vlc_timer_t *, void *),
void *data)
{
id->func = func;
id->data = data;
id->overrun = 0;
id->handle = INVALID_HANDLE_VALUE;
InitializeCriticalSection (&id->serializer);
return 0;
}
void vlc_timer_destroy (vlc_timer_t *id)
{
if (id->handle != INVALID_HANDLE_VALUE)
DeleteTimerQueueTimer (NULL, id->handle, NULL);
DeleteCriticalSection (&id->serializer);
}
void vlc_timer_schedule (vlc_timer_t *id, bool absolute,
mtime_t value, mtime_t interval)
{
if (id->handle != INVALID_HANDLE_VALUE)
{
DeleteTimerQueueTimer (NULL, id->handle, NULL);
id->handle = INVALID_HANDLE_VALUE;
}
if (value == 0)
return; /* Disarm */
if (absolute)
value -= mdate ();
value = (value + 999) / 1000;
interval = (interval + 999) / 1000;
if (!CreateTimerQueueTimer (&id->handle, NULL, vlc_timer_do, id, value,
interval, WT_EXECUTEDEFAULT))
abort ();
}
unsigned vlc_timer_getoverrun (const vlc_timer_t *id)
{
return id->overrun;
}
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