2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * PURPOSE: Timer Queue implementation
5 * FILE: lib/rtl/timerqueue.c
9 /* INCLUDES *****************************************************************/
17 #undef LIST_FOR_EACH_SAFE
18 #include <wine/list.h>
20 /* FUNCTIONS ***************************************************************/
22 extern PRTL_START_POOL_THREAD RtlpStartThreadFunc
;
23 extern PRTL_EXIT_POOL_THREAD RtlpExitThreadFunc
;
24 HANDLE TimerThreadHandle
= NULL
;
27 RtlpInitializeTimerThread(VOID
)
29 return STATUS_NOT_IMPLEMENTED
;
32 static inline PLARGE_INTEGER
get_nt_timeout( PLARGE_INTEGER pTime
, ULONG timeout
)
34 if (timeout
== INFINITE
) return NULL
;
35 pTime
->QuadPart
= (ULONGLONG
)timeout
* -10000;
42 struct timer_queue
*q
;
44 ULONG runcount
; /* number of callbacks pending execution */
45 WAITORTIMERCALLBACKFUNC callback
;
50 BOOL destroy
; /* timer should be deleted; once set, never unset */
51 HANDLE event
; /* removal event */
57 RTL_CRITICAL_SECTION cs
;
58 struct list timers
; /* sorted by expiration time */
59 BOOL quit
; /* queue should be deleted; once set, never unset */
64 #define EXPIRE_NEVER (~(ULONGLONG) 0)
65 #define TIMER_QUEUE_MAGIC 0x516d6954 /* TimQ */
67 static void queue_remove_timer(struct queue_timer
*t
)
69 /* We MUST hold the queue cs while calling this function. This ensures
70 that we cannot queue another callback for this timer. The runcount
71 being zero makes sure we don't have any already queued. */
72 struct timer_queue
*q
= t
->q
;
74 assert(t
->runcount
== 0);
77 list_remove(&t
->entry
);
79 NtSetEvent(t
->event
, NULL
);
80 RtlFreeHeap(RtlGetProcessHeap(), 0, t
);
82 if (q
->quit
&& list_empty(&q
->timers
))
83 NtSetEvent(q
->event
, NULL
);
86 static void timer_cleanup_callback(struct queue_timer
*t
)
88 struct timer_queue
*q
= t
->q
;
89 RtlEnterCriticalSection(&q
->cs
);
91 assert(0 < t
->runcount
);
94 if (t
->destroy
&& t
->runcount
== 0)
95 queue_remove_timer(t
);
97 RtlLeaveCriticalSection(&q
->cs
);
100 static VOID WINAPI
timer_callback_wrapper(LPVOID p
)
102 struct queue_timer
*t
= p
;
103 t
->callback(t
->param
, TRUE
);
104 timer_cleanup_callback(t
);
107 static inline ULONGLONG
queue_current_time(void)
109 LARGE_INTEGER now
, freq
;
110 NtQueryPerformanceCounter(&now
, &freq
);
111 return now
.QuadPart
* 1000 / freq
.QuadPart
;
114 static void queue_add_timer(struct queue_timer
*t
, ULONGLONG time
,
117 /* We MUST hold the queue cs while calling this function. */
118 struct timer_queue
*q
= t
->q
;
119 struct list
*ptr
= &q
->timers
;
121 assert(!q
->quit
|| (t
->destroy
&& time
== EXPIRE_NEVER
));
123 if (time
!= EXPIRE_NEVER
)
124 LIST_FOR_EACH(ptr
, &q
->timers
)
126 struct queue_timer
*cur
= LIST_ENTRY(ptr
, struct queue_timer
, entry
);
127 if (time
< cur
->expire
)
130 list_add_before(ptr
, &t
->entry
);
134 /* If we insert at the head of the list, we need to expire sooner
136 if (set_event
&& &t
->entry
== list_head(&q
->timers
))
137 NtSetEvent(q
->event
, NULL
);
140 static inline void queue_move_timer(struct queue_timer
*t
, ULONGLONG time
,
143 /* We MUST hold the queue cs while calling this function. */
144 list_remove(&t
->entry
);
145 queue_add_timer(t
, time
, set_event
);
148 static void queue_timer_expire(struct timer_queue
*q
)
150 struct queue_timer
*t
= NULL
;
152 RtlEnterCriticalSection(&q
->cs
);
153 if (list_head(&q
->timers
))
156 t
= LIST_ENTRY(list_head(&q
->timers
), struct queue_timer
, entry
);
157 if (!t
->destroy
&& t
->expire
<= ((now
= queue_current_time())))
162 next
= t
->expire
+ t
->period
;
163 /* avoid trigger cascade if overloaded / hibernated */
165 next
= now
+ t
->period
;
169 queue_move_timer(t
, next
, FALSE
);
174 RtlLeaveCriticalSection(&q
->cs
);
178 if (t
->flags
& WT_EXECUTEINTIMERTHREAD
)
179 timer_callback_wrapper(t
);
184 & (WT_EXECUTEINIOTHREAD
| WT_EXECUTEINPERSISTENTTHREAD
185 | WT_EXECUTELONGFUNCTION
| WT_TRANSFER_IMPERSONATION
));
186 NTSTATUS status
= RtlQueueWorkItem(timer_callback_wrapper
, t
, flags
);
187 if (status
!= STATUS_SUCCESS
)
188 timer_cleanup_callback(t
);
193 static ULONG
queue_get_timeout(struct timer_queue
*q
)
195 struct queue_timer
*t
;
196 ULONG timeout
= INFINITE
;
198 RtlEnterCriticalSection(&q
->cs
);
199 if (list_head(&q
->timers
))
201 t
= LIST_ENTRY(list_head(&q
->timers
), struct queue_timer
, entry
);
202 assert(!t
->destroy
|| t
->expire
== EXPIRE_NEVER
);
204 if (t
->expire
!= EXPIRE_NEVER
)
206 ULONGLONG time
= queue_current_time();
207 timeout
= t
->expire
< time
? 0 : (ULONG
)(t
->expire
- time
);
210 RtlLeaveCriticalSection(&q
->cs
);
215 static DWORD WINAPI
timer_queue_thread_proc(LPVOID p
)
217 struct timer_queue
*q
= p
;
220 timeout_ms
= INFINITE
;
223 LARGE_INTEGER timeout
;
227 status
= NtWaitForSingleObject(
228 q
->event
, FALSE
, get_nt_timeout(&timeout
, timeout_ms
));
230 if (status
== STATUS_WAIT_0
)
232 /* There are two possible ways to trigger the event. Either
233 we are quitting and the last timer got removed, or a new
234 timer got put at the head of the list so we need to adjust
236 RtlEnterCriticalSection(&q
->cs
);
237 if (q
->quit
&& list_empty(&q
->timers
))
239 RtlLeaveCriticalSection(&q
->cs
);
241 else if (status
== STATUS_TIMEOUT
)
242 queue_timer_expire(q
);
247 timeout_ms
= queue_get_timeout(q
);
251 RtlDeleteCriticalSection(&q
->cs
);
253 RtlFreeHeap(RtlGetProcessHeap(), 0, q
);
254 RtlpExitThreadFunc(STATUS_SUCCESS
);
258 static void queue_destroy_timer(struct queue_timer
*t
)
260 /* We MUST hold the queue cs while calling this function. */
262 if (t
->runcount
== 0)
263 /* Ensure a timer is promptly removed. If callbacks are pending,
264 it will be removed after the last one finishes by the callback
266 queue_remove_timer(t
);
268 /* Make sure no destroyed timer masks an active timer at the head
269 of the sorted list. */
270 queue_move_timer(t
, EXPIRE_NEVER
, FALSE
);
273 /***********************************************************************
274 * RtlCreateTimerQueue (NTDLL.@)
276 * Creates a timer queue object and returns a handle to it.
279 * NewTimerQueue [O] The newly created queue.
282 * Success: STATUS_SUCCESS.
283 * Failure: Any NTSTATUS code.
285 NTSTATUS WINAPI
RtlCreateTimerQueue(PHANDLE NewTimerQueue
)
288 struct timer_queue
*q
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof *q
);
290 return STATUS_NO_MEMORY
;
292 RtlInitializeCriticalSection(&q
->cs
);
293 list_init(&q
->timers
);
295 q
->magic
= TIMER_QUEUE_MAGIC
;
296 status
= NtCreateEvent(&q
->event
, EVENT_ALL_ACCESS
, NULL
, SynchronizationEvent
, FALSE
);
297 if (status
!= STATUS_SUCCESS
)
299 RtlFreeHeap(RtlGetProcessHeap(), 0, q
);
302 status
= RtlpStartThreadFunc(timer_queue_thread_proc
, q
, &q
->thread
);
303 if (status
!= STATUS_SUCCESS
)
306 RtlFreeHeap(RtlGetProcessHeap(), 0, q
);
310 NtResumeThread(q
->thread
, NULL
);
312 return STATUS_SUCCESS
;
315 /***********************************************************************
316 * RtlDeleteTimerQueueEx (NTDLL.@)
318 * Deletes a timer queue object.
321 * TimerQueue [I] The timer queue to destroy.
322 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
323 * wait until all timers are finished firing before
324 * returning. Otherwise, return immediately and set the
325 * event when all timers are done.
328 * Success: STATUS_SUCCESS if synchronous, STATUS_PENDING if not.
329 * Failure: Any NTSTATUS code.
331 NTSTATUS WINAPI
RtlDeleteTimerQueueEx(HANDLE TimerQueue
, HANDLE CompletionEvent
)
333 struct timer_queue
*q
= TimerQueue
;
334 struct queue_timer
*t
, *temp
;
338 if (!q
|| q
->magic
!= TIMER_QUEUE_MAGIC
)
339 return STATUS_INVALID_HANDLE
;
343 RtlEnterCriticalSection(&q
->cs
);
345 if (list_head(&q
->timers
))
346 /* When the last timer is removed, it will signal the timer thread to
348 LIST_FOR_EACH_ENTRY_SAFE(t
, temp
, &q
->timers
, struct queue_timer
, entry
)
349 queue_destroy_timer(t
);
351 /* However if we have none, we must do it ourselves. */
352 NtSetEvent(q
->event
, NULL
);
353 RtlLeaveCriticalSection(&q
->cs
);
355 if (CompletionEvent
== INVALID_HANDLE_VALUE
)
357 NtWaitForSingleObject(thread
, FALSE
, NULL
);
358 status
= STATUS_SUCCESS
;
364 DPRINT1("asynchronous return on completion event unimplemented\n");
365 NtWaitForSingleObject(thread
, FALSE
, NULL
);
366 NtSetEvent(CompletionEvent
, NULL
);
368 status
= STATUS_PENDING
;
375 static struct timer_queue
*get_timer_queue(HANDLE TimerQueue
)
377 static struct timer_queue
*default_timer_queue
;
383 if (!default_timer_queue
)
386 NTSTATUS status
= RtlCreateTimerQueue(&q
);
387 if (status
== STATUS_SUCCESS
)
389 PVOID p
= InterlockedCompareExchangePointer(
390 (void **) &default_timer_queue
, q
, NULL
);
392 /* Got beat to the punch. */
393 RtlDeleteTimerQueueEx(q
, NULL
);
396 return default_timer_queue
;
400 /***********************************************************************
401 * RtlCreateTimer (NTDLL.@)
403 * Creates a new timer associated with the given queue.
406 * NewTimer [O] The newly created timer.
407 * TimerQueue [I] The queue to hold the timer.
408 * Callback [I] The callback to fire.
409 * Parameter [I] The argument for the callback.
410 * DueTime [I] The delay, in milliseconds, before first firing the
412 * Period [I] The period, in milliseconds, at which to fire the timer
413 * after the first callback. If zero, the timer will only
414 * fire once. It still needs to be deleted with
416 * Flags [I] Flags controlling the execution of the callback. In
417 * addition to the WT_* thread pool flags (see
418 * RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and
419 * WT_EXECUTEONLYONCE are supported.
422 * Success: STATUS_SUCCESS.
423 * Failure: Any NTSTATUS code.
425 NTSTATUS WINAPI
RtlCreateTimer(HANDLE TimerQueue
, PHANDLE NewTimer
,
426 WAITORTIMERCALLBACKFUNC Callback
,
427 PVOID Parameter
, DWORD DueTime
, DWORD Period
,
431 struct queue_timer
*t
;
432 struct timer_queue
*q
= get_timer_queue(TimerQueue
);
434 if (!q
) return STATUS_NO_MEMORY
;
435 if (q
->magic
!= TIMER_QUEUE_MAGIC
) return STATUS_INVALID_HANDLE
;
437 t
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof *t
);
439 return STATUS_NO_MEMORY
;
443 t
->callback
= Callback
;
444 t
->param
= Parameter
;
450 status
= STATUS_SUCCESS
;
451 RtlEnterCriticalSection(&q
->cs
);
453 status
= STATUS_INVALID_HANDLE
;
455 queue_add_timer(t
, queue_current_time() + DueTime
, TRUE
);
456 RtlLeaveCriticalSection(&q
->cs
);
458 if (status
== STATUS_SUCCESS
)
461 RtlFreeHeap(RtlGetProcessHeap(), 0, t
);
466 /***********************************************************************
467 * RtlUpdateTimer (NTDLL.@)
469 * Changes the time at which a timer expires.
472 * TimerQueue [I] The queue that holds the timer.
473 * Timer [I] The timer to update.
474 * DueTime [I] The delay, in milliseconds, before next firing the timer.
475 * Period [I] The period, in milliseconds, at which to fire the timer
476 * after the first callback. If zero, the timer will not
477 * refire once. It still needs to be deleted with
481 * Success: STATUS_SUCCESS.
482 * Failure: Any NTSTATUS code.
484 NTSTATUS WINAPI
RtlUpdateTimer(HANDLE TimerQueue
, HANDLE Timer
,
485 DWORD DueTime
, DWORD Period
)
487 struct queue_timer
*t
= Timer
;
488 struct timer_queue
*q
= t
->q
;
490 RtlEnterCriticalSection(&q
->cs
);
491 /* Can't change a timer if it was once-only or destroyed. */
492 if (t
->expire
!= EXPIRE_NEVER
)
495 queue_move_timer(t
, queue_current_time() + DueTime
, TRUE
);
497 RtlLeaveCriticalSection(&q
->cs
);
499 return STATUS_SUCCESS
;
502 /***********************************************************************
503 * RtlDeleteTimer (NTDLL.@)
505 * Cancels a timer-queue timer.
508 * TimerQueue [I] The queue that holds the timer.
509 * Timer [I] The timer to update.
510 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
511 * wait until the timer is finished firing all pending
512 * callbacks before returning. Otherwise, return
513 * immediately and set the timer is done.
516 * Success: STATUS_SUCCESS if the timer is done, STATUS_PENDING if not,
517 or if the completion event is NULL.
518 * Failure: Any NTSTATUS code.
520 NTSTATUS WINAPI
RtlDeleteTimer(HANDLE TimerQueue
, HANDLE Timer
,
521 HANDLE CompletionEvent
)
523 struct queue_timer
*t
= Timer
;
524 struct timer_queue
*q
;
525 NTSTATUS status
= STATUS_PENDING
;
529 return STATUS_INVALID_PARAMETER_1
;
531 if (CompletionEvent
== INVALID_HANDLE_VALUE
)
533 status
= NtCreateEvent(&event
, EVENT_ALL_ACCESS
, NULL
, SynchronizationEvent
, FALSE
);
534 if (status
== STATUS_SUCCESS
)
535 status
= STATUS_PENDING
;
537 else if (CompletionEvent
)
538 event
= CompletionEvent
;
540 RtlEnterCriticalSection(&q
->cs
);
542 if (t
->runcount
== 0 && event
)
543 status
= STATUS_SUCCESS
;
544 queue_destroy_timer(t
);
545 RtlLeaveCriticalSection(&q
->cs
);
547 if (CompletionEvent
== INVALID_HANDLE_VALUE
&& event
)
549 if (status
== STATUS_PENDING
)
551 NtWaitForSingleObject(event
, FALSE
, NULL
);
552 status
= STATUS_SUCCESS
;
565 RtlDeleteTimerQueue(HANDLE TimerQueue
)
567 return RtlDeleteTimerQueueEx(TimerQueue
, INVALID_HANDLE_VALUE
);