From: RĂ©mi Denis-Courmont Date: Tue, 2 Jun 2009 17:19:19 +0000 (+0300) Subject: Asynchronous timer API X-Git-Tag: 1.1.0-ff~5637 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=35e2425f9a49e674683479fde78fd7972b389a71;p=vlc Asynchronous timer API --- diff --git a/include/vlc_threads.h b/include/vlc_threads.h index 7ccafbbafc..d6535950a2 100644 --- a/include/vlc_threads.h +++ b/include/vlc_threads.h @@ -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, diff --git a/src/libvlccore.sym b/src/libvlccore.sym index 239ff76bba..bd116a8d67 100644 --- a/src/libvlccore.sym +++ b/src/libvlccore.sym @@ -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 diff --git a/src/misc/pthread.c b/src/misc/pthread.c index 757df9ac62..f3127f3e0f 100644 --- a/src/misc/pthread.c +++ b/src/misc/pthread.c @@ -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 must 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; +} diff --git a/src/misc/w32thread.c b/src/misc/w32thread.c index 5e187cab32..60f72c7f86 100644 --- a/src/misc/w32thread.c +++ b/src/misc/w32thread.c @@ -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; +}