FreeRDP
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 
48 static BOOL TimerCloseHandle(HANDLE handle);
49 
50 static BOOL TimerIsHandled(HANDLE handle)
51 {
52  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_TIMER, FALSE);
53 }
54 
55 static 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 
65 static 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 
119 typedef struct
120 {
121  WINPR_APC_ITEM apcItem;
122  WINPR_TIMER* timer;
123 } TimerDeleter;
124 
125 static 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 
134 BOOL 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 
205 static 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 
218 static INIT_ONCE TimerSignalHandler_InitOnce = INIT_ONCE_STATIC_INIT;
219 
220 static 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
233 static 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 
253 static 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 
282 static 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 
295 static 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 
321 HANDLE 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)
372 fail:
373  TimerCloseHandle(handle);
374  return NULL;
375 #endif
376 }
377 
378 HANDLE 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 
396 HANDLE 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 
408 HANDLE 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 
426 static 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 
456 BOOL 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 
620 BOOL SetWaitableTimerEx(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPeriod,
621  PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine,
622  PREASON_CONTEXT WakeContext, ULONG TolerableDelay)
623 {
624  return SetWaitableTimer(hTimer, lpDueTime, lPeriod, pfnCompletionRoutine,
625  lpArgToCompletionRoutine, FALSE);
626 }
627 
628 HANDLE OpenWaitableTimerA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpTimerName)
629 {
630  /* TODO: Implement */
631  WLog_ERR(TAG, "not implemented");
632  return NULL;
633 }
634 
635 HANDLE OpenWaitableTimerW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpTimerName)
636 {
637  /* TODO: Implement */
638  WLog_ERR(TAG, "not implemented");
639  return NULL;
640 }
641 
642 BOOL CancelWaitableTimer(HANDLE hTimer)
643 {
644  ULONG Type = 0;
645  WINPR_HANDLE* Object = NULL;
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 
670 int GetTimerFileDescriptor(HANDLE hTimer)
671 {
672  WINPR_HANDLE* hdl = NULL;
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 
694 static 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 
703 static 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 
712 static 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 
722 static 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 
730 static void InsertTimerQueueTimer(WINPR_TIMER_QUEUE_TIMER** pHead, WINPR_TIMER_QUEUE_TIMER* timer)
731 {
732  WINPR_TIMER_QUEUE_TIMER* node = NULL;
733 
734  WINPR_ASSERT(pHead);
735  WINPR_ASSERT(timer);
736 
737  if (!(*pHead))
738  {
739  *pHead = timer;
740  timer->next = NULL;
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 = NULL;
766  }
767 }
768 
769 static void RemoveTimerQueueTimer(WINPR_TIMER_QUEUE_TIMER** pHead, WINPR_TIMER_QUEUE_TIMER* timer)
770 {
771  BOOL found = FALSE;
772  WINPR_TIMER_QUEUE_TIMER* node = NULL;
773  WINPR_TIMER_QUEUE_TIMER* prevNode = NULL;
774 
775  WINPR_ASSERT(pHead);
776  WINPR_ASSERT(timer);
777  if (timer == *pHead)
778  {
779  *pHead = timer->next;
780  timer->next = NULL;
781  return;
782  }
783 
784  node = *pHead;
785  prevNode = NULL;
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 = NULL;
807  }
808 }
809 
810 static int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue)
811 {
812  struct timespec CurrentTime;
813  WINPR_TIMER_QUEUE_TIMER* node = NULL;
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 = NULL;
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 
853 static 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 NULL;
890 }
891 
892 static int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue)
893 {
894  WINPR_ASSERT(timerQueue);
895  pthread_cond_init(&(timerQueue->cond), NULL);
896  pthread_mutex_init(&(timerQueue->cond_mutex), NULL);
897  pthread_mutex_init(&(timerQueue->mutex), NULL);
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 
906 HANDLE CreateTimerQueue(void)
907 {
908  HANDLE handle = NULL;
909  WINPR_TIMER_QUEUE* timerQueue = NULL;
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 = NULL;
917  timerQueue->inactiveHead = NULL;
918  timerQueue->bCancelled = FALSE;
919  StartTimerQueueThread(timerQueue);
920  }
921 
922  return handle;
923 }
924 
925 BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent)
926 {
927  void* rvalue = NULL;
928  WINPR_TIMER_QUEUE* timerQueue = NULL;
929  WINPR_TIMER_QUEUE_TIMER* node = NULL;
930  WINPR_TIMER_QUEUE_TIMER* nextNode = NULL;
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 = NULL;
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 = NULL;
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 
988 BOOL DeleteTimerQueue(HANDLE TimerQueue)
989 {
990  return DeleteTimerQueueEx(TimerQueue, NULL);
991 }
992 
993 BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback,
994  PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags)
995 {
996  struct timespec CurrentTime;
997  WINPR_TIMER_QUEUE* timerQueue = NULL;
998  WINPR_TIMER_QUEUE_TIMER* timer = NULL;
999 
1000  if (!TimerQueue)
1001  return FALSE;
1002 
1003  timespec_gettimeofday(&CurrentTime);
1004  timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue;
1005  timer = (WINPR_TIMER_QUEUE_TIMER*)malloc(sizeof(WINPR_TIMER_QUEUE_TIMER));
1006 
1007  if (!timer)
1008  return FALSE;
1009 
1010  WINPR_HANDLE_SET_TYPE_AND_MODE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER, WINPR_FD_READ);
1011  *((UINT_PTR*)phNewTimer) = (UINT_PTR)(HANDLE)timer;
1012  timespec_copy(&(timer->StartTime), &CurrentTime);
1013  timespec_add_ms(&(timer->StartTime), DueTime);
1014  timespec_copy(&(timer->ExpirationTime), &(timer->StartTime));
1015  timer->Flags = Flags;
1016  timer->DueTime = DueTime;
1017  timer->Period = Period;
1018  timer->Callback = Callback;
1019  timer->Parameter = Parameter;
1020  timer->timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue;
1021  timer->FireCount = 0;
1022  timer->next = NULL;
1023  pthread_mutex_lock(&(timerQueue->cond_mutex));
1024  InsertTimerQueueTimer(&(timerQueue->activeHead), timer);
1025  pthread_cond_signal(&(timerQueue->cond));
1026  pthread_mutex_unlock(&(timerQueue->cond_mutex));
1027  return TRUE;
1028 }
1029 
1030 BOOL ChangeTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, ULONG DueTime, ULONG Period)
1031 {
1032  struct timespec CurrentTime;
1033  WINPR_TIMER_QUEUE* timerQueue = NULL;
1034  WINPR_TIMER_QUEUE_TIMER* timer = NULL;
1035 
1036  if (!TimerQueue || !Timer)
1037  return FALSE;
1038 
1039  timespec_gettimeofday(&CurrentTime);
1040  timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue;
1041  timer = (WINPR_TIMER_QUEUE_TIMER*)Timer;
1042  pthread_mutex_lock(&(timerQueue->cond_mutex));
1043  RemoveTimerQueueTimer(&(timerQueue->activeHead), timer);
1044  RemoveTimerQueueTimer(&(timerQueue->inactiveHead), timer);
1045  timer->DueTime = DueTime;
1046  timer->Period = Period;
1047  timer->next = NULL;
1048  timespec_copy(&(timer->StartTime), &CurrentTime);
1049  timespec_add_ms(&(timer->StartTime), DueTime);
1050  timespec_copy(&(timer->ExpirationTime), &(timer->StartTime));
1051  InsertTimerQueueTimer(&(timerQueue->activeHead), timer);
1052  pthread_cond_signal(&(timerQueue->cond));
1053  pthread_mutex_unlock(&(timerQueue->cond_mutex));
1054  return TRUE;
1055 }
1056 
1057 BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent)
1058 {
1059  WINPR_TIMER_QUEUE* timerQueue = NULL;
1060  WINPR_TIMER_QUEUE_TIMER* timer = NULL;
1061 
1062  if (!TimerQueue || !Timer)
1063  return FALSE;
1064 
1065  timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue;
1066  timer = (WINPR_TIMER_QUEUE_TIMER*)Timer;
1067  pthread_mutex_lock(&(timerQueue->cond_mutex));
1078  RemoveTimerQueueTimer(&(timerQueue->activeHead), timer);
1079  pthread_cond_signal(&(timerQueue->cond));
1080  pthread_mutex_unlock(&(timerQueue->cond_mutex));
1081  free(timer);
1082 
1083  if (CompletionEvent && (CompletionEvent != INVALID_HANDLE_VALUE))
1084  (void)SetEvent(CompletionEvent);
1085 
1086  return TRUE;
1087 }
1088 
1089 #endif