FreeRDP
Loading...
Searching...
No Matches
winpr/libwinpr/synch/timer.c
1
21#include <winpr/config.h>
22
23#include <winpr/crt.h>
24#include <winpr/file.h>
25#include <winpr/assert.h>
26#include <winpr/sysinfo.h>
27
28#include <winpr/synch.h>
29
30#ifndef _WIN32
31#include <unistd.h>
32#include <errno.h>
33#include <sys/time.h>
34#include <signal.h>
35#endif
36
37#include "event.h"
38#include "synch.h"
39
40#ifndef _WIN32
41
42#include "../handle/handle.h"
43#include "../thread/thread.h"
44
45#include "../log.h"
46#define TAG WINPR_TAG("synch.timer")
47
48static BOOL TimerCloseHandle(HANDLE handle);
49
50static BOOL TimerIsHandled(HANDLE handle)
51{
52 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_TIMER, FALSE);
53}
54
55static int TimerGetFd(HANDLE handle)
56{
57 WINPR_TIMER* timer = (WINPR_TIMER*)handle;
58
59 if (!TimerIsHandled(handle))
60 return -1;
61
62 return timer->fd;
63}
64
65static DWORD TimerCleanupHandle(HANDLE handle)
66{
67 WINPR_TIMER* timer = (WINPR_TIMER*)handle;
68
69 if (!TimerIsHandled(handle))
70 return WAIT_FAILED;
71
72 if (timer->bManualReset)
73 return WAIT_OBJECT_0;
74
75#ifdef TIMER_IMPL_TIMERFD
76 SSIZE_T length = 0;
77 do
78 {
79 UINT64 expirations = 0;
80 length = read(timer->fd, (void*)&expirations, sizeof(UINT64));
81 } while (length < 0 && errno == EINTR);
82
83 if (length != 8)
84 {
85 if (length < 0)
86 {
87 char ebuffer[256] = WINPR_C_ARRAY_INIT;
88 switch (errno)
89 {
90 case ETIMEDOUT:
91 case EAGAIN:
92 return WAIT_TIMEOUT;
93
94 default:
95 break;
96 }
97
98 WLog_ERR(TAG, "timer read() failure [%d] %s", errno,
99 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
100 }
101 else
102 {
103 WLog_ERR(TAG, "timer read() failure - incorrect number of bytes read");
104 }
105
106 return WAIT_FAILED;
107 }
108#elif defined(TIMER_IMPL_POSIX) || defined(TIMER_IMPL_DISPATCH)
109 if (!winpr_event_reset(&timer->event))
110 {
111 WLog_ERR(TAG, "timer reset() failure");
112 return WAIT_FAILED;
113 }
114#endif
115
116 return WAIT_OBJECT_0;
117}
118
119typedef struct
120{
121 WINPR_APC_ITEM apcItem;
122 WINPR_TIMER* timer;
123} TimerDeleter;
124
125static void TimerPostDelete_APC(LPVOID arg)
126{
127 TimerDeleter* deleter = (TimerDeleter*)arg;
128 WINPR_ASSERT(deleter);
129 /* releases the extra reference TimerCloseHandle() took to keep the struct alive until here
130 * (see APC_REMOVE_DELAY_FREE below) - only actually frees once nothing else still holds it */
131 winpr_Handle_Release((HANDLE)deleter->timer);
132 deleter->apcItem.markedForFree = TRUE;
133 deleter->apcItem.markedForRemove = TRUE;
134}
135
136BOOL TimerCloseHandle(HANDLE handle)
137{
138 WINPR_TIMER* timer = nullptr;
139 timer = (WINPR_TIMER*)handle;
140
141 if (!TimerIsHandled(handle))
142 return FALSE;
143
144#ifdef TIMER_IMPL_TIMERFD
145 if (timer->fd != -1)
146 close(timer->fd);
147#endif
148
149#ifdef TIMER_IMPL_POSIX
150 timer_delete(timer->tid);
151#endif
152
153#ifdef TIMER_IMPL_DISPATCH
154 dispatch_release(timer->queue);
155 dispatch_release(timer->source);
156#endif
157
158#if defined(TIMER_IMPL_POSIX) || defined(TIMER_IMPL_DISPATCH)
159 winpr_event_uninit(&timer->event);
160#endif
161
162 free(timer->name);
163 timer->name = nullptr;
164 if (timer->apcItem.linked)
165 {
166 TimerDeleter* deleter = nullptr;
167 WINPR_APC_ITEM* apcItem = nullptr;
168
169 switch (apc_remove(&timer->apcItem))
170 {
171 case APC_REMOVE_OK:
172 break;
173 case APC_REMOVE_DELAY_FREE:
174 {
175 WINPR_THREAD* thread = winpr_GetCurrentThread();
176 if (!thread)
177 return FALSE;
178
179 deleter = calloc(1, sizeof(*deleter));
180 if (!deleter)
181 {
182 WLog_ERR(TAG, "unable to allocate a timer deleter");
183 return TRUE;
184 }
185
186 /* keeps the struct alive until TimerPostDelete_APC() runs and releases it -
187 * without this, the winpr_Handle_Release() the generic CloseHandle() wrapper
188 * makes right after this function returns would free it immediately, while the
189 * APC list may still be walking it. */
190 winpr_Handle_AddRef(handle);
191 deleter->timer = timer;
192 apcItem = &deleter->apcItem;
193 apcItem->type = APC_TYPE_HANDLE_FREE;
194 apcItem->alwaysSignaled = TRUE;
195 apcItem->completion = TimerPostDelete_APC;
196 apcItem->completionArgs = deleter;
197 apc_register(thread, apcItem);
198 return TRUE;
199 }
200 case APC_REMOVE_ERROR:
201 default:
202 WLog_ERR(TAG, "unable to remove timer from APC list");
203 break;
204 }
205 }
206
207 return TRUE;
208}
209
210#ifdef TIMER_IMPL_POSIX
211
212static void WaitableTimerSignalHandler(int signum, siginfo_t* siginfo, void* arg)
213{
214 WINPR_TIMER* timer = siginfo->si_value.sival_ptr;
215 UINT64 data = 1;
216 WINPR_UNUSED(arg);
217
218 if (!timer || (signum != SIGALRM))
219 return;
220
221 if (!winpr_event_set(&timer->event))
222 WLog_ERR(TAG, "error when notifying event");
223}
224
225static INIT_ONCE TimerSignalHandler_InitOnce = INIT_ONCE_STATIC_INIT;
226
227static BOOL InstallTimerSignalHandler(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
228{
229 struct sigaction action;
230 sigemptyset(&action.sa_mask);
231 sigaddset(&action.sa_mask, SIGALRM);
232 action.sa_flags = SA_RESTART | SA_SIGINFO;
233 action.sa_sigaction = WaitableTimerSignalHandler;
234 sigaction(SIGALRM, &action, nullptr);
235 return TRUE;
236}
237#endif
238
239#ifdef TIMER_IMPL_DISPATCH
240static void WaitableTimerHandler(void* arg)
241{
242 WINPR_TIMER* timer = (WINPR_TIMER*)arg;
243
244 if (!timer)
245 return;
246
247 if (!winpr_event_set(&timer->event))
248 WLog_ERR(TAG, "failed to write to pipe");
249
250 if (timer->lPeriod == 0)
251 {
252 if (timer->running)
253 dispatch_suspend(timer->source);
254
255 timer->running = FALSE;
256 }
257}
258#endif
259
260static int InitializeWaitableTimer(WINPR_TIMER* timer)
261{
262 int result = 0;
263
264#ifdef TIMER_IMPL_TIMERFD
265 timer->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
266 if (timer->fd <= 0)
267 return -1;
268#elif defined(TIMER_IMPL_POSIX)
269 struct sigevent sigev = WINPR_C_ARRAY_INIT;
270 if (!InitOnceExecuteOnce(&TimerSignalHandler_InitOnce, InstallTimerSignalHandler, nullptr,
271 nullptr))
272 return -1;
273 sigev.sigev_notify = SIGEV_SIGNAL;
274 sigev.sigev_signo = SIGALRM;
275 sigev.sigev_value.sival_ptr = (void*)timer;
276
277 if ((timer_create(CLOCK_MONOTONIC, &sigev, &(timer->tid))) != 0)
278 {
279 WLog_ERR(TAG, "timer_create");
280 return -1;
281 }
282#elif !defined(TIMER_IMPL_DISPATCH)
283 WLog_ERR(TAG, "os specific implementation is missing");
284 result = -1;
285#endif
286
287 timer->bInit = TRUE;
288 return result;
289}
290
291static BOOL timer_drain_fd(int fd)
292{
293 UINT64 expr = 0;
294 SSIZE_T ret = 0;
295
296 do
297 {
298 ret = read(fd, &expr, sizeof(expr));
299 } while (ret < 0 && errno == EINTR);
300
301 return ret >= 0;
302}
303
304static HANDLE_OPS ops = { TimerIsHandled, TimerCloseHandle, TimerGetFd, TimerCleanupHandle,
305 nullptr, nullptr, nullptr, nullptr,
306 nullptr, nullptr, nullptr, nullptr,
307 nullptr, nullptr, nullptr, nullptr,
308 nullptr, nullptr, nullptr, nullptr,
309 nullptr };
310
315HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset,
316 LPCSTR lpTimerName)
317{
318 HANDLE handle = nullptr;
319 WINPR_TIMER* timer = nullptr;
320
321 if (lpTimerAttributes)
322 WLog_WARN(TAG, "[%s] does not support lpTimerAttributes", lpTimerName);
323
324 timer = (WINPR_TIMER*)calloc(1, sizeof(WINPR_TIMER));
325
326 if (timer)
327 {
328 WINPR_HANDLE_SET_TYPE_AND_MODE(timer, HANDLE_TYPE_TIMER, WINPR_FD_READ);
329 handle = (HANDLE)timer;
330 timer->fd = -1;
331 timer->lPeriod = 0;
332 timer->bManualReset = bManualReset;
333 timer->pfnCompletionRoutine = nullptr;
334 timer->lpArgToCompletionRoutine = nullptr;
335 timer->bInit = FALSE;
336
337 if (lpTimerName)
338 timer->name = strdup(lpTimerName);
339
340 timer->common.ops = &ops;
341#if defined(TIMER_IMPL_DISPATCH) || defined(TIMER_IMPL_POSIX)
342 if (!winpr_event_init(&timer->event))
343 goto fail;
344 timer->fd = timer->event.fds[0];
345#endif
346
347#if defined(TIMER_IMPL_DISPATCH)
348 timer->queue = dispatch_queue_create(TAG, DISPATCH_QUEUE_SERIAL);
349
350 if (!timer->queue)
351 goto fail;
352
353 timer->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timer->queue);
354
355 if (!timer->source)
356 goto fail;
357
358 dispatch_set_context(timer->source, timer);
359 dispatch_source_set_event_handler_f(timer->source, WaitableTimerHandler);
360#endif
361 }
362
363 return handle;
364
365#if defined(TIMER_IMPL_DISPATCH) || defined(TIMER_IMPL_POSIX)
366fail:
367 TimerCloseHandle(handle);
368 free(timer);
369 return nullptr;
370#endif
371}
372
373HANDLE CreateWaitableTimerW(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset,
374 LPCWSTR lpTimerName)
375{
376 HANDLE handle = nullptr;
377 LPSTR name = nullptr;
378
379 if (lpTimerName)
380 {
381 name = ConvertWCharToUtf8Alloc(lpTimerName, nullptr);
382 if (!name)
383 return nullptr;
384 }
385
386 handle = CreateWaitableTimerA(lpTimerAttributes, bManualReset, name);
387 free(name);
388 return handle;
389}
390
391HANDLE CreateWaitableTimerExA(LPSECURITY_ATTRIBUTES lpTimerAttributes, LPCSTR lpTimerName,
392 DWORD dwFlags, DWORD dwDesiredAccess)
393{
394 BOOL bManualReset = (dwFlags & CREATE_WAITABLE_TIMER_MANUAL_RESET) != 0;
395
396 if (dwDesiredAccess != 0)
397 WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpTimerName,
398 dwDesiredAccess);
399
400 return CreateWaitableTimerA(lpTimerAttributes, bManualReset, lpTimerName);
401}
402
403HANDLE CreateWaitableTimerExW(LPSECURITY_ATTRIBUTES lpTimerAttributes, LPCWSTR lpTimerName,
404 DWORD dwFlags, DWORD dwDesiredAccess)
405{
406 HANDLE handle = nullptr;
407 LPSTR name = nullptr;
408
409 if (lpTimerName)
410 {
411 name = ConvertWCharToUtf8Alloc(lpTimerName, nullptr);
412 if (!name)
413 return nullptr;
414 }
415
416 handle = CreateWaitableTimerExA(lpTimerAttributes, name, dwFlags, dwDesiredAccess);
417 free(name);
418 return handle;
419}
420
421static void timerAPC(LPVOID arg)
422{
423 WINPR_TIMER* timer = (WINPR_TIMER*)arg;
424 WINPR_ASSERT(timer);
425 if (!timer->lPeriod)
426 {
427 /* this is a one time shot timer with a completion, let's remove us from
428 the APC list */
429 switch (apc_remove(&timer->apcItem))
430 {
431 case APC_REMOVE_OK:
432 case APC_REMOVE_DELAY_FREE:
433 break;
434 case APC_REMOVE_ERROR:
435 default:
436 WLog_ERR(TAG, "error removing the APC routine");
437 }
438 }
439
440 if (timer->pfnCompletionRoutine)
441 timer->pfnCompletionRoutine(timer->lpArgToCompletionRoutine, 0, 0);
442
443#ifdef TIMER_IMPL_TIMERFD
444 while (timer_drain_fd(timer->fd))
445 ;
446#elif defined(TIMER_IMPL_POSIX) || defined(TIMER_IMPL_DISPATCH)
447 winpr_event_reset(&timer->event);
448#endif
449}
450
451BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPeriod,
452 PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine,
453 BOOL fResume)
454{
455 ULONG Type = 0;
456 WINPR_HANDLE* Object = nullptr;
457 WINPR_TIMER* timer = nullptr;
458 LONGLONG seconds = 0;
459 LONGLONG nanoseconds = 0;
460 int status = 0;
461
462 if (!winpr_Handle_GetInfo(hTimer, &Type, &Object))
463 return FALSE;
464
465 if (Type != HANDLE_TYPE_TIMER)
466 return FALSE;
467
468 if (!lpDueTime)
469 return FALSE;
470
471 if (lPeriod < 0)
472 return FALSE;
473
474 if (fResume)
475 {
476 WLog_ERR(TAG, "does not support fResume");
477 return FALSE;
478 }
479
480 timer = (WINPR_TIMER*)Object;
481 timer->lPeriod = lPeriod; /* milliseconds */
482 timer->pfnCompletionRoutine = pfnCompletionRoutine;
483 timer->lpArgToCompletionRoutine = lpArgToCompletionRoutine;
484
485 if (!timer->bInit)
486 {
487 if (InitializeWaitableTimer(timer) < 0)
488 return FALSE;
489 }
490
491#if defined(TIMER_IMPL_TIMERFD) || defined(TIMER_IMPL_POSIX)
492 ZeroMemory(&(timer->timeout), sizeof(struct itimerspec));
493
494 if (lpDueTime->QuadPart < 0)
495 {
496 LONGLONG due = lpDueTime->QuadPart * (-1);
497 /* due time is in 100 nanosecond intervals */
498 seconds = (due / 10000000);
499 nanoseconds = ((due % 10000000) * 100);
500 }
501 else if (lpDueTime->QuadPart == 0)
502 {
503 seconds = nanoseconds = 0;
504 }
505 else
506 {
507 WLog_ERR(TAG, "absolute time not implemented");
508 return FALSE;
509 }
510
511 if (lPeriod > 0)
512 {
513 timer->timeout.it_interval.tv_sec = (lPeriod / 1000LL); /* seconds */
514 timer->timeout.it_interval.tv_nsec = (1000000LL * (lPeriod % 1000LL)); /* nanoseconds */
515 }
516
517 if (lpDueTime->QuadPart != 0)
518 {
519 timer->timeout.it_value.tv_sec = seconds; /* seconds */
520 timer->timeout.it_value.tv_nsec = nanoseconds; /* nanoseconds */
521 }
522 else
523 {
524 timer->timeout.it_value.tv_sec = timer->timeout.it_interval.tv_sec; /* seconds */
525 timer->timeout.it_value.tv_nsec = timer->timeout.it_interval.tv_nsec; /* nanoseconds */
526 }
527
528#ifdef TIMER_IMPL_TIMERFD
529 status = timerfd_settime(timer->fd, 0, &(timer->timeout), nullptr);
530 if (status)
531 {
532 WLog_ERR(TAG, "timerfd_settime failure: %d", status);
533 return FALSE;
534 }
535#else
536 status = timer_settime(timer->tid, 0, &(timer->timeout), nullptr);
537 if (status != 0)
538 {
539 WLog_ERR(TAG, "timer_settime failure");
540 return FALSE;
541 }
542#endif
543#endif
544
545#ifdef TIMER_IMPL_DISPATCH
546 if (lpDueTime->QuadPart < 0)
547 {
548 LONGLONG due = lpDueTime->QuadPart * (-1);
549 /* due time is in 100 nanosecond intervals */
550 seconds = (due / 10000000);
551 nanoseconds = due * 100;
552 }
553 else if (lpDueTime->QuadPart == 0)
554 {
555 seconds = nanoseconds = 0;
556 }
557 else
558 {
559 WLog_ERR(TAG, "absolute time not implemented");
560 return FALSE;
561 }
562
563 if (!winpr_event_reset(&timer->event))
564 {
565 WLog_ERR(TAG, "error when resetting timer event");
566 }
567
568 {
569 if (timer->running)
570 dispatch_suspend(timer->source);
571
572 dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, nanoseconds);
573 uint64_t interval = DISPATCH_TIME_FOREVER;
574
575 if (lPeriod > 0)
576 interval = lPeriod * 1000000;
577
578 dispatch_source_set_timer(timer->source, start, interval, 0);
579 dispatch_resume(timer->source);
580 timer->running = TRUE;
581 }
582#endif
583
584 if (pfnCompletionRoutine)
585 {
586 WINPR_APC_ITEM* apcItem = &timer->apcItem;
587
588 /* install our APC routine that will call the completion */
589 apcItem->type = APC_TYPE_TIMER;
590 apcItem->alwaysSignaled = FALSE;
591 apcItem->pollFd = timer->fd;
592 apcItem->pollMode = WINPR_FD_READ;
593 apcItem->completion = timerAPC;
594 apcItem->completionArgs = timer;
595
596 if (!apcItem->linked)
597 {
598 WINPR_THREAD* thread = winpr_GetCurrentThread();
599 if (!thread)
600 return FALSE;
601
602 apc_register(thread, apcItem);
603 }
604 }
605 else
606 {
607 if (timer->apcItem.linked)
608 {
609 apc_remove(&timer->apcItem);
610 }
611 }
612 return TRUE;
613}
614
615BOOL SetWaitableTimerEx(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPeriod,
616 PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine,
617 WINPR_ATTR_UNUSED PREASON_CONTEXT WakeContext,
618 WINPR_ATTR_UNUSED ULONG TolerableDelay)
619{
620 return SetWaitableTimer(hTimer, lpDueTime, lPeriod, pfnCompletionRoutine,
621 lpArgToCompletionRoutine, FALSE);
622}
623
624HANDLE OpenWaitableTimerA(WINPR_ATTR_UNUSED DWORD dwDesiredAccess,
625 WINPR_ATTR_UNUSED BOOL bInheritHandle,
626 WINPR_ATTR_UNUSED LPCSTR lpTimerName)
627{
628 /* TODO: Implement */
629 WLog_ERR(TAG, "not implemented");
630 return nullptr;
631}
632
633HANDLE OpenWaitableTimerW(WINPR_ATTR_UNUSED DWORD dwDesiredAccess,
634 WINPR_ATTR_UNUSED BOOL bInheritHandle,
635 WINPR_ATTR_UNUSED LPCWSTR lpTimerName)
636{
637 /* TODO: Implement */
638 WLog_ERR(TAG, "not implemented");
639 return nullptr;
640}
641
642BOOL CancelWaitableTimer(HANDLE hTimer)
643{
644 ULONG Type = 0;
645 WINPR_HANDLE* Object = nullptr;
646
647 if (!winpr_Handle_GetInfo(hTimer, &Type, &Object))
648 return FALSE;
649
650 if (Type != HANDLE_TYPE_TIMER)
651 return FALSE;
652
653#if defined(__APPLE__)
654 {
655 WINPR_TIMER* timer = (WINPR_TIMER*)Object;
656 if (timer->running)
657 dispatch_suspend(timer->source);
658
659 timer->running = FALSE;
660 }
661#endif
662 return TRUE;
663}
664
665/*
666 * Returns inner file descriptor for usage with select()
667 * This file descriptor is not usable on Windows
668 */
669
670int GetTimerFileDescriptor(HANDLE hTimer)
671{
672 WINPR_HANDLE* hdl = nullptr;
673 ULONG type = 0;
674
675 if (!winpr_Handle_GetInfo(hTimer, &type, &hdl) || type != HANDLE_TYPE_TIMER)
676 {
677 WLog_ERR(TAG, "GetTimerFileDescriptor: hTimer is not an timer");
678 SetLastError(ERROR_INVALID_PARAMETER);
679 return -1;
680 }
681
682 return winpr_Handle_getFd(hTimer);
683}
684
694static void timespec_add_ms(struct timespec* tspec, UINT32 ms)
695{
696 INT64 ns = 0;
697 WINPR_ASSERT(tspec);
698 ns = tspec->tv_nsec + (ms * 1000000LL);
699 tspec->tv_sec += (ns / 1000000000LL);
700 tspec->tv_nsec = (ns % 1000000000LL);
701}
702
703static void timespec_gettimeofday(struct timespec* tspec)
704{
705 WINPR_ASSERT(tspec);
706
707 const UINT64 ns = winpr_GetUnixTimeNS();
708 tspec->tv_sec = WINPR_TIME_NS_TO_S(ns);
709 tspec->tv_nsec = WINPR_TIME_NS_REM_NS(ns);
710}
711
712static INT64 timespec_compare(const struct timespec* tspec1, const struct timespec* tspec2)
713{
714 WINPR_ASSERT(tspec1);
715 WINPR_ASSERT(tspec2);
716 if (tspec1->tv_sec == tspec2->tv_sec)
717 return (tspec1->tv_nsec - tspec2->tv_nsec);
718 else
719 return (tspec1->tv_sec - tspec2->tv_sec);
720}
721
722static void timespec_copy(struct timespec* dst, struct timespec* src)
723{
724 WINPR_ASSERT(dst);
725 WINPR_ASSERT(src);
726 dst->tv_sec = src->tv_sec;
727 dst->tv_nsec = src->tv_nsec;
728}
729
730static void InsertTimerQueueTimer(WINPR_TIMER_QUEUE_TIMER** pHead, WINPR_TIMER_QUEUE_TIMER* timer)
731{
732 WINPR_TIMER_QUEUE_TIMER* node = nullptr;
733
734 WINPR_ASSERT(pHead);
735 WINPR_ASSERT(timer);
736
737 if (!(*pHead))
738 {
739 *pHead = timer;
740 timer->next = nullptr;
741 return;
742 }
743
744 node = *pHead;
745
746 while (node->next)
747 {
748 if (timespec_compare(&(timer->ExpirationTime), &(node->ExpirationTime)) > 0)
749 {
750 if (timespec_compare(&(timer->ExpirationTime), &(node->next->ExpirationTime)) < 0)
751 break;
752 }
753
754 node = node->next;
755 }
756
757 if (node->next)
758 {
759 timer->next = node->next->next;
760 node->next = timer;
761 }
762 else
763 {
764 node->next = timer;
765 timer->next = nullptr;
766 }
767}
768
769static void RemoveTimerQueueTimer(WINPR_TIMER_QUEUE_TIMER** pHead, WINPR_TIMER_QUEUE_TIMER* timer)
770{
771 BOOL found = FALSE;
772 WINPR_TIMER_QUEUE_TIMER* node = nullptr;
773 WINPR_TIMER_QUEUE_TIMER* prevNode = nullptr;
774
775 WINPR_ASSERT(pHead);
776 WINPR_ASSERT(timer);
777 if (timer == *pHead)
778 {
779 *pHead = timer->next;
780 timer->next = nullptr;
781 return;
782 }
783
784 node = *pHead;
785 prevNode = nullptr;
786
787 while (node)
788 {
789 if (node == timer)
790 {
791 found = TRUE;
792 break;
793 }
794
795 prevNode = node;
796 node = node->next;
797 }
798
799 if (found)
800 {
801 if (prevNode)
802 {
803 prevNode->next = timer->next;
804 }
805
806 timer->next = nullptr;
807 }
808}
809
810static int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue)
811{
812 struct timespec CurrentTime;
813 WINPR_TIMER_QUEUE_TIMER* node = nullptr;
814
815 WINPR_ASSERT(timerQueue);
816
817 if (!timerQueue->activeHead)
818 return 0;
819
820 timespec_gettimeofday(&CurrentTime);
821 node = timerQueue->activeHead;
822
823 while (node)
824 {
825 if (timespec_compare(&CurrentTime, &(node->ExpirationTime)) >= 0)
826 {
827 node->Callback(node->Parameter, TRUE);
828 node->FireCount++;
829 timerQueue->activeHead = node->next;
830 node->next = nullptr;
831
832 if (node->Period)
833 {
834 timespec_add_ms(&(node->ExpirationTime), node->Period);
835 InsertTimerQueueTimer(&(timerQueue->activeHead), node);
836 }
837 else
838 {
839 InsertTimerQueueTimer(&(timerQueue->inactiveHead), node);
840 }
841
842 node = timerQueue->activeHead;
843 }
844 else
845 {
846 break;
847 }
848 }
849
850 return 0;
851}
852
853static void* TimerQueueThread(void* arg)
854{
855 int status = 0;
856 struct timespec timeout;
857 WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*)arg;
858
859 WINPR_ASSERT(timerQueue);
860 while (1)
861 {
862 pthread_mutex_lock(&(timerQueue->cond_mutex));
863 timespec_gettimeofday(&timeout);
864
865 if (!timerQueue->activeHead)
866 {
867 timespec_add_ms(&timeout, 50);
868 }
869 else
870 {
871 if (timespec_compare(&timeout, &(timerQueue->activeHead->ExpirationTime)) < 0)
872 {
873 timespec_copy(&timeout, &(timerQueue->activeHead->ExpirationTime));
874 }
875 }
876
877 status = pthread_cond_timedwait(&(timerQueue->cond), &(timerQueue->cond_mutex), &timeout);
878 FireExpiredTimerQueueTimers(timerQueue);
879 const BOOL bCancelled = timerQueue->bCancelled;
880 pthread_mutex_unlock(&(timerQueue->cond_mutex));
881
882 if ((status != ETIMEDOUT) && (status != 0))
883 break;
884
885 if (bCancelled)
886 break;
887 }
888
889 return nullptr;
890}
891
892static int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue)
893{
894 WINPR_ASSERT(timerQueue);
895 pthread_cond_init(&(timerQueue->cond), nullptr);
896 pthread_mutex_init(&(timerQueue->cond_mutex), nullptr);
897 pthread_mutex_init(&(timerQueue->mutex), nullptr);
898 pthread_attr_init(&(timerQueue->attr));
899 timerQueue->param.sched_priority = sched_get_priority_max(SCHED_FIFO);
900 pthread_attr_setschedparam(&(timerQueue->attr), &(timerQueue->param));
901 pthread_attr_setschedpolicy(&(timerQueue->attr), SCHED_FIFO);
902 pthread_create(&(timerQueue->thread), &(timerQueue->attr), TimerQueueThread, timerQueue);
903 return 0;
904}
905
906HANDLE CreateTimerQueue(void)
907{
908 HANDLE handle = nullptr;
909 WINPR_TIMER_QUEUE* timerQueue = nullptr;
910 timerQueue = (WINPR_TIMER_QUEUE*)calloc(1, sizeof(WINPR_TIMER_QUEUE));
911
912 if (timerQueue)
913 {
914 WINPR_HANDLE_SET_TYPE_AND_MODE(timerQueue, HANDLE_TYPE_TIMER_QUEUE, WINPR_FD_READ);
915 handle = (HANDLE)timerQueue;
916 timerQueue->activeHead = nullptr;
917 timerQueue->inactiveHead = nullptr;
918 timerQueue->bCancelled = FALSE;
919 StartTimerQueueThread(timerQueue);
920 }
921
922 return handle;
923}
924
925BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent)
926{
927 void* rvalue = nullptr;
928 WINPR_TIMER_QUEUE* timerQueue = nullptr;
929 WINPR_TIMER_QUEUE_TIMER* node = nullptr;
930 WINPR_TIMER_QUEUE_TIMER* nextNode = nullptr;
931
932 if (!TimerQueue)
933 return FALSE;
934
935 timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue;
936 /* Cancel and delete timer queue timers */
937 pthread_mutex_lock(&(timerQueue->cond_mutex));
938 timerQueue->bCancelled = TRUE;
939 pthread_cond_signal(&(timerQueue->cond));
940 pthread_mutex_unlock(&(timerQueue->cond_mutex));
941 pthread_join(timerQueue->thread, &rvalue);
952 {
953 /* Move all active timers to the inactive timer list */
954 node = timerQueue->activeHead;
955
956 while (node)
957 {
958 InsertTimerQueueTimer(&(timerQueue->inactiveHead), node);
959 node = node->next;
960 }
961
962 timerQueue->activeHead = nullptr;
963 /* Once all timers are inactive, free them */
964 node = timerQueue->inactiveHead;
965
966 while (node)
967 {
968 nextNode = node->next;
969 free(node);
970 node = nextNode;
971 }
972
973 timerQueue->inactiveHead = nullptr;
974 }
975 /* Delete timer queue */
976 pthread_cond_destroy(&(timerQueue->cond));
977 pthread_mutex_destroy(&(timerQueue->cond_mutex));
978 pthread_mutex_destroy(&(timerQueue->mutex));
979 pthread_attr_destroy(&(timerQueue->attr));
980 free(timerQueue);
981
982 if (CompletionEvent && (CompletionEvent != INVALID_HANDLE_VALUE))
983 (void)SetEvent(CompletionEvent);
984
985 return TRUE;
986}
987
988BOOL DeleteTimerQueue(HANDLE TimerQueue)
989{
990 return DeleteTimerQueueEx(TimerQueue, nullptr);
991}
992
993BOOL CreateTimerQueueTimer(HANDLE* phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback,
994 void* Parameter, DWORD DueTime, DWORD Period, ULONG Flags)
995{
996 struct timespec CurrentTime = WINPR_C_ARRAY_INIT;
997
998 if (!TimerQueue)
999 return FALSE;
1000
1001 timespec_gettimeofday(&CurrentTime);
1002 WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue;
1003 WINPR_TIMER_QUEUE_TIMER* timer = calloc(1, sizeof(WINPR_TIMER_QUEUE_TIMER));
1004
1005 if (!timer)
1006 return FALSE;
1007
1008 WINPR_HANDLE_SET_TYPE_AND_MODE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER, WINPR_FD_READ);
1009 *phNewTimer = (HANDLE)timer;
1010 timespec_copy(&(timer->StartTime), &CurrentTime);
1011 timespec_add_ms(&(timer->StartTime), DueTime);
1012 timespec_copy(&(timer->ExpirationTime), &(timer->StartTime));
1013 timer->Flags = Flags;
1014 timer->DueTime = DueTime;
1015 timer->Period = Period;
1016 timer->Callback = Callback;
1017 timer->Parameter = Parameter;
1018 timer->timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue;
1019 timer->FireCount = 0;
1020 timer->next = nullptr;
1021 pthread_mutex_lock(&(timerQueue->cond_mutex));
1022 InsertTimerQueueTimer(&(timerQueue->activeHead), timer);
1023 pthread_cond_signal(&(timerQueue->cond));
1024 pthread_mutex_unlock(&(timerQueue->cond_mutex));
1025 return TRUE;
1026}
1027
1028BOOL ChangeTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, ULONG DueTime, ULONG Period)
1029{
1030 struct timespec CurrentTime;
1031 WINPR_TIMER_QUEUE* timerQueue = nullptr;
1032 WINPR_TIMER_QUEUE_TIMER* timer = nullptr;
1033
1034 if (!TimerQueue || !Timer)
1035 return FALSE;
1036
1037 timespec_gettimeofday(&CurrentTime);
1038 timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue;
1039 timer = (WINPR_TIMER_QUEUE_TIMER*)Timer;
1040 pthread_mutex_lock(&(timerQueue->cond_mutex));
1041 RemoveTimerQueueTimer(&(timerQueue->activeHead), timer);
1042 RemoveTimerQueueTimer(&(timerQueue->inactiveHead), timer);
1043 timer->DueTime = DueTime;
1044 timer->Period = Period;
1045 timer->next = nullptr;
1046 timespec_copy(&(timer->StartTime), &CurrentTime);
1047 timespec_add_ms(&(timer->StartTime), DueTime);
1048 timespec_copy(&(timer->ExpirationTime), &(timer->StartTime));
1049 InsertTimerQueueTimer(&(timerQueue->activeHead), timer);
1050 pthread_cond_signal(&(timerQueue->cond));
1051 pthread_mutex_unlock(&(timerQueue->cond_mutex));
1052 return TRUE;
1053}
1054
1055BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent)
1056{
1057 WINPR_TIMER_QUEUE* timerQueue = nullptr;
1058 WINPR_TIMER_QUEUE_TIMER* timer = nullptr;
1059
1060 if (!TimerQueue || !Timer)
1061 return FALSE;
1062
1063 timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue;
1064 timer = (WINPR_TIMER_QUEUE_TIMER*)Timer;
1065 pthread_mutex_lock(&(timerQueue->cond_mutex));
1076 RemoveTimerQueueTimer(&(timerQueue->activeHead), timer);
1077 pthread_cond_signal(&(timerQueue->cond));
1078 pthread_mutex_unlock(&(timerQueue->cond_mutex));
1079 free(timer);
1080
1081 if (CompletionEvent && (CompletionEvent != INVALID_HANDLE_VALUE))
1082 (void)SetEvent(CompletionEvent);
1083
1084 return TRUE;
1085}
1086
1087#endif