21#include <winpr/thread.h>
22#include <winpr/collections.h>
24#include <freerdp/timer.h>
25#include <freerdp/log.h>
30#if !defined(EMSCRIPTEN)
31#define FREERDP_TIMER_SUPPORTED
33#define TAG FREERDP_TAG("timer")
40 uint64_t nextRunTimeNS;
41 FreeRDP_TimerCallback cb;
47struct ALIGN64 freerdp_timer_s
58FreeRDP_TimerID freerdp_timer_add(rdpContext* context, uint64_t intervalNS,
59 FreeRDP_TimerCallback callback,
void* userdata,
bool mainloop)
61 WINPR_ASSERT(context);
62 WINPR_ASSERT(context->rdp);
64#if !defined(FREERDP_TIMER_SUPPORTED)
65 WINPR_UNUSED(context);
66 WINPR_UNUSED(intervalNS);
67 WINPR_UNUSED(callback);
68 WINPR_UNUSED(userdata);
69 WINPR_UNUSED(mainloop);
70 WLog_WARN(TAG,
"Platform does not support freerdp_timer_* API");
73 FreeRDPTimer* timer = context->rdp->timer;
76 if ((intervalNS == 0) || !callback)
79 const uint64_t cur = winpr_GetTickCount64NS();
80 const timer_entry_t entry = { .id = ++timer->maxIdx,
81 .intervallNS = intervalNS,
82 .nextRunTimeNS = cur + intervalNS,
86 .mainloop = mainloop };
88 if (!ArrayList_Append(timer->entries, &entry))
90 (void)SetEvent(timer->event);
95static BOOL foreach_entry(
void* data, WINPR_ATTR_UNUSED
size_t index, va_list ap)
97 timer_entry_t* entry = data;
100 FreeRDP_TimerID
id = va_arg(ap, FreeRDP_TimerID);
107 entry->intervallNS = 0;
113bool freerdp_timer_remove(rdpContext* context, FreeRDP_TimerID
id)
115 WINPR_ASSERT(context);
116 WINPR_ASSERT(context->rdp);
118 FreeRDPTimer* timer = context->rdp->timer;
121 return !ArrayList_ForEach(timer->entries, foreach_entry,
id);
124static BOOL runTimerEvent(timer_entry_t* entry, uint64_t* now)
129 entry->cb(entry->context, entry->userdata, entry->id, *now, entry->intervallNS);
130 *now = winpr_GetTickCount64NS();
131 entry->nextRunTimeNS = *now + entry->intervallNS;
135static BOOL runExpiredTimer(
void* data, WINPR_ATTR_UNUSED
size_t index,
136 WINPR_ATTR_UNUSED va_list ap)
138 timer_entry_t* entry = data;
140 WINPR_ASSERT(entry->cb);
143 if (entry->intervallNS == 0)
146 uint64_t* now = va_arg(ap, uint64_t*);
149 bool* mainloop = va_arg(ap,
bool*);
150 WINPR_ASSERT(mainloop);
152 if (entry->nextRunTimeNS > *now)
158 runTimerEvent(entry, now);
163#if defined(FREERDP_TIMER_SUPPORTED)
164static uint64_t expire_and_reschedule(FreeRDPTimer* timer)
168 bool mainloop =
false;
169 uint64_t next = UINT64_MAX;
170 uint64_t now = winpr_GetTickCount64NS();
172 ArrayList_Lock(timer->entries);
173 ArrayList_ForEach(timer->entries, runExpiredTimer, &now, &mainloop);
175 (void)SetEvent(timer->mainevent);
178 while (pos < ArrayList_Count(timer->entries))
180 timer_entry_t* entry = ArrayList_GetItem(timer->entries, pos);
182 if (entry->intervallNS == 0)
184 ArrayList_RemoveAt(timer->entries, pos);
187 if (next > entry->nextRunTimeNS)
188 next = entry->nextRunTimeNS;
191 ArrayList_Unlock(timer->entries);
196static DWORD WINAPI timer_thread(LPVOID arg)
198 FreeRDPTimer* timer = arg;
202 DWORD timeout = INFINITE;
203 HANDLE handles[2] = { utils_get_abort_event(timer->rdp), timer->event };
205 while (timer->running &&
206 (WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, timeout) != WAIT_OBJECT_0))
208 (void)ResetEvent(timer->event);
209 const uint64_t next = expire_and_reschedule(timer);
210 const uint64_t now = winpr_GetTickCount64NS();
211 if (next == UINT64_MAX)
222 const uint64_t diff = next - now;
223 const uint64_t diffMS = diff / 1000000ull;
225 if (diffMS < INFINITE)
226 timeout = (uint32_t)diffMS;
232void freerdp_timer_free(FreeRDPTimer* timer)
237 timer->running =
false;
239 (void)SetEvent(timer->event);
243 (void)WaitForSingleObject(timer->thread, INFINITE);
244 CloseHandle(timer->thread);
246 if (timer->mainevent)
247 CloseHandle(timer->mainevent);
249 CloseHandle(timer->event);
250 ArrayList_Free(timer->entries);
254static void* entry_new(
const void* val)
256 const timer_entry_t* entry = val;
260 timer_entry_t* copy = calloc(1,
sizeof(timer_entry_t));
267FreeRDPTimer* freerdp_timer_new(rdpRdp* rdp)
270 FreeRDPTimer* timer = calloc(1,
sizeof(FreeRDPTimer));
275 timer->entries = ArrayList_New(TRUE);
278 wObject* obj = ArrayList_Object(timer->entries);
280 obj->fnObjectNew = entry_new;
281 obj->fnObjectFree = free;
283 timer->event = CreateEventA(NULL, TRUE, FALSE, NULL);
287 timer->mainevent = CreateEventA(NULL, TRUE, FALSE, NULL);
288 if (!timer->mainevent)
291#if defined(FREERDP_TIMER_SUPPORTED)
292 timer->running =
true;
293 timer->thread = CreateThread(NULL, 0, timer_thread, timer, 0, NULL);
300 freerdp_timer_free(timer);
304static BOOL runExpiredTimerOnMainloop(
void* data, WINPR_ATTR_UNUSED
size_t index,
305 WINPR_ATTR_UNUSED va_list ap)
307 timer_entry_t* entry = data;
309 WINPR_ASSERT(entry->cb);
312 if (!entry->mainloop)
316 if (entry->intervallNS == 0)
319 uint64_t* now = va_arg(ap, uint64_t*);
322 if (entry->nextRunTimeNS > *now)
325 runTimerEvent(entry, now);
329bool freerdp_timer_poll(FreeRDPTimer* timer)
333 if (WaitForSingleObject(timer->mainevent, 0) != WAIT_OBJECT_0)
336 ArrayList_Lock(timer->entries);
337 (void)ResetEvent(timer->mainevent);
338 uint64_t now = winpr_GetTickCount64NS();
339 ArrayList_ForEach(timer->entries, runExpiredTimerOnMainloop, &now);
340 (void)SetEvent(timer->event);
341 ArrayList_Unlock(timer->entries);
345HANDLE freerdp_timer_get_event(FreeRDPTimer* timer)
348 return timer->mainevent;
This struct contains function pointer to initialize/free objects.