ntdll: Implement the timer queue thread.
[wine/gsoc_dplay.git] / dlls / ntdll / threadpool.c
blobcbb97f4fd7c1755dd23bcbf0e07e3131cd12ce4f
1 /*
2 * Thread pooling
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
21 #include "config.h"
22 #include "wine/port.h"
24 #include <assert.h>
25 #include <stdarg.h>
26 #include <limits.h>
28 #define NONAMELESSUNION
29 #include "ntstatus.h"
30 #define WIN32_NO_STATUS
31 #include "winternl.h"
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 =
52 0, 0, &threadpool_cs,
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 };
68 struct work_item
70 struct list entry;
71 PRTL_WORK_ITEM_ROUTINE function;
72 PVOID context;
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 */
90 while (TRUE)
92 if (num_work_items > 0)
94 struct list *item;
95 RtlEnterCriticalSection(&threadpool_cs);
96 item = list_head(&work_item_list);
97 if (item)
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);
113 /* do the work */
114 work_item.function(work_item.context);
116 interlocked_dec(&num_busy_workers);
118 else
119 RtlLeaveCriticalSection(&threadpool_cs);
121 else
123 NTSTATUS status;
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)
128 break;
132 interlocked_dec(&num_workers);
134 RtlExitUserThread(0);
136 /* never reached */
139 static NTSTATUS add_work_item_to_queue(struct work_item *work_item)
141 NTSTATUS status;
143 RtlEnterCriticalSection(&threadpool_cs);
144 list_add_tail(&work_item_list, &work_item->entry);
145 num_work_items++;
146 RtlLeaveCriticalSection(&threadpool_cs);
148 if (!work_item_event)
150 HANDLE sem;
151 status = NtCreateSemaphore(&sem, SEMAPHORE_ALL_ACCESS, NULL, 1, LONG_MAX);
152 if (interlocked_cmpxchg_ptr( &work_item_event, sem, 0 ))
153 NtClose(sem); /* somebody beat us to it */
155 else
156 status = NtReleaseSemaphore(work_item_event, 1, NULL);
158 return status;
161 /***********************************************************************
162 * RtlQueueWorkItem (NTDLL.@)
164 * Queues a work item into a thread in the thread pool.
166 * PARAMS
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.
171 * RETURNS
172 * Success: STATUS_SUCCESS.
173 * Failure: Any NTSTATUS code.
175 * NOTES
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)
185 HANDLE thread;
186 NTSTATUS status;
187 struct work_item *work_item = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(struct work_item));
189 if (!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,
206 NULL, 0, 0,
207 worker_thread_proc, NULL, &thread, NULL );
208 if (status == STATUS_SUCCESS)
209 NtClose( thread );
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);
227 return status;
230 return STATUS_SUCCESS;
233 /***********************************************************************
234 * iocp_poller - get completion events and run callbacks
236 static DWORD CALLBACK iocp_poller(LPVOID Arg)
238 while( TRUE )
240 PRTL_OVERLAPPED_COMPLETION_ROUTINE callback;
241 LPVOID overlapped;
242 IO_STATUS_BLOCK iosb;
243 NTSTATUS res = NtRemoveIoCompletion( compl_port, (PULONG_PTR)&callback, (PULONG_PTR)&overlapped, &iosb, NULL );
244 if (res)
246 ERR("NtRemoveIoCompletion failed: 0x%x\n", res);
248 else
250 DWORD transferred = 0;
251 DWORD err = 0;
253 if (iosb.u.Status == STATUS_SUCCESS)
254 transferred = iosb.Information;
255 else
256 err = RtlNtStatusToDosError(iosb.u.Status);
258 callback( err, transferred, overlapped );
263 /***********************************************************************
264 * RtlSetIoCompletionCallback (NTDLL.@)
266 * Binds a handle to a thread pool's completion port, and possibly
267 * starts a non-I/O thread to monitor this port and call functions back.
269 * PARAMS
270 * FileHandle [I] Handle to bind to a completion port.
271 * Function [I] Callback function to call on I/O completions.
272 * Flags [I] Not used.
274 * RETURNS
275 * Success: STATUS_SUCCESS.
276 * Failure: Any NTSTATUS code.
279 NTSTATUS WINAPI RtlSetIoCompletionCallback(HANDLE FileHandle, PRTL_OVERLAPPED_COMPLETION_ROUTINE Function, ULONG Flags)
281 IO_STATUS_BLOCK iosb;
282 FILE_COMPLETION_INFORMATION info;
284 if (Flags) FIXME("Unknown value Flags=0x%x\n", Flags);
286 if (!compl_port)
288 NTSTATUS res = STATUS_SUCCESS;
290 RtlEnterCriticalSection(&threadpool_compl_cs);
291 if (!compl_port)
293 HANDLE cport;
295 res = NtCreateIoCompletion( &cport, IO_COMPLETION_ALL_ACCESS, NULL, 0 );
296 if (!res)
298 /* FIXME native can start additional threads in case of e.g. hung callback function. */
299 res = RtlQueueWorkItem( iocp_poller, NULL, WT_EXECUTEDEFAULT );
300 if (!res)
301 compl_port = cport;
302 else
303 NtClose( cport );
306 RtlLeaveCriticalSection(&threadpool_compl_cs);
307 if (res) return res;
310 info.CompletionPort = compl_port;
311 info.CompletionKey = (ULONG_PTR)Function;
313 return NtSetInformationFile( FileHandle, &iosb, &info, sizeof(info), FileCompletionInformation );
316 static inline PLARGE_INTEGER get_nt_timeout( PLARGE_INTEGER pTime, ULONG timeout )
318 if (timeout == INFINITE) return NULL;
319 pTime->QuadPart = (ULONGLONG)timeout * -10000;
320 return pTime;
323 struct wait_work_item
325 HANDLE Object;
326 HANDLE CancelEvent;
327 WAITORTIMERCALLBACK Callback;
328 PVOID Context;
329 ULONG Milliseconds;
330 ULONG Flags;
331 HANDLE CompletionEvent;
332 LONG DeleteCount;
333 BOOLEAN CallbackInProgress;
336 static void delete_wait_work_item(struct wait_work_item *wait_work_item)
338 NtClose( wait_work_item->CancelEvent );
339 RtlFreeHeap( GetProcessHeap(), 0, wait_work_item );
342 static DWORD CALLBACK wait_thread_proc(LPVOID Arg)
344 struct wait_work_item *wait_work_item = Arg;
345 NTSTATUS status;
346 BOOLEAN alertable = (wait_work_item->Flags & WT_EXECUTEINIOTHREAD) ? TRUE : FALSE;
347 HANDLE handles[2] = { wait_work_item->Object, wait_work_item->CancelEvent };
348 LARGE_INTEGER timeout;
349 HANDLE completion_event;
351 TRACE("\n");
353 while (TRUE)
355 status = NtWaitForMultipleObjects( 2, handles, FALSE, alertable,
356 get_nt_timeout( &timeout, wait_work_item->Milliseconds ) );
357 if (status == STATUS_WAIT_0 || status == STATUS_TIMEOUT)
359 BOOLEAN TimerOrWaitFired;
361 if (status == STATUS_WAIT_0)
363 TRACE( "object %p signaled, calling callback %p with context %p\n",
364 wait_work_item->Object, wait_work_item->Callback,
365 wait_work_item->Context );
366 TimerOrWaitFired = FALSE;
368 else
370 TRACE( "wait for object %p timed out, calling callback %p with context %p\n",
371 wait_work_item->Object, wait_work_item->Callback,
372 wait_work_item->Context );
373 TimerOrWaitFired = TRUE;
375 wait_work_item->CallbackInProgress = TRUE;
376 wait_work_item->Callback( wait_work_item->Context, TimerOrWaitFired );
377 wait_work_item->CallbackInProgress = FALSE;
379 if (wait_work_item->Flags & WT_EXECUTEONLYONCE)
380 break;
382 else
383 break;
386 completion_event = wait_work_item->CompletionEvent;
387 if (completion_event) NtSetEvent( completion_event, NULL );
389 if (interlocked_inc( &wait_work_item->DeleteCount ) == 2 )
390 delete_wait_work_item( wait_work_item );
392 return 0;
395 /***********************************************************************
396 * RtlRegisterWait (NTDLL.@)
398 * Registers a wait for a handle to become signaled.
400 * PARAMS
401 * NewWaitObject [I] Handle to the new wait object. Use RtlDeregisterWait() to free it.
402 * Object [I] Object to wait to become signaled.
403 * Callback [I] Callback function to execute when the wait times out or the handle is signaled.
404 * Context [I] Context to pass to the callback function when it is executed.
405 * Milliseconds [I] Number of milliseconds to wait before timing out.
406 * Flags [I] Flags. See notes.
408 * RETURNS
409 * Success: STATUS_SUCCESS.
410 * Failure: Any NTSTATUS code.
412 * NOTES
413 * Flags can be one or more of the following:
414 *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread.
415 *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread.
416 *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent.
417 *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
418 *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
420 NTSTATUS WINAPI RtlRegisterWait(PHANDLE NewWaitObject, HANDLE Object,
421 RTL_WAITORTIMERCALLBACKFUNC Callback,
422 PVOID Context, ULONG Milliseconds, ULONG Flags)
424 struct wait_work_item *wait_work_item;
425 NTSTATUS status;
427 TRACE( "(%p, %p, %p, %p, %d, 0x%x)\n", NewWaitObject, Object, Callback, Context, Milliseconds, Flags );
429 wait_work_item = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*wait_work_item) );
430 if (!wait_work_item)
431 return STATUS_NO_MEMORY;
433 wait_work_item->Object = Object;
434 wait_work_item->Callback = Callback;
435 wait_work_item->Context = Context;
436 wait_work_item->Milliseconds = Milliseconds;
437 wait_work_item->Flags = Flags;
438 wait_work_item->CallbackInProgress = FALSE;
439 wait_work_item->DeleteCount = 0;
440 wait_work_item->CompletionEvent = NULL;
442 status = NtCreateEvent( &wait_work_item->CancelEvent, EVENT_ALL_ACCESS, NULL, TRUE, FALSE );
443 if (status != STATUS_SUCCESS)
445 RtlFreeHeap( GetProcessHeap(), 0, wait_work_item );
446 return status;
449 status = RtlQueueWorkItem( wait_thread_proc, wait_work_item, Flags & ~WT_EXECUTEONLYONCE );
450 if (status != STATUS_SUCCESS)
452 delete_wait_work_item( wait_work_item );
453 return status;
456 *NewWaitObject = wait_work_item;
457 return status;
460 /***********************************************************************
461 * RtlDeregisterWaitEx (NTDLL.@)
463 * Cancels a wait operation and frees the resources associated with calling
464 * RtlRegisterWait().
466 * PARAMS
467 * WaitObject [I] Handle to the wait object to free.
469 * RETURNS
470 * Success: STATUS_SUCCESS.
471 * Failure: Any NTSTATUS code.
473 NTSTATUS WINAPI RtlDeregisterWaitEx(HANDLE WaitHandle, HANDLE CompletionEvent)
475 struct wait_work_item *wait_work_item = WaitHandle;
476 NTSTATUS status = STATUS_SUCCESS;
478 TRACE( "(%p)\n", WaitHandle );
480 NtSetEvent( wait_work_item->CancelEvent, NULL );
481 if (wait_work_item->CallbackInProgress)
483 if (CompletionEvent != NULL)
485 if (CompletionEvent == INVALID_HANDLE_VALUE)
487 status = NtCreateEvent( &CompletionEvent, EVENT_ALL_ACCESS, NULL, TRUE, FALSE );
488 if (status != STATUS_SUCCESS)
489 return status;
490 interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
491 if (wait_work_item->CallbackInProgress)
492 NtWaitForSingleObject( CompletionEvent, FALSE, NULL );
493 NtClose( CompletionEvent );
495 else
497 interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
498 if (wait_work_item->CallbackInProgress)
499 status = STATUS_PENDING;
502 else
503 status = STATUS_PENDING;
506 if (interlocked_inc( &wait_work_item->DeleteCount ) == 2 )
508 status = STATUS_SUCCESS;
509 delete_wait_work_item( wait_work_item );
512 return status;
515 /***********************************************************************
516 * RtlDeregisterWait (NTDLL.@)
518 * Cancels a wait operation and frees the resources associated with calling
519 * RtlRegisterWait().
521 * PARAMS
522 * WaitObject [I] Handle to the wait object to free.
524 * RETURNS
525 * Success: STATUS_SUCCESS.
526 * Failure: Any NTSTATUS code.
528 NTSTATUS WINAPI RtlDeregisterWait(HANDLE WaitHandle)
530 return RtlDeregisterWaitEx(WaitHandle, NULL);
534 /************************** Timer Queue Impl **************************/
536 struct timer_queue;
537 struct queue_timer
539 struct timer_queue *q;
540 struct list entry;
541 ULONG runcount; /* number of callbacks pending execution */
542 RTL_WAITORTIMERCALLBACKFUNC callback;
543 PVOID param;
544 DWORD period;
545 ULONG flags;
546 ULONGLONG expire;
547 BOOL destroy; /* timer should be deleted; once set, never unset */
550 struct timer_queue
552 RTL_CRITICAL_SECTION cs;
553 struct list timers; /* sorted by expiration time */
554 BOOL quit; /* queue should be deleted; once set, never unset */
555 HANDLE event;
556 HANDLE thread;
559 #define EXPIRE_NEVER (~(ULONGLONG) 0)
561 static void queue_remove_timer(struct queue_timer *t)
563 /* We MUST hold the queue cs while calling this function. This ensures
564 that we cannot queue another callback for this timer. The runcount
565 being zero makes sure we don't have any already queued. */
566 struct timer_queue *q = t->q;
568 assert(t->runcount == 0);
569 assert(t->destroy);
571 list_remove(&t->entry);
572 RtlFreeHeap(GetProcessHeap(), 0, t);
574 if (q->quit && list_count(&q->timers) == 0)
575 NtSetEvent(q->event, NULL);
578 static void timer_cleanup_callback(struct queue_timer *t)
580 struct timer_queue *q = t->q;
581 RtlEnterCriticalSection(&q->cs);
583 assert(0 < t->runcount);
584 --t->runcount;
586 if (t->destroy && t->runcount == 0)
587 queue_remove_timer(t);
589 RtlLeaveCriticalSection(&q->cs);
592 static DWORD WINAPI timer_callback_wrapper(LPVOID p)
594 struct queue_timer *t = p;
595 t->callback(t->param, TRUE);
596 timer_cleanup_callback(t);
597 return 0;
600 static inline ULONGLONG queue_current_time(void)
602 LARGE_INTEGER now;
603 NtQuerySystemTime(&now);
604 return now.QuadPart / 10000;
607 static void queue_add_timer(struct queue_timer *t, ULONGLONG time,
608 BOOL set_event)
610 /* We MUST hold the queue cs while calling this function. */
611 struct timer_queue *q = t->q;
612 struct list *ptr = &q->timers;
614 assert(!q->quit || (t->destroy && time == EXPIRE_NEVER));
616 if (time != EXPIRE_NEVER)
617 LIST_FOR_EACH(ptr, &q->timers)
619 struct queue_timer *cur = LIST_ENTRY(ptr, struct queue_timer, entry);
620 if (time < cur->expire)
621 break;
623 list_add_before(ptr, &t->entry);
625 t->expire = time;
627 /* If we insert at the head of the list, we need to expire sooner
628 than expected. */
629 if (set_event && &t->entry == list_head(&q->timers))
630 NtSetEvent(q->event, NULL);
633 static inline void queue_move_timer(struct queue_timer *t, ULONGLONG time,
634 BOOL set_event)
636 /* We MUST hold the queue cs while calling this function. */
637 list_remove(&t->entry);
638 queue_add_timer(t, time, set_event);
641 static void queue_timer_expire(struct timer_queue *q)
643 struct queue_timer *t;
645 RtlEnterCriticalSection(&q->cs);
646 if (list_head(&q->timers))
648 t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry);
649 if (!t->destroy && t->expire <= queue_current_time())
651 ++t->runcount;
652 queue_move_timer(
653 t, t->period ? queue_current_time() + t->period : EXPIRE_NEVER,
654 FALSE);
657 else
658 t = NULL;
659 RtlLeaveCriticalSection(&q->cs);
661 if (t)
663 if (t->flags & WT_EXECUTEINTIMERTHREAD)
664 timer_callback_wrapper(t);
665 else
667 ULONG flags
668 = (t->flags
669 & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD
670 | WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION));
671 NTSTATUS status = RtlQueueWorkItem(timer_callback_wrapper, t, flags);
672 if (status != STATUS_SUCCESS)
673 timer_cleanup_callback(t);
678 static ULONG queue_get_timeout(struct timer_queue *q)
680 struct queue_timer *t;
681 ULONG timeout = INFINITE;
683 RtlEnterCriticalSection(&q->cs);
684 if (list_head(&q->timers))
686 t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry);
687 assert(!t->destroy || t->expire == EXPIRE_NEVER);
689 if (t->expire != EXPIRE_NEVER)
691 ULONGLONG time = queue_current_time();
692 timeout = t->expire < time ? 0 : t->expire - time;
695 RtlLeaveCriticalSection(&q->cs);
697 return timeout;
700 static void WINAPI timer_queue_thread_proc(LPVOID p)
702 struct timer_queue *q = p;
703 ULONG timeout_ms;
705 timeout_ms = INFINITE;
706 for (;;)
708 LARGE_INTEGER timeout;
709 NTSTATUS status;
710 BOOL done = FALSE;
712 status = NtWaitForSingleObject(
713 q->event, FALSE, get_nt_timeout(&timeout, timeout_ms));
715 if (status == STATUS_WAIT_0)
717 /* There are two possible ways to trigger the event. Either
718 we are quitting and the last timer got removed, or a new
719 timer got put at the head of the list so we need to adjust
720 our timeout. */
721 RtlEnterCriticalSection(&q->cs);
722 if (q->quit && list_count(&q->timers) == 0)
723 done = TRUE;
724 RtlLeaveCriticalSection(&q->cs);
726 else if (status == STATUS_TIMEOUT)
727 queue_timer_expire(q);
729 if (done)
730 break;
732 timeout_ms = queue_get_timeout(q);
735 NtClose(q->event);
736 RtlDeleteCriticalSection(&q->cs);
737 RtlFreeHeap(GetProcessHeap(), 0, q);
740 static void queue_destroy_timer(struct queue_timer *t)
742 /* We MUST hold the queue cs while calling this function. */
743 t->destroy = TRUE;
744 if (t->runcount == 0)
745 /* Ensure a timer is promptly removed. If callbacks are pending,
746 it will be removed after the last one finishes by the callback
747 cleanup wrapper. */
748 queue_remove_timer(t);
749 else
750 /* Make sure no destroyed timer masks an active timer at the head
751 of the sorted list. */
752 queue_move_timer(t, EXPIRE_NEVER, FALSE);
755 /***********************************************************************
756 * RtlCreateTimerQueue (NTDLL.@)
758 * Creates a timer queue object and returns a handle to it.
760 * PARAMS
761 * NewTimerQueue [O] The newly created queue.
763 * RETURNS
764 * Success: STATUS_SUCCESS.
765 * Failure: Any NTSTATUS code.
767 NTSTATUS WINAPI RtlCreateTimerQueue(PHANDLE NewTimerQueue)
769 NTSTATUS status;
770 struct timer_queue *q = RtlAllocateHeap(GetProcessHeap(), 0, sizeof *q);
771 if (!q)
772 return STATUS_NO_MEMORY;
774 RtlInitializeCriticalSection(&q->cs);
775 list_init(&q->timers);
776 q->quit = FALSE;
777 status = NtCreateEvent(&q->event, EVENT_ALL_ACCESS, NULL, FALSE, FALSE);
778 if (status != STATUS_SUCCESS)
780 RtlFreeHeap(GetProcessHeap(), 0, q);
781 return status;
783 status = RtlCreateUserThread(GetCurrentProcess(), NULL, FALSE, NULL, 0, 0,
784 timer_queue_thread_proc, q, &q->thread, NULL);
785 if (status != STATUS_SUCCESS)
787 NtClose(q->event);
788 RtlFreeHeap(GetProcessHeap(), 0, q);
789 return status;
792 *NewTimerQueue = q;
793 return STATUS_SUCCESS;
796 /***********************************************************************
797 * RtlDeleteTimerQueueEx (NTDLL.@)
799 * Deletes a timer queue object.
801 * PARAMS
802 * TimerQueue [I] The timer queue to destroy.
803 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
804 * wait until all timers are finished firing before
805 * returning. Otherwise, return immediately and set the
806 * event when all timers are done.
808 * RETURNS
809 * Success: STATUS_SUCCESS if synchronous, STATUS_PENDING if not.
810 * Failure: Any NTSTATUS code.
812 NTSTATUS WINAPI RtlDeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent)
814 struct timer_queue *q = TimerQueue;
815 struct queue_timer *t, *temp;
816 HANDLE thread = q->thread;
817 NTSTATUS status;
819 RtlEnterCriticalSection(&q->cs);
820 q->quit = TRUE;
821 if (list_head(&q->timers))
822 /* When the last timer is removed, it will signal the timer thread to
823 exit... */
824 LIST_FOR_EACH_ENTRY_SAFE(t, temp, &q->timers, struct queue_timer, entry)
825 queue_destroy_timer(t);
826 else
827 /* However if we have none, we must do it ourselves. */
828 NtSetEvent(q->event, NULL);
829 RtlLeaveCriticalSection(&q->cs);
831 if (CompletionEvent == INVALID_HANDLE_VALUE)
833 NtWaitForSingleObject(thread, FALSE, NULL);
834 status = STATUS_SUCCESS;
836 else
838 if (CompletionEvent)
840 FIXME("asynchronous return on completion event unimplemented\n");
841 NtWaitForSingleObject(thread, FALSE, NULL);
842 NtSetEvent(CompletionEvent, NULL);
844 status = STATUS_PENDING;
847 NtClose(thread);
848 return status;
851 /***********************************************************************
852 * RtlCreateTimer (NTDLL.@)
854 * Creates a new timer associated with the given queue.
856 * PARAMS
857 * NewTimer [O] The newly created timer.
858 * TimerQueue [I] The queue to hold the timer.
859 * Callback [I] The callback to fire.
860 * Parameter [I] The argument for the callback.
861 * DueTime [I] The delay, in milliseconds, before first firing the
862 * timer.
863 * Period [I] The period, in milliseconds, at which to fire the timer
864 * after the first callback. If zero, the timer will only
865 * fire once. It still needs to be deleted with
866 * RtlDeleteTimer.
867 * Flags [I] Flags controling the execution of the callback. In
868 * addition to the WT_* thread pool flags (see
869 * RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and
870 * WT_EXECUTEONLYONCE are supported.
872 * RETURNS
873 * Success: STATUS_SUCCESS.
874 * Failure: Any NTSTATUS code.
876 NTSTATUS WINAPI RtlCreateTimer(PHANDLE NewTimer, HANDLE TimerQueue,
877 RTL_WAITORTIMERCALLBACKFUNC Callback,
878 PVOID Parameter, DWORD DueTime, DWORD Period,
879 ULONG Flags)
881 NTSTATUS status;
882 struct timer_queue *q = TimerQueue;
883 struct queue_timer *t = RtlAllocateHeap(GetProcessHeap(), 0, sizeof *t);
884 if (!t)
885 return STATUS_NO_MEMORY;
887 t->q = q;
888 t->runcount = 0;
889 t->callback = Callback;
890 t->param = Parameter;
891 t->period = Period;
892 t->flags = Flags;
893 t->destroy = FALSE;
895 status = STATUS_SUCCESS;
896 RtlEnterCriticalSection(&q->cs);
897 if (q->quit)
898 status = STATUS_INVALID_HANDLE;
899 else
900 queue_add_timer(t, queue_current_time() + DueTime, TRUE);
901 RtlLeaveCriticalSection(&q->cs);
903 if (status == STATUS_SUCCESS)
904 *NewTimer = t;
905 else
906 RtlFreeHeap(GetProcessHeap(), 0, t);
908 return status;