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