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