21#include <winpr/thread.h>
22#include <winpr/collections.h>
24#include <freerdp/timer.h>
33 uint64_t nextRunTimeNS;
34 FreeRDP_TimerCallback cb;
40struct ALIGN64 freerdp_timer_s
51FreeRDP_TimerID freerdp_timer_add(rdpContext* context, uint64_t intervalNS,
52 FreeRDP_TimerCallback callback,
void* userdata,
bool mainloop)
54 WINPR_ASSERT(context);
55 WINPR_ASSERT(context->rdp);
57 FreeRDPTimer* timer = context->rdp->timer;
60 if ((intervalNS == 0) || !callback)
63 const uint64_t cur = winpr_GetTickCount64NS();
64 const timer_entry_t entry = { .id = ++timer->maxIdx,
65 .intervallNS = intervalNS,
66 .nextRunTimeNS = cur + intervalNS,
70 .mainloop = mainloop };
72 if (!ArrayList_Append(timer->entries, &entry))
74 (void)SetEvent(timer->event);
78static BOOL foreach_entry(
void* data, WINPR_ATTR_UNUSED
size_t index, va_list ap)
80 timer_entry_t* entry = data;
83 FreeRDP_TimerID
id = va_arg(ap, FreeRDP_TimerID);
90 entry->intervallNS = 0;
96bool freerdp_timer_remove(rdpContext* context, FreeRDP_TimerID
id)
98 WINPR_ASSERT(context);
99 WINPR_ASSERT(context->rdp);
101 FreeRDPTimer* timer = context->rdp->timer;
104 return !ArrayList_ForEach(timer->entries, foreach_entry,
id);
107static BOOL runTimerEvent(timer_entry_t* entry, uint64_t* now)
112 entry->cb(entry->context, entry->userdata, entry->id, *now, entry->intervallNS);
113 *now = winpr_GetTickCount64NS();
114 entry->nextRunTimeNS = *now + entry->intervallNS;
118static BOOL runExpiredTimer(
void* data, WINPR_ATTR_UNUSED
size_t index,
119 WINPR_ATTR_UNUSED va_list ap)
121 timer_entry_t* entry = data;
123 WINPR_ASSERT(entry->cb);
126 if (entry->intervallNS == 0)
129 uint64_t* now = va_arg(ap, uint64_t*);
132 bool* mainloop = va_arg(ap,
bool*);
133 WINPR_ASSERT(mainloop);
135 if (entry->nextRunTimeNS > *now)
141 runTimerEvent(entry, now);
146static uint64_t expire_and_reschedule(FreeRDPTimer* timer)
150 bool mainloop =
false;
151 uint64_t next = UINT64_MAX;
152 uint64_t now = winpr_GetTickCount64NS();
154 ArrayList_Lock(timer->entries);
155 ArrayList_ForEach(timer->entries, runExpiredTimer, &now, &mainloop);
157 (void)SetEvent(timer->mainevent);
160 while (pos < ArrayList_Count(timer->entries))
162 timer_entry_t* entry = ArrayList_GetItem(timer->entries, pos);
164 if (entry->intervallNS == 0)
166 ArrayList_RemoveAt(timer->entries, pos);
169 if (next > entry->nextRunTimeNS)
170 next = entry->nextRunTimeNS;
173 ArrayList_Unlock(timer->entries);
175 if (next == UINT64_MAX)
180static DWORD WINAPI timer_thread(LPVOID arg)
182 FreeRDPTimer* timer = arg;
186 DWORD timeout = INFINITE;
187 HANDLE handles[2] = { utils_get_abort_event(timer->rdp), timer->event };
189 while (timer->running &&
190 (WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, timeout) != WAIT_OBJECT_0))
192 (void)ResetEvent(timer->event);
193 const uint64_t next = expire_and_reschedule(timer);
194 const uint64_t now = winpr_GetTickCount64NS();
201 const uint64_t diff = next - now;
202 const uint64_t diffMS = diff / 1000;
204 if (diffMS < INFINITE)
205 timeout = (uint32_t)diffMS;
210void freerdp_timer_free(FreeRDPTimer* timer)
215 timer->running =
false;
217 (void)SetEvent(timer->event);
221 (void)WaitForSingleObject(timer->thread, INFINITE);
222 CloseHandle(timer->thread);
224 if (timer->mainevent)
225 CloseHandle(timer->mainevent);
227 CloseHandle(timer->event);
228 ArrayList_Free(timer->entries);
232static void* entry_new(
const void* val)
234 const timer_entry_t* entry = val;
238 timer_entry_t* copy = calloc(1,
sizeof(timer_entry_t));
245FreeRDPTimer* freerdp_timer_new(rdpRdp* rdp)
248 FreeRDPTimer* timer = calloc(1,
sizeof(FreeRDPTimer));
253 timer->entries = ArrayList_New(TRUE);
256 wObject* obj = ArrayList_Object(timer->entries);
258 obj->fnObjectNew = entry_new;
259 obj->fnObjectFree = free;
261 timer->event = CreateEventA(NULL, TRUE, FALSE, NULL);
265 timer->mainevent = CreateEventA(NULL, TRUE, FALSE, NULL);
266 if (!timer->mainevent)
269 timer->running =
true;
270 timer->thread = CreateThread(NULL, 0, timer_thread, timer, 0, NULL);
276 freerdp_timer_free(timer);
280static BOOL runExpiredTimerOnMainloop(
void* data, WINPR_ATTR_UNUSED
size_t index,
281 WINPR_ATTR_UNUSED va_list ap)
283 timer_entry_t* entry = data;
285 WINPR_ASSERT(entry->cb);
288 if (!entry->mainloop)
292 if (entry->intervallNS == 0)
295 uint64_t* now = va_arg(ap, uint64_t*);
298 if (entry->nextRunTimeNS > *now)
301 runTimerEvent(entry, now);
305bool freerdp_timer_poll(FreeRDPTimer* timer)
309 if (WaitForSingleObject(timer->mainevent, 0) != WAIT_OBJECT_0)
312 ArrayList_Lock(timer->entries);
313 (void)ResetEvent(timer->mainevent);
314 uint64_t now = winpr_GetTickCount64NS();
315 ArrayList_ForEach(timer->entries, runExpiredTimerOnMainloop, &now);
316 (void)SetEvent(timer->event);
317 ArrayList_Unlock(timer->entries);
321HANDLE freerdp_timer_get_event(FreeRDPTimer* timer)
324 return timer->mainevent;
This struct contains function pointer to initialize/free objects.