From 9fdd9fb1013044fb085c2116761d3764f3de37c0 Mon Sep 17 00:00:00 2001 From: Thomas Guillem Date: Thu, 7 Jul 2022 15:30:20 +0200 Subject: [PATCH] win32: timer: use threadpoolapiset.h New API: https://docs.microsoft.com/en-us/windows/win32/procthread/thread-pool-api This avoid one allocation (that can fail) in vlc_timer_schedule() (that was handled with a call to abort()). One remark from the documentation: "If the due time specified by pftDueTime is relative, the time that the system spends in sleep or hibernation does not count toward the expiration of the timer. The timer is signaled when the cumulative amount of elapsed time the system spends in the waking state equals the timer's relative due time or period. If the due time specified by pftDueTime is absolute, the time that the system spends in sleep or hibernation does count toward the expiration of the timer. If the timer expires while the system is sleeping, the timer is signaled immediately when the system wakes." For that reason, the vlc_timer API always use a absolute FILETIME in SetThreadpoolTimer(). vlc_timer absolute ticks are substracted with vlc_tick_now() and added to the absolute FILETIME. --- src/win32/timer.c | 87 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 27 deletions(-) diff --git a/src/win32/timer.c b/src/win32/timer.c index 4fe3d2b067c..ee1ce62294d 100644 --- a/src/win32/timer.c +++ b/src/win32/timer.c @@ -29,17 +29,19 @@ struct vlc_timer { - HANDLE handle; + PTP_TIMER t; void (*func) (void *); void *data; }; -static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout) +static VOID CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance, + PVOID context, PTP_TIMER t) { - struct vlc_timer *timer = val; + (void) instance; + (void) t; assert(t); + struct vlc_timer *timer = context; - assert (timeout); - timer->func (timer->data); + timer->func(timer->data); } int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data) @@ -48,48 +50,79 @@ int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data) if (timer == NULL) return ENOMEM; + timer->t = CreateThreadpoolTimer(timer_callback, timer, NULL); + if (timer->t == NULL) + { + free(timer); + return ENOMEM; + } + assert(func); timer->func = func; timer->data = data; - timer->handle = INVALID_HANDLE_VALUE; *id = timer; return 0; } void vlc_timer_destroy (vlc_timer_t timer) { - if (timer->handle != INVALID_HANDLE_VALUE) - DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE); - free (timer); + SetThreadpoolTimer(timer->t, NULL, 0, 0); + WaitForThreadpoolTimerCallbacks(timer->t, TRUE); + CloseThreadpoolTimer(timer->t); + free(timer); } void vlc_timer_schedule (vlc_timer_t timer, bool absolute, vlc_tick_t value, vlc_tick_t interval) { - if (timer->handle != INVALID_HANDLE_VALUE) + if (value == VLC_TIMER_DISARM) { - DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE); - timer->handle = INVALID_HANDLE_VALUE; + SetThreadpoolTimer(timer->t, NULL, 0, 0); + /* Cancel any pending callbacks */ + WaitForThreadpoolTimerCallbacks(timer->t, TRUE); + return; } - if (value == VLC_TIMER_DISARM) - return; /* Disarm */ + /* Always use absolute FILETIME (positive) since "the time that the system + * spends in sleep or hibernation does count toward the expiration of the + * timer. */ + + /* Convert the tick value to a relative tick. */ if (absolute) + value -= vlc_tick_now(); + if (value < 0) + value = 0; + + /* Get the system FILETIME */ + FILETIME time; +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) && (!defined(VLC_WINSTORE_APP) || _WIN32_WINNT >= 0x0A00) + GetSystemTimePreciseAsFileTime(&time); +#else + GetSystemTimeAsFileTime(&time); +#endif + + /* Convert it to ULARGE_INTEGER to allow calculation (addition here) */ + ULARGE_INTEGER time_ul = { + .LowPart = time.dwLowDateTime, + .HighPart = time.dwHighDateTime, + }; + + /* Add the relative tick to the absolute time */ + time_ul.QuadPart += MSFTIME_FROM_VLC_TICK(value); + + /* Convert it back to a FILETIME*/ + time.dwLowDateTime = time_ul.u.LowPart; + time.dwHighDateTime = time_ul.u.HighPart; + + DWORD intervaldw; + if (interval == VLC_TIMER_FIRE_ONCE) + intervaldw = 0; + else { - value -= vlc_tick_now (); - if (value < 0) - value = 0; + uint64_t intervalms = MS_FROM_VLC_TICK(interval); + intervaldw = unlikely(intervalms > UINT32_MAX) ? UINT32_MAX : intervalms; } - DWORD val = MS_FROM_VLC_TICK(value); - DWORD interv = MS_FROM_VLC_TICK(interval); - if (val == 0 && value != 0) - val = 1; /* rounding error */ - if (interv == 0 && interval != 0) - interv = 1; /* rounding error */ - - if (!CreateTimerQueueTimer(&timer->handle, NULL, vlc_timer_do, timer, - val, interv, WT_EXECUTEDEFAULT)) - abort (); + SetThreadpoolTimer(timer->t, &time, intervaldw, 0); } unsigned vlc_timer_getoverrun (vlc_timer_t timer) -- GitLab