4 * Copyright (c) 2006 Robert Shearman
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
28 #define NONAMELESSUNION
30 #define WIN32_NO_STATUS
33 #include "wine/debug.h"
34 #include "wine/list.h"
36 #include "ntdll_misc.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(threadpool);
40 #define WORKER_TIMEOUT 30000 /* 30 seconds */
42 static LONG num_workers;
43 static LONG num_work_items;
44 static LONG num_busy_workers;
46 static struct list work_item_list = LIST_INIT(work_item_list);
47 static HANDLE work_item_event;
49 static RTL_CRITICAL_SECTION threadpool_cs;
50 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
53 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
54 0, 0, { (DWORD_PTR)(__FILE__ ": threadpool_cs") }
56 static RTL_CRITICAL_SECTION threadpool_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
58 static HANDLE compl_port = NULL;
59 static RTL_CRITICAL_SECTION threadpool_compl_cs;
60 static RTL_CRITICAL_SECTION_DEBUG critsect_compl_debug =
62 0, 0, &threadpool_compl_cs,
63 { &critsect_compl_debug.ProcessLocksList, &critsect_compl_debug.ProcessLocksList },
64 0, 0, { (DWORD_PTR)(__FILE__ ": threadpool_compl_cs") }
66 static RTL_CRITICAL_SECTION threadpool_compl_cs = { &critsect_compl_debug, -1, 0, 0, 0, 0 };
71 PRTL_WORK_ITEM_ROUTINE function;
75 static inline LONG interlocked_inc( PLONG dest )
77 return interlocked_xchg_add( dest, 1 ) + 1;
80 static inline LONG interlocked_dec( PLONG dest )
82 return interlocked_xchg_add( dest, -1 ) - 1;
85 static void WINAPI worker_thread_proc(void * param)
87 interlocked_inc(&num_workers);
89 /* free the work item memory sooner to reduce memory usage */
92 if (num_work_items > 0)
95 RtlEnterCriticalSection(&threadpool_cs);
96 item = list_head(&work_item_list);
99 struct work_item *work_item_ptr = LIST_ENTRY(item, struct work_item, entry);
100 struct work_item work_item;
101 list_remove(&work_item_ptr->entry);
102 interlocked_dec(&num_work_items);
104 RtlLeaveCriticalSection(&threadpool_cs);
106 work_item = *work_item_ptr;
107 RtlFreeHeap(GetProcessHeap(), 0, work_item_ptr);
109 TRACE("executing %p(%p)\n", work_item.function, work_item.context);
111 interlocked_inc(&num_busy_workers);
114 work_item.function(work_item.context);
116 interlocked_dec(&num_busy_workers);
119 RtlLeaveCriticalSection(&threadpool_cs);
124 LARGE_INTEGER timeout;
125 timeout.QuadPart = -(WORKER_TIMEOUT * (ULONGLONG)10000);
126 status = NtWaitForSingleObject(work_item_event, FALSE, &timeout);
127 if (status != STATUS_WAIT_0)
132 interlocked_dec(&num_workers);
134 RtlExitUserThread(0);
139 static NTSTATUS add_work_item_to_queue(struct work_item *work_item)
143 RtlEnterCriticalSection(&threadpool_cs);
144 list_add_tail(&work_item_list, &work_item->entry);
146 RtlLeaveCriticalSection(&threadpool_cs);
148 if (!work_item_event)
151 status = NtCreateSemaphore(&sem, SEMAPHORE_ALL_ACCESS, NULL, 1, INT_MAX);
152 if (interlocked_cmpxchg_ptr( &work_item_event, sem, 0 ))
153 NtClose(sem); /* somebody beat us to it */
156 status = NtReleaseSemaphore(work_item_event, 1, NULL);
161 /***********************************************************************
162 * RtlQueueWorkItem (NTDLL.@)
164 * Queues a work item into a thread in the thread pool.
167 * Function [I] Work function to execute.
168 * Context [I] Context to pass to the work function when it is executed.
169 * Flags [I] Flags. See notes.
172 * Success: STATUS_SUCCESS.
173 * Failure: Any NTSTATUS code.
176 * Flags can be one or more of the following:
177 *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread.
178 *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread.
179 *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent.
180 *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
181 *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
183 NTSTATUS WINAPI RtlQueueWorkItem(PRTL_WORK_ITEM_ROUTINE Function, PVOID Context, ULONG Flags)
187 struct work_item *work_item = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(struct work_item));
190 return STATUS_NO_MEMORY;
192 work_item->function = Function;
193 work_item->context = Context;
195 if (Flags & ~WT_EXECUTELONGFUNCTION)
196 FIXME("Flags 0x%x not supported\n", Flags);
198 status = add_work_item_to_queue(work_item);
200 /* FIXME: tune this algorithm to not be as aggressive with creating threads
201 * if WT_EXECUTELONGFUNCTION isn't specified */
202 if ((status == STATUS_SUCCESS) &&
203 ((num_workers == 0) || (num_workers == num_busy_workers)))
205 status = RtlCreateUserThread( GetCurrentProcess(), NULL, FALSE,
207 worker_thread_proc, NULL, &thread, NULL );
208 if (status == STATUS_SUCCESS)
211 /* NOTE: we don't care if we couldn't create the thread if there is at
212 * least one other available to process the request */
213 if ((num_workers > 0) && (status != STATUS_SUCCESS))
214 status = STATUS_SUCCESS;
217 if (status != STATUS_SUCCESS)
219 RtlEnterCriticalSection(&threadpool_cs);
221 interlocked_dec(&num_work_items);
222 list_remove(&work_item->entry);
223 RtlFreeHeap(GetProcessHeap(), 0, work_item);
225 RtlLeaveCriticalSection(&threadpool_cs);
230 return STATUS_SUCCESS;
233 /***********************************************************************
234 * iocp_poller - get completion events and run callbacks
236 static DWORD CALLBACK iocp_poller(LPVOID Arg)
240 PRTL_OVERLAPPED_COMPLETION_ROUTINE callback;
242 IO_STATUS_BLOCK iosb;
243 NTSTATUS res = NtRemoveIoCompletion( compl_port, (PULONG_PTR)&callback, (PULONG_PTR)&overlapped, &iosb, NULL );
246 ERR("NtRemoveIoCompletion failed: 0x%x\n", res);
250 DWORD transferred = 0;
253 if (iosb.u.Status == STATUS_SUCCESS)
254 transferred = iosb.Information;
256 err = RtlNtStatusToDosError(iosb.u.Status);
258 callback( err, transferred, overlapped );
264 /***********************************************************************
265 * RtlSetIoCompletionCallback (NTDLL.@)
267 * Binds a handle to a thread pool's completion port, and possibly
268 * starts a non-I/O thread to monitor this port and call functions back.
271 * FileHandle [I] Handle to bind to a completion port.
272 * Function [I] Callback function to call on I/O completions.
273 * Flags [I] Not used.
276 * Success: STATUS_SUCCESS.
277 * Failure: Any NTSTATUS code.
280 NTSTATUS WINAPI RtlSetIoCompletionCallback(HANDLE FileHandle, PRTL_OVERLAPPED_COMPLETION_ROUTINE Function, ULONG Flags)
282 IO_STATUS_BLOCK iosb;
283 FILE_COMPLETION_INFORMATION info;
285 if (Flags) FIXME("Unknown value Flags=0x%x\n", Flags);
289 NTSTATUS res = STATUS_SUCCESS;
291 RtlEnterCriticalSection(&threadpool_compl_cs);
296 res = NtCreateIoCompletion( &cport, IO_COMPLETION_ALL_ACCESS, NULL, 0 );
299 /* FIXME native can start additional threads in case of e.g. hung callback function. */
300 res = RtlQueueWorkItem( iocp_poller, NULL, WT_EXECUTEDEFAULT );
307 RtlLeaveCriticalSection(&threadpool_compl_cs);
311 info.CompletionPort = compl_port;
312 info.CompletionKey = (ULONG_PTR)Function;
314 return NtSetInformationFile( FileHandle, &iosb, &info, sizeof(info), FileCompletionInformation );
317 static inline PLARGE_INTEGER get_nt_timeout( PLARGE_INTEGER pTime, ULONG timeout )
319 if (timeout == INFINITE) return NULL;
320 pTime->QuadPart = (ULONGLONG)timeout * -10000;
324 struct wait_work_item
328 WAITORTIMERCALLBACK Callback;
332 HANDLE CompletionEvent;
334 BOOLEAN CallbackInProgress;
337 static void delete_wait_work_item(struct wait_work_item *wait_work_item)
339 NtClose( wait_work_item->CancelEvent );
340 RtlFreeHeap( GetProcessHeap(), 0, wait_work_item );
343 static DWORD CALLBACK wait_thread_proc(LPVOID Arg)
345 struct wait_work_item *wait_work_item = Arg;
347 BOOLEAN alertable = (wait_work_item->Flags & WT_EXECUTEINIOTHREAD) != 0;
348 HANDLE handles[2] = { wait_work_item->Object, wait_work_item->CancelEvent };
349 LARGE_INTEGER timeout;
350 HANDLE completion_event;
356 status = NtWaitForMultipleObjects( 2, handles, FALSE, alertable,
357 get_nt_timeout( &timeout, wait_work_item->Milliseconds ) );
358 if (status == STATUS_WAIT_0 || status == STATUS_TIMEOUT)
360 BOOLEAN TimerOrWaitFired;
362 if (status == STATUS_WAIT_0)
364 TRACE( "object %p signaled, calling callback %p with context %p\n",
365 wait_work_item->Object, wait_work_item->Callback,
366 wait_work_item->Context );
367 TimerOrWaitFired = FALSE;
371 TRACE( "wait for object %p timed out, calling callback %p with context %p\n",
372 wait_work_item->Object, wait_work_item->Callback,
373 wait_work_item->Context );
374 TimerOrWaitFired = TRUE;
376 wait_work_item->CallbackInProgress = TRUE;
377 wait_work_item->Callback( wait_work_item->Context, TimerOrWaitFired );
378 wait_work_item->CallbackInProgress = FALSE;
380 if (wait_work_item->Flags & WT_EXECUTEONLYONCE)
387 completion_event = wait_work_item->CompletionEvent;
388 if (completion_event) NtSetEvent( completion_event, NULL );
390 if (interlocked_inc( &wait_work_item->DeleteCount ) == 2 )
391 delete_wait_work_item( wait_work_item );
396 /***********************************************************************
397 * RtlRegisterWait (NTDLL.@)
399 * Registers a wait for a handle to become signaled.
402 * NewWaitObject [I] Handle to the new wait object. Use RtlDeregisterWait() to free it.
403 * Object [I] Object to wait to become signaled.
404 * Callback [I] Callback function to execute when the wait times out or the handle is signaled.
405 * Context [I] Context to pass to the callback function when it is executed.
406 * Milliseconds [I] Number of milliseconds to wait before timing out.
407 * Flags [I] Flags. See notes.
410 * Success: STATUS_SUCCESS.
411 * Failure: Any NTSTATUS code.
414 * Flags can be one or more of the following:
415 *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread.
416 *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread.
417 *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent.
418 *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
419 *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
421 NTSTATUS WINAPI RtlRegisterWait(PHANDLE NewWaitObject, HANDLE Object,
422 RTL_WAITORTIMERCALLBACKFUNC Callback,
423 PVOID Context, ULONG Milliseconds, ULONG Flags)
425 struct wait_work_item *wait_work_item;
428 TRACE( "(%p, %p, %p, %p, %d, 0x%x)\n", NewWaitObject, Object, Callback, Context, Milliseconds, Flags );
430 wait_work_item = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*wait_work_item) );
432 return STATUS_NO_MEMORY;
434 wait_work_item->Object = Object;
435 wait_work_item->Callback = Callback;
436 wait_work_item->Context = Context;
437 wait_work_item->Milliseconds = Milliseconds;
438 wait_work_item->Flags = Flags;
439 wait_work_item->CallbackInProgress = FALSE;
440 wait_work_item->DeleteCount = 0;
441 wait_work_item->CompletionEvent = NULL;
443 status = NtCreateEvent( &wait_work_item->CancelEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
444 if (status != STATUS_SUCCESS)
446 RtlFreeHeap( GetProcessHeap(), 0, wait_work_item );
450 Flags = Flags & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD |
451 WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION);
452 status = RtlQueueWorkItem( wait_thread_proc, wait_work_item, Flags );
453 if (status != STATUS_SUCCESS)
455 delete_wait_work_item( wait_work_item );
459 *NewWaitObject = wait_work_item;
463 /***********************************************************************
464 * RtlDeregisterWaitEx (NTDLL.@)
466 * Cancels a wait operation and frees the resources associated with calling
470 * WaitObject [I] Handle to the wait object to free.
473 * Success: STATUS_SUCCESS.
474 * Failure: Any NTSTATUS code.
476 NTSTATUS WINAPI RtlDeregisterWaitEx(HANDLE WaitHandle, HANDLE CompletionEvent)
478 struct wait_work_item *wait_work_item = WaitHandle;
479 NTSTATUS status = STATUS_SUCCESS;
481 TRACE( "(%p)\n", WaitHandle );
483 NtSetEvent( wait_work_item->CancelEvent, NULL );
484 if (wait_work_item->CallbackInProgress)
486 if (CompletionEvent != NULL)
488 if (CompletionEvent == INVALID_HANDLE_VALUE)
490 status = NtCreateEvent( &CompletionEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
491 if (status != STATUS_SUCCESS)
493 interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
494 if (wait_work_item->CallbackInProgress)
495 NtWaitForSingleObject( CompletionEvent, FALSE, NULL );
496 NtClose( CompletionEvent );
500 interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
501 if (wait_work_item->CallbackInProgress)
502 status = STATUS_PENDING;
506 status = STATUS_PENDING;
509 if (interlocked_inc( &wait_work_item->DeleteCount ) == 2 )
511 status = STATUS_SUCCESS;
512 delete_wait_work_item( wait_work_item );
518 /***********************************************************************
519 * RtlDeregisterWait (NTDLL.@)
521 * Cancels a wait operation and frees the resources associated with calling
525 * WaitObject [I] Handle to the wait object to free.
528 * Success: STATUS_SUCCESS.
529 * Failure: Any NTSTATUS code.
531 NTSTATUS WINAPI RtlDeregisterWait(HANDLE WaitHandle)
533 return RtlDeregisterWaitEx(WaitHandle, NULL);
537 /************************** Timer Queue Impl **************************/
542 struct timer_queue *q;
544 ULONG runcount; /* number of callbacks pending execution */
545 RTL_WAITORTIMERCALLBACKFUNC callback;
550 BOOL destroy; /* timer should be deleted; once set, never unset */
551 HANDLE event; /* removal event */
556 RTL_CRITICAL_SECTION cs;
557 struct list timers; /* sorted by expiration time */
558 BOOL quit; /* queue should be deleted; once set, never unset */
563 #define EXPIRE_NEVER (~(ULONGLONG) 0)
565 static void queue_remove_timer(struct queue_timer *t)
567 /* We MUST hold the queue cs while calling this function. This ensures
568 that we cannot queue another callback for this timer. The runcount
569 being zero makes sure we don't have any already queued. */
570 struct timer_queue *q = t->q;
572 assert(t->runcount == 0);
575 list_remove(&t->entry);
577 NtSetEvent(t->event, NULL);
578 RtlFreeHeap(GetProcessHeap(), 0, t);
580 if (q->quit && list_empty(&q->timers))
581 NtSetEvent(q->event, NULL);
584 static void timer_cleanup_callback(struct queue_timer *t)
586 struct timer_queue *q = t->q;
587 RtlEnterCriticalSection(&q->cs);
589 assert(0 < t->runcount);
592 if (t->destroy && t->runcount == 0)
593 queue_remove_timer(t);
595 RtlLeaveCriticalSection(&q->cs);
598 static DWORD WINAPI timer_callback_wrapper(LPVOID p)
600 struct queue_timer *t = p;
601 t->callback(t->param, TRUE);
602 timer_cleanup_callback(t);
606 static inline ULONGLONG queue_current_time(void)
608 LARGE_INTEGER now, freq;
609 NtQueryPerformanceCounter(&now, &freq);
610 return now.QuadPart * 1000 / freq.QuadPart;
613 static void queue_add_timer(struct queue_timer *t, ULONGLONG time,
616 /* We MUST hold the queue cs while calling this function. */
617 struct timer_queue *q = t->q;
618 struct list *ptr = &q->timers;
620 assert(!q->quit || (t->destroy && time == EXPIRE_NEVER));
622 if (time != EXPIRE_NEVER)
623 LIST_FOR_EACH(ptr, &q->timers)
625 struct queue_timer *cur = LIST_ENTRY(ptr, struct queue_timer, entry);
626 if (time < cur->expire)
629 list_add_before(ptr, &t->entry);
633 /* If we insert at the head of the list, we need to expire sooner
635 if (set_event && &t->entry == list_head(&q->timers))
636 NtSetEvent(q->event, NULL);
639 static inline void queue_move_timer(struct queue_timer *t, ULONGLONG time,
642 /* We MUST hold the queue cs while calling this function. */
643 list_remove(&t->entry);
644 queue_add_timer(t, time, set_event);
647 static void queue_timer_expire(struct timer_queue *q)
649 struct queue_timer *t = NULL;
651 RtlEnterCriticalSection(&q->cs);
652 if (list_head(&q->timers))
655 t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry);
656 if (!t->destroy && t->expire <= ((now = queue_current_time())))
661 next = t->expire + t->period;
662 /* avoid trigger cascade if overloaded / hibernated */
664 next = now + t->period;
668 queue_move_timer(t, next, FALSE);
673 RtlLeaveCriticalSection(&q->cs);
677 if (t->flags & WT_EXECUTEINTIMERTHREAD)
678 timer_callback_wrapper(t);
683 & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD
684 | WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION));
685 NTSTATUS status = RtlQueueWorkItem(timer_callback_wrapper, t, flags);
686 if (status != STATUS_SUCCESS)
687 timer_cleanup_callback(t);
692 static ULONG queue_get_timeout(struct timer_queue *q)
694 struct queue_timer *t;
695 ULONG timeout = INFINITE;
697 RtlEnterCriticalSection(&q->cs);
698 if (list_head(&q->timers))
700 t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry);
701 assert(!t->destroy || t->expire == EXPIRE_NEVER);
703 if (t->expire != EXPIRE_NEVER)
705 ULONGLONG time = queue_current_time();
706 timeout = t->expire < time ? 0 : t->expire - time;
709 RtlLeaveCriticalSection(&q->cs);
714 static void WINAPI timer_queue_thread_proc(LPVOID p)
716 struct timer_queue *q = p;
719 timeout_ms = INFINITE;
722 LARGE_INTEGER timeout;
726 status = NtWaitForSingleObject(
727 q->event, FALSE, get_nt_timeout(&timeout, timeout_ms));
729 if (status == STATUS_WAIT_0)
731 /* There are two possible ways to trigger the event. Either
732 we are quitting and the last timer got removed, or a new
733 timer got put at the head of the list so we need to adjust
735 RtlEnterCriticalSection(&q->cs);
736 if (q->quit && list_empty(&q->timers))
738 RtlLeaveCriticalSection(&q->cs);
740 else if (status == STATUS_TIMEOUT)
741 queue_timer_expire(q);
746 timeout_ms = queue_get_timeout(q);
750 RtlDeleteCriticalSection(&q->cs);
751 RtlFreeHeap(GetProcessHeap(), 0, q);
754 static void queue_destroy_timer(struct queue_timer *t)
756 /* We MUST hold the queue cs while calling this function. */
758 if (t->runcount == 0)
759 /* Ensure a timer is promptly removed. If callbacks are pending,
760 it will be removed after the last one finishes by the callback
762 queue_remove_timer(t);
764 /* Make sure no destroyed timer masks an active timer at the head
765 of the sorted list. */
766 queue_move_timer(t, EXPIRE_NEVER, FALSE);
769 /***********************************************************************
770 * RtlCreateTimerQueue (NTDLL.@)
772 * Creates a timer queue object and returns a handle to it.
775 * NewTimerQueue [O] The newly created queue.
778 * Success: STATUS_SUCCESS.
779 * Failure: Any NTSTATUS code.
781 NTSTATUS WINAPI RtlCreateTimerQueue(PHANDLE NewTimerQueue)
784 struct timer_queue *q = RtlAllocateHeap(GetProcessHeap(), 0, sizeof *q);
786 return STATUS_NO_MEMORY;
788 RtlInitializeCriticalSection(&q->cs);
789 list_init(&q->timers);
791 status = NtCreateEvent(&q->event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
792 if (status != STATUS_SUCCESS)
794 RtlFreeHeap(GetProcessHeap(), 0, q);
797 status = RtlCreateUserThread(GetCurrentProcess(), NULL, FALSE, NULL, 0, 0,
798 timer_queue_thread_proc, q, &q->thread, NULL);
799 if (status != STATUS_SUCCESS)
802 RtlFreeHeap(GetProcessHeap(), 0, q);
807 return STATUS_SUCCESS;
810 /***********************************************************************
811 * RtlDeleteTimerQueueEx (NTDLL.@)
813 * Deletes a timer queue object.
816 * TimerQueue [I] The timer queue to destroy.
817 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
818 * wait until all timers are finished firing before
819 * returning. Otherwise, return immediately and set the
820 * event when all timers are done.
823 * Success: STATUS_SUCCESS if synchronous, STATUS_PENDING if not.
824 * Failure: Any NTSTATUS code.
826 NTSTATUS WINAPI RtlDeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent)
828 struct timer_queue *q = TimerQueue;
829 struct queue_timer *t, *temp;
834 return STATUS_INVALID_HANDLE;
838 RtlEnterCriticalSection(&q->cs);
840 if (list_head(&q->timers))
841 /* When the last timer is removed, it will signal the timer thread to
843 LIST_FOR_EACH_ENTRY_SAFE(t, temp, &q->timers, struct queue_timer, entry)
844 queue_destroy_timer(t);
846 /* However if we have none, we must do it ourselves. */
847 NtSetEvent(q->event, NULL);
848 RtlLeaveCriticalSection(&q->cs);
850 if (CompletionEvent == INVALID_HANDLE_VALUE)
852 NtWaitForSingleObject(thread, FALSE, NULL);
853 status = STATUS_SUCCESS;
859 FIXME("asynchronous return on completion event unimplemented\n");
860 NtWaitForSingleObject(thread, FALSE, NULL);
861 NtSetEvent(CompletionEvent, NULL);
863 status = STATUS_PENDING;
870 static struct timer_queue *default_timer_queue;
872 static struct timer_queue *get_timer_queue(HANDLE TimerQueue)
878 if (!default_timer_queue)
881 NTSTATUS status = RtlCreateTimerQueue(&q);
882 if (status == STATUS_SUCCESS)
884 PVOID p = interlocked_cmpxchg_ptr(
885 (void **) &default_timer_queue, q, NULL);
887 /* Got beat to the punch. */
888 RtlDeleteTimerQueueEx(p, NULL);
891 return default_timer_queue;
895 /***********************************************************************
896 * RtlCreateTimer (NTDLL.@)
898 * Creates a new timer associated with the given queue.
901 * NewTimer [O] The newly created timer.
902 * TimerQueue [I] The queue to hold the timer.
903 * Callback [I] The callback to fire.
904 * Parameter [I] The argument for the callback.
905 * DueTime [I] The delay, in milliseconds, before first firing the
907 * Period [I] The period, in milliseconds, at which to fire the timer
908 * after the first callback. If zero, the timer will only
909 * fire once. It still needs to be deleted with
911 * Flags [I] Flags controlling the execution of the callback. In
912 * addition to the WT_* thread pool flags (see
913 * RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and
914 * WT_EXECUTEONLYONCE are supported.
917 * Success: STATUS_SUCCESS.
918 * Failure: Any NTSTATUS code.
920 NTSTATUS WINAPI RtlCreateTimer(PHANDLE NewTimer, HANDLE TimerQueue,
921 RTL_WAITORTIMERCALLBACKFUNC Callback,
922 PVOID Parameter, DWORD DueTime, DWORD Period,
926 struct queue_timer *t;
927 struct timer_queue *q = get_timer_queue(TimerQueue);
929 return STATUS_NO_MEMORY;
931 t = RtlAllocateHeap(GetProcessHeap(), 0, sizeof *t);
933 return STATUS_NO_MEMORY;
937 t->callback = Callback;
938 t->param = Parameter;
944 status = STATUS_SUCCESS;
945 RtlEnterCriticalSection(&q->cs);
947 status = STATUS_INVALID_HANDLE;
949 queue_add_timer(t, queue_current_time() + DueTime, TRUE);
950 RtlLeaveCriticalSection(&q->cs);
952 if (status == STATUS_SUCCESS)
955 RtlFreeHeap(GetProcessHeap(), 0, t);
960 /***********************************************************************
961 * RtlUpdateTimer (NTDLL.@)
963 * Changes the time at which a timer expires.
966 * TimerQueue [I] The queue that holds the timer.
967 * Timer [I] The timer to update.
968 * DueTime [I] The delay, in milliseconds, before next firing the timer.
969 * Period [I] The period, in milliseconds, at which to fire the timer
970 * after the first callback. If zero, the timer will not
971 * refire once. It still needs to be deleted with
975 * Success: STATUS_SUCCESS.
976 * Failure: Any NTSTATUS code.
978 NTSTATUS WINAPI RtlUpdateTimer(HANDLE TimerQueue, HANDLE Timer,
979 DWORD DueTime, DWORD Period)
981 struct queue_timer *t = Timer;
982 struct timer_queue *q = t->q;
984 RtlEnterCriticalSection(&q->cs);
985 /* Can't change a timer if it was once-only or destroyed. */
986 if (t->expire != EXPIRE_NEVER)
989 queue_move_timer(t, queue_current_time() + DueTime, TRUE);
991 RtlLeaveCriticalSection(&q->cs);
993 return STATUS_SUCCESS;
996 /***********************************************************************
997 * RtlDeleteTimer (NTDLL.@)
999 * Cancels a timer-queue timer.
1002 * TimerQueue [I] The queue that holds the timer.
1003 * Timer [I] The timer to update.
1004 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
1005 * wait until the timer is finished firing all pending
1006 * callbacks before returning. Otherwise, return
1007 * immediately and set the timer is done.
1010 * Success: STATUS_SUCCESS if the timer is done, STATUS_PENDING if not,
1011 or if the completion event is NULL.
1012 * Failure: Any NTSTATUS code.
1014 NTSTATUS WINAPI RtlDeleteTimer(HANDLE TimerQueue, HANDLE Timer,
1015 HANDLE CompletionEvent)
1017 struct queue_timer *t = Timer;
1018 struct timer_queue *q;
1019 NTSTATUS status = STATUS_PENDING;
1020 HANDLE event = NULL;
1023 return STATUS_INVALID_PARAMETER_1;
1025 if (CompletionEvent == INVALID_HANDLE_VALUE)
1027 status = NtCreateEvent(&event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
1028 if (status == STATUS_SUCCESS)
1029 status = STATUS_PENDING;
1031 else if (CompletionEvent)
1032 event = CompletionEvent;
1034 RtlEnterCriticalSection(&q->cs);
1036 if (t->runcount == 0 && event)
1037 status = STATUS_SUCCESS;
1038 queue_destroy_timer(t);
1039 RtlLeaveCriticalSection(&q->cs);
1041 if (CompletionEvent == INVALID_HANDLE_VALUE && event)
1043 if (status == STATUS_PENDING)
1045 NtWaitForSingleObject(event, FALSE, NULL);
1046 status = STATUS_SUCCESS;