user32: Get rid of the unused parameter in the EmptyClipboard driver entry point.
[wine/multimedia.git] / dlls / ntdll / threadpool.c
blob513c13d6ab77ec488464821bae4b344a8bf31b11
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 OLD_WORKER_TIMEOUT 30000 /* 30 seconds */
41 #define EXPIRE_NEVER (~(ULONGLONG)0)
42 #define TIMER_QUEUE_MAGIC 0x516d6954 /* TimQ */
44 static RTL_CRITICAL_SECTION_DEBUG critsect_debug;
45 static RTL_CRITICAL_SECTION_DEBUG critsect_compl_debug;
47 static struct
49 /* threadpool_cs must be held while modifying the following four elements */
50 struct list work_item_list;
51 LONG num_workers;
52 LONG num_busy_workers;
53 LONG num_items_processed;
54 RTL_CONDITION_VARIABLE threadpool_cond;
55 RTL_CRITICAL_SECTION threadpool_cs;
56 HANDLE compl_port;
57 RTL_CRITICAL_SECTION threadpool_compl_cs;
59 old_threadpool =
61 LIST_INIT(old_threadpool.work_item_list), /* work_item_list */
62 0, /* num_workers */
63 0, /* num_busy_workers */
64 0, /* num_items_processed */
65 RTL_CONDITION_VARIABLE_INIT, /* threadpool_cond */
66 { &critsect_debug, -1, 0, 0, 0, 0 }, /* threadpool_cs */
67 NULL, /* compl_port */
68 { &critsect_compl_debug, -1, 0, 0, 0, 0 }, /* threadpool_compl_cs */
71 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
73 0, 0, &old_threadpool.threadpool_cs,
74 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
75 0, 0, { (DWORD_PTR)(__FILE__ ": threadpool_cs") }
78 static RTL_CRITICAL_SECTION_DEBUG critsect_compl_debug =
80 0, 0, &old_threadpool.threadpool_compl_cs,
81 { &critsect_compl_debug.ProcessLocksList, &critsect_compl_debug.ProcessLocksList },
82 0, 0, { (DWORD_PTR)(__FILE__ ": threadpool_compl_cs") }
85 struct work_item
87 struct list entry;
88 PRTL_WORK_ITEM_ROUTINE function;
89 PVOID context;
92 struct wait_work_item
94 HANDLE Object;
95 HANDLE CancelEvent;
96 WAITORTIMERCALLBACK Callback;
97 PVOID Context;
98 ULONG Milliseconds;
99 ULONG Flags;
100 HANDLE CompletionEvent;
101 LONG DeleteCount;
102 BOOLEAN CallbackInProgress;
105 struct timer_queue;
106 struct queue_timer
108 struct timer_queue *q;
109 struct list entry;
110 ULONG runcount; /* number of callbacks pending execution */
111 RTL_WAITORTIMERCALLBACKFUNC callback;
112 PVOID param;
113 DWORD period;
114 ULONG flags;
115 ULONGLONG expire;
116 BOOL destroy; /* timer should be deleted; once set, never unset */
117 HANDLE event; /* removal event */
120 struct timer_queue
122 DWORD magic;
123 RTL_CRITICAL_SECTION cs;
124 struct list timers; /* sorted by expiration time */
125 BOOL quit; /* queue should be deleted; once set, never unset */
126 HANDLE event;
127 HANDLE thread;
130 static inline LONG interlocked_inc( PLONG dest )
132 return interlocked_xchg_add( dest, 1 ) + 1;
135 static inline LONG interlocked_dec( PLONG dest )
137 return interlocked_xchg_add( dest, -1 ) - 1;
140 static void WINAPI worker_thread_proc(void * param)
142 struct list *item;
143 struct work_item *work_item_ptr, work_item;
144 LARGE_INTEGER timeout;
145 timeout.QuadPart = -(OLD_WORKER_TIMEOUT * (ULONGLONG)10000);
147 RtlEnterCriticalSection( &old_threadpool.threadpool_cs );
148 old_threadpool.num_workers++;
150 for (;;)
152 if ((item = list_head( &old_threadpool.work_item_list )))
154 work_item_ptr = LIST_ENTRY( item, struct work_item, entry );
155 list_remove( &work_item_ptr->entry );
156 old_threadpool.num_busy_workers++;
157 old_threadpool.num_items_processed++;
158 RtlLeaveCriticalSection( &old_threadpool.threadpool_cs );
160 /* copy item to stack and do the work */
161 work_item = *work_item_ptr;
162 RtlFreeHeap( GetProcessHeap(), 0, work_item_ptr );
163 TRACE("executing %p(%p)\n", work_item.function, work_item.context);
164 work_item.function( work_item.context );
166 RtlEnterCriticalSection( &old_threadpool.threadpool_cs );
167 old_threadpool.num_busy_workers--;
169 else if (RtlSleepConditionVariableCS( &old_threadpool.threadpool_cond,
170 &old_threadpool.threadpool_cs, &timeout ) != STATUS_SUCCESS)
172 break;
176 old_threadpool.num_workers--;
177 RtlLeaveCriticalSection( &old_threadpool.threadpool_cs );
178 RtlExitUserThread( 0 );
180 /* never reached */
183 /***********************************************************************
184 * RtlQueueWorkItem (NTDLL.@)
186 * Queues a work item into a thread in the thread pool.
188 * PARAMS
189 * Function [I] Work function to execute.
190 * Context [I] Context to pass to the work function when it is executed.
191 * Flags [I] Flags. See notes.
193 * RETURNS
194 * Success: STATUS_SUCCESS.
195 * Failure: Any NTSTATUS code.
197 * NOTES
198 * Flags can be one or more of the following:
199 *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread.
200 *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread.
201 *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent.
202 *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
203 *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
205 NTSTATUS WINAPI RtlQueueWorkItem(PRTL_WORK_ITEM_ROUTINE Function, PVOID Context, ULONG Flags)
207 HANDLE thread;
208 NTSTATUS status;
209 LONG items_processed;
210 struct work_item *work_item = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(struct work_item));
212 if (!work_item)
213 return STATUS_NO_MEMORY;
215 work_item->function = Function;
216 work_item->context = Context;
218 if (Flags & ~WT_EXECUTELONGFUNCTION)
219 FIXME("Flags 0x%x not supported\n", Flags);
221 RtlEnterCriticalSection( &old_threadpool.threadpool_cs );
222 list_add_tail( &old_threadpool.work_item_list, &work_item->entry );
223 status = (old_threadpool.num_workers > old_threadpool.num_busy_workers) ?
224 STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
225 items_processed = old_threadpool.num_items_processed;
226 RtlLeaveCriticalSection( &old_threadpool.threadpool_cs );
228 /* FIXME: tune this algorithm to not be as aggressive with creating threads
229 * if WT_EXECUTELONGFUNCTION isn't specified */
230 if (status == STATUS_SUCCESS)
231 RtlWakeConditionVariable( &old_threadpool.threadpool_cond );
232 else
234 status = RtlCreateUserThread( GetCurrentProcess(), NULL, FALSE, NULL, 0, 0,
235 worker_thread_proc, NULL, &thread, NULL );
237 /* NOTE: we don't care if we couldn't create the thread if there is at
238 * least one other available to process the request */
239 if (status == STATUS_SUCCESS)
240 NtClose( thread );
241 else
243 RtlEnterCriticalSection( &old_threadpool.threadpool_cs );
244 if (old_threadpool.num_workers > 0 ||
245 old_threadpool.num_items_processed != items_processed)
247 status = STATUS_SUCCESS;
249 else
250 list_remove( &work_item->entry );
251 RtlLeaveCriticalSection( &old_threadpool.threadpool_cs );
253 if (status != STATUS_SUCCESS)
254 RtlFreeHeap( GetProcessHeap(), 0, work_item );
258 return status;
261 /***********************************************************************
262 * iocp_poller - get completion events and run callbacks
264 static DWORD CALLBACK iocp_poller(LPVOID Arg)
266 HANDLE cport = Arg;
268 while( TRUE )
270 PRTL_OVERLAPPED_COMPLETION_ROUTINE callback;
271 LPVOID overlapped;
272 IO_STATUS_BLOCK iosb;
273 NTSTATUS res = NtRemoveIoCompletion( cport, (PULONG_PTR)&callback, (PULONG_PTR)&overlapped, &iosb, NULL );
274 if (res)
276 ERR("NtRemoveIoCompletion failed: 0x%x\n", res);
278 else
280 DWORD transferred = 0;
281 DWORD err = 0;
283 if (iosb.u.Status == STATUS_SUCCESS)
284 transferred = iosb.Information;
285 else
286 err = RtlNtStatusToDosError(iosb.u.Status);
288 callback( err, transferred, overlapped );
291 return 0;
294 /***********************************************************************
295 * RtlSetIoCompletionCallback (NTDLL.@)
297 * Binds a handle to a thread pool's completion port, and possibly
298 * starts a non-I/O thread to monitor this port and call functions back.
300 * PARAMS
301 * FileHandle [I] Handle to bind to a completion port.
302 * Function [I] Callback function to call on I/O completions.
303 * Flags [I] Not used.
305 * RETURNS
306 * Success: STATUS_SUCCESS.
307 * Failure: Any NTSTATUS code.
310 NTSTATUS WINAPI RtlSetIoCompletionCallback(HANDLE FileHandle, PRTL_OVERLAPPED_COMPLETION_ROUTINE Function, ULONG Flags)
312 IO_STATUS_BLOCK iosb;
313 FILE_COMPLETION_INFORMATION info;
315 if (Flags) FIXME("Unknown value Flags=0x%x\n", Flags);
317 if (!old_threadpool.compl_port)
319 NTSTATUS res = STATUS_SUCCESS;
321 RtlEnterCriticalSection(&old_threadpool.threadpool_compl_cs);
322 if (!old_threadpool.compl_port)
324 HANDLE cport;
326 res = NtCreateIoCompletion( &cport, IO_COMPLETION_ALL_ACCESS, NULL, 0 );
327 if (!res)
329 /* FIXME native can start additional threads in case of e.g. hung callback function. */
330 res = RtlQueueWorkItem( iocp_poller, cport, WT_EXECUTEDEFAULT );
331 if (!res)
332 old_threadpool.compl_port = cport;
333 else
334 NtClose( cport );
337 RtlLeaveCriticalSection(&old_threadpool.threadpool_compl_cs);
338 if (res) return res;
341 info.CompletionPort = old_threadpool.compl_port;
342 info.CompletionKey = (ULONG_PTR)Function;
344 return NtSetInformationFile( FileHandle, &iosb, &info, sizeof(info), FileCompletionInformation );
347 static inline PLARGE_INTEGER get_nt_timeout( PLARGE_INTEGER pTime, ULONG timeout )
349 if (timeout == INFINITE) return NULL;
350 pTime->QuadPart = (ULONGLONG)timeout * -10000;
351 return pTime;
354 static void delete_wait_work_item(struct wait_work_item *wait_work_item)
356 NtClose( wait_work_item->CancelEvent );
357 RtlFreeHeap( GetProcessHeap(), 0, wait_work_item );
360 static DWORD CALLBACK wait_thread_proc(LPVOID Arg)
362 struct wait_work_item *wait_work_item = Arg;
363 NTSTATUS status;
364 BOOLEAN alertable = (wait_work_item->Flags & WT_EXECUTEINIOTHREAD) != 0;
365 HANDLE handles[2] = { wait_work_item->Object, wait_work_item->CancelEvent };
366 LARGE_INTEGER timeout;
367 HANDLE completion_event;
369 TRACE("\n");
371 while (TRUE)
373 status = NtWaitForMultipleObjects( 2, handles, TRUE, alertable,
374 get_nt_timeout( &timeout, wait_work_item->Milliseconds ) );
375 if (status == STATUS_WAIT_0 || status == STATUS_TIMEOUT)
377 BOOLEAN TimerOrWaitFired;
379 if (status == STATUS_WAIT_0)
381 TRACE( "object %p signaled, calling callback %p with context %p\n",
382 wait_work_item->Object, wait_work_item->Callback,
383 wait_work_item->Context );
384 TimerOrWaitFired = FALSE;
386 else
388 TRACE( "wait for object %p timed out, calling callback %p with context %p\n",
389 wait_work_item->Object, wait_work_item->Callback,
390 wait_work_item->Context );
391 TimerOrWaitFired = TRUE;
393 wait_work_item->CallbackInProgress = TRUE;
394 wait_work_item->Callback( wait_work_item->Context, TimerOrWaitFired );
395 wait_work_item->CallbackInProgress = FALSE;
397 if (wait_work_item->Flags & WT_EXECUTEONLYONCE)
398 break;
400 else
401 break;
404 completion_event = wait_work_item->CompletionEvent;
405 if (completion_event) NtSetEvent( completion_event, NULL );
407 if (interlocked_inc( &wait_work_item->DeleteCount ) == 2 )
408 delete_wait_work_item( wait_work_item );
410 return 0;
413 /***********************************************************************
414 * RtlRegisterWait (NTDLL.@)
416 * Registers a wait for a handle to become signaled.
418 * PARAMS
419 * NewWaitObject [I] Handle to the new wait object. Use RtlDeregisterWait() to free it.
420 * Object [I] Object to wait to become signaled.
421 * Callback [I] Callback function to execute when the wait times out or the handle is signaled.
422 * Context [I] Context to pass to the callback function when it is executed.
423 * Milliseconds [I] Number of milliseconds to wait before timing out.
424 * Flags [I] Flags. See notes.
426 * RETURNS
427 * Success: STATUS_SUCCESS.
428 * Failure: Any NTSTATUS code.
430 * NOTES
431 * Flags can be one or more of the following:
432 *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread.
433 *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread.
434 *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent.
435 *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
436 *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
438 NTSTATUS WINAPI RtlRegisterWait(PHANDLE NewWaitObject, HANDLE Object,
439 RTL_WAITORTIMERCALLBACKFUNC Callback,
440 PVOID Context, ULONG Milliseconds, ULONG Flags)
442 struct wait_work_item *wait_work_item;
443 NTSTATUS status;
445 TRACE( "(%p, %p, %p, %p, %d, 0x%x)\n", NewWaitObject, Object, Callback, Context, Milliseconds, Flags );
447 wait_work_item = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*wait_work_item) );
448 if (!wait_work_item)
449 return STATUS_NO_MEMORY;
451 wait_work_item->Object = Object;
452 wait_work_item->Callback = Callback;
453 wait_work_item->Context = Context;
454 wait_work_item->Milliseconds = Milliseconds;
455 wait_work_item->Flags = Flags;
456 wait_work_item->CallbackInProgress = FALSE;
457 wait_work_item->DeleteCount = 0;
458 wait_work_item->CompletionEvent = NULL;
460 status = NtCreateEvent( &wait_work_item->CancelEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
461 if (status != STATUS_SUCCESS)
463 RtlFreeHeap( GetProcessHeap(), 0, wait_work_item );
464 return status;
467 Flags = Flags & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD |
468 WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION);
469 status = RtlQueueWorkItem( wait_thread_proc, wait_work_item, Flags );
470 if (status != STATUS_SUCCESS)
472 delete_wait_work_item( wait_work_item );
473 return status;
476 *NewWaitObject = wait_work_item;
477 return status;
480 /***********************************************************************
481 * RtlDeregisterWaitEx (NTDLL.@)
483 * Cancels a wait operation and frees the resources associated with calling
484 * RtlRegisterWait().
486 * PARAMS
487 * WaitObject [I] Handle to the wait object to free.
489 * RETURNS
490 * Success: STATUS_SUCCESS.
491 * Failure: Any NTSTATUS code.
493 NTSTATUS WINAPI RtlDeregisterWaitEx(HANDLE WaitHandle, HANDLE CompletionEvent)
495 struct wait_work_item *wait_work_item = WaitHandle;
496 NTSTATUS status = STATUS_SUCCESS;
498 TRACE( "(%p)\n", WaitHandle );
500 NtSetEvent( wait_work_item->CancelEvent, NULL );
501 if (wait_work_item->CallbackInProgress)
503 if (CompletionEvent != NULL)
505 if (CompletionEvent == INVALID_HANDLE_VALUE)
507 status = NtCreateEvent( &CompletionEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
508 if (status != STATUS_SUCCESS)
509 return status;
510 interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
511 if (wait_work_item->CallbackInProgress)
512 NtWaitForSingleObject( CompletionEvent, FALSE, NULL );
513 NtClose( CompletionEvent );
515 else
517 interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
518 if (wait_work_item->CallbackInProgress)
519 status = STATUS_PENDING;
522 else
523 status = STATUS_PENDING;
526 if (interlocked_inc( &wait_work_item->DeleteCount ) == 2 )
528 status = STATUS_SUCCESS;
529 delete_wait_work_item( wait_work_item );
532 return status;
535 /***********************************************************************
536 * RtlDeregisterWait (NTDLL.@)
538 * Cancels a wait operation and frees the resources associated with calling
539 * RtlRegisterWait().
541 * PARAMS
542 * WaitObject [I] Handle to the wait object to free.
544 * RETURNS
545 * Success: STATUS_SUCCESS.
546 * Failure: Any NTSTATUS code.
548 NTSTATUS WINAPI RtlDeregisterWait(HANDLE WaitHandle)
550 return RtlDeregisterWaitEx(WaitHandle, NULL);
554 /************************** Timer Queue Impl **************************/
556 static void queue_remove_timer(struct queue_timer *t)
558 /* We MUST hold the queue cs while calling this function. This ensures
559 that we cannot queue another callback for this timer. The runcount
560 being zero makes sure we don't have any already queued. */
561 struct timer_queue *q = t->q;
563 assert(t->runcount == 0);
564 assert(t->destroy);
566 list_remove(&t->entry);
567 if (t->event)
568 NtSetEvent(t->event, NULL);
569 RtlFreeHeap(GetProcessHeap(), 0, t);
571 if (q->quit && list_empty(&q->timers))
572 NtSetEvent(q->event, NULL);
575 static void timer_cleanup_callback(struct queue_timer *t)
577 struct timer_queue *q = t->q;
578 RtlEnterCriticalSection(&q->cs);
580 assert(0 < t->runcount);
581 --t->runcount;
583 if (t->destroy && t->runcount == 0)
584 queue_remove_timer(t);
586 RtlLeaveCriticalSection(&q->cs);
589 static DWORD WINAPI timer_callback_wrapper(LPVOID p)
591 struct queue_timer *t = p;
592 t->callback(t->param, TRUE);
593 timer_cleanup_callback(t);
594 return 0;
597 static inline ULONGLONG queue_current_time(void)
599 LARGE_INTEGER now, freq;
600 NtQueryPerformanceCounter(&now, &freq);
601 return now.QuadPart * 1000 / freq.QuadPart;
604 static void queue_add_timer(struct queue_timer *t, ULONGLONG time,
605 BOOL set_event)
607 /* We MUST hold the queue cs while calling this function. */
608 struct timer_queue *q = t->q;
609 struct list *ptr = &q->timers;
611 assert(!q->quit || (t->destroy && time == EXPIRE_NEVER));
613 if (time != EXPIRE_NEVER)
614 LIST_FOR_EACH(ptr, &q->timers)
616 struct queue_timer *cur = LIST_ENTRY(ptr, struct queue_timer, entry);
617 if (time < cur->expire)
618 break;
620 list_add_before(ptr, &t->entry);
622 t->expire = time;
624 /* If we insert at the head of the list, we need to expire sooner
625 than expected. */
626 if (set_event && &t->entry == list_head(&q->timers))
627 NtSetEvent(q->event, NULL);
630 static inline void queue_move_timer(struct queue_timer *t, ULONGLONG time,
631 BOOL set_event)
633 /* We MUST hold the queue cs while calling this function. */
634 list_remove(&t->entry);
635 queue_add_timer(t, time, set_event);
638 static void queue_timer_expire(struct timer_queue *q)
640 struct queue_timer *t = NULL;
642 RtlEnterCriticalSection(&q->cs);
643 if (list_head(&q->timers))
645 ULONGLONG now, next;
646 t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry);
647 if (!t->destroy && t->expire <= ((now = queue_current_time())))
649 ++t->runcount;
650 if (t->period)
652 next = t->expire + t->period;
653 /* avoid trigger cascade if overloaded / hibernated */
654 if (next < now)
655 next = now + t->period;
657 else
658 next = EXPIRE_NEVER;
659 queue_move_timer(t, next, FALSE);
661 else
662 t = NULL;
664 RtlLeaveCriticalSection(&q->cs);
666 if (t)
668 if (t->flags & WT_EXECUTEINTIMERTHREAD)
669 timer_callback_wrapper(t);
670 else
672 ULONG flags
673 = (t->flags
674 & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD
675 | WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION));
676 NTSTATUS status = RtlQueueWorkItem(timer_callback_wrapper, t, flags);
677 if (status != STATUS_SUCCESS)
678 timer_cleanup_callback(t);
683 static ULONG queue_get_timeout(struct timer_queue *q)
685 struct queue_timer *t;
686 ULONG timeout = INFINITE;
688 RtlEnterCriticalSection(&q->cs);
689 if (list_head(&q->timers))
691 t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry);
692 assert(!t->destroy || t->expire == EXPIRE_NEVER);
694 if (t->expire != EXPIRE_NEVER)
696 ULONGLONG time = queue_current_time();
697 timeout = t->expire < time ? 0 : t->expire - time;
700 RtlLeaveCriticalSection(&q->cs);
702 return timeout;
705 static void WINAPI timer_queue_thread_proc(LPVOID p)
707 struct timer_queue *q = p;
708 ULONG timeout_ms;
710 timeout_ms = INFINITE;
711 for (;;)
713 LARGE_INTEGER timeout;
714 NTSTATUS status;
715 BOOL done = FALSE;
717 status = NtWaitForSingleObject(
718 q->event, FALSE, get_nt_timeout(&timeout, timeout_ms));
720 if (status == STATUS_WAIT_0)
722 /* There are two possible ways to trigger the event. Either
723 we are quitting and the last timer got removed, or a new
724 timer got put at the head of the list so we need to adjust
725 our timeout. */
726 RtlEnterCriticalSection(&q->cs);
727 if (q->quit && list_empty(&q->timers))
728 done = TRUE;
729 RtlLeaveCriticalSection(&q->cs);
731 else if (status == STATUS_TIMEOUT)
732 queue_timer_expire(q);
734 if (done)
735 break;
737 timeout_ms = queue_get_timeout(q);
740 NtClose(q->event);
741 RtlDeleteCriticalSection(&q->cs);
742 q->magic = 0;
743 RtlFreeHeap(GetProcessHeap(), 0, q);
746 static void queue_destroy_timer(struct queue_timer *t)
748 /* We MUST hold the queue cs while calling this function. */
749 t->destroy = TRUE;
750 if (t->runcount == 0)
751 /* Ensure a timer is promptly removed. If callbacks are pending,
752 it will be removed after the last one finishes by the callback
753 cleanup wrapper. */
754 queue_remove_timer(t);
755 else
756 /* Make sure no destroyed timer masks an active timer at the head
757 of the sorted list. */
758 queue_move_timer(t, EXPIRE_NEVER, FALSE);
761 /***********************************************************************
762 * RtlCreateTimerQueue (NTDLL.@)
764 * Creates a timer queue object and returns a handle to it.
766 * PARAMS
767 * NewTimerQueue [O] The newly created queue.
769 * RETURNS
770 * Success: STATUS_SUCCESS.
771 * Failure: Any NTSTATUS code.
773 NTSTATUS WINAPI RtlCreateTimerQueue(PHANDLE NewTimerQueue)
775 NTSTATUS status;
776 struct timer_queue *q = RtlAllocateHeap(GetProcessHeap(), 0, sizeof *q);
777 if (!q)
778 return STATUS_NO_MEMORY;
780 RtlInitializeCriticalSection(&q->cs);
781 list_init(&q->timers);
782 q->quit = FALSE;
783 q->magic = TIMER_QUEUE_MAGIC;
784 status = NtCreateEvent(&q->event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
785 if (status != STATUS_SUCCESS)
787 RtlFreeHeap(GetProcessHeap(), 0, q);
788 return status;
790 status = RtlCreateUserThread(GetCurrentProcess(), NULL, FALSE, NULL, 0, 0,
791 timer_queue_thread_proc, q, &q->thread, NULL);
792 if (status != STATUS_SUCCESS)
794 NtClose(q->event);
795 RtlFreeHeap(GetProcessHeap(), 0, q);
796 return status;
799 *NewTimerQueue = q;
800 return STATUS_SUCCESS;
803 /***********************************************************************
804 * RtlDeleteTimerQueueEx (NTDLL.@)
806 * Deletes a timer queue object.
808 * PARAMS
809 * TimerQueue [I] The timer queue to destroy.
810 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
811 * wait until all timers are finished firing before
812 * returning. Otherwise, return immediately and set the
813 * event when all timers are done.
815 * RETURNS
816 * Success: STATUS_SUCCESS if synchronous, STATUS_PENDING if not.
817 * Failure: Any NTSTATUS code.
819 NTSTATUS WINAPI RtlDeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent)
821 struct timer_queue *q = TimerQueue;
822 struct queue_timer *t, *temp;
823 HANDLE thread;
824 NTSTATUS status;
826 if (!q || q->magic != TIMER_QUEUE_MAGIC)
827 return STATUS_INVALID_HANDLE;
829 thread = q->thread;
831 RtlEnterCriticalSection(&q->cs);
832 q->quit = TRUE;
833 if (list_head(&q->timers))
834 /* When the last timer is removed, it will signal the timer thread to
835 exit... */
836 LIST_FOR_EACH_ENTRY_SAFE(t, temp, &q->timers, struct queue_timer, entry)
837 queue_destroy_timer(t);
838 else
839 /* However if we have none, we must do it ourselves. */
840 NtSetEvent(q->event, NULL);
841 RtlLeaveCriticalSection(&q->cs);
843 if (CompletionEvent == INVALID_HANDLE_VALUE)
845 NtWaitForSingleObject(thread, FALSE, NULL);
846 status = STATUS_SUCCESS;
848 else
850 if (CompletionEvent)
852 FIXME("asynchronous return on completion event unimplemented\n");
853 NtWaitForSingleObject(thread, FALSE, NULL);
854 NtSetEvent(CompletionEvent, NULL);
856 status = STATUS_PENDING;
859 NtClose(thread);
860 return status;
863 static struct timer_queue *get_timer_queue(HANDLE TimerQueue)
865 static struct timer_queue *default_timer_queue;
867 if (TimerQueue)
868 return TimerQueue;
869 else
871 if (!default_timer_queue)
873 HANDLE q;
874 NTSTATUS status = RtlCreateTimerQueue(&q);
875 if (status == STATUS_SUCCESS)
877 PVOID p = interlocked_cmpxchg_ptr(
878 (void **) &default_timer_queue, q, NULL);
879 if (p)
880 /* Got beat to the punch. */
881 RtlDeleteTimerQueueEx(q, NULL);
884 return default_timer_queue;
888 /***********************************************************************
889 * RtlCreateTimer (NTDLL.@)
891 * Creates a new timer associated with the given queue.
893 * PARAMS
894 * NewTimer [O] The newly created timer.
895 * TimerQueue [I] The queue to hold the timer.
896 * Callback [I] The callback to fire.
897 * Parameter [I] The argument for the callback.
898 * DueTime [I] The delay, in milliseconds, before first firing the
899 * timer.
900 * Period [I] The period, in milliseconds, at which to fire the timer
901 * after the first callback. If zero, the timer will only
902 * fire once. It still needs to be deleted with
903 * RtlDeleteTimer.
904 * Flags [I] Flags controlling the execution of the callback. In
905 * addition to the WT_* thread pool flags (see
906 * RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and
907 * WT_EXECUTEONLYONCE are supported.
909 * RETURNS
910 * Success: STATUS_SUCCESS.
911 * Failure: Any NTSTATUS code.
913 NTSTATUS WINAPI RtlCreateTimer(PHANDLE NewTimer, HANDLE TimerQueue,
914 RTL_WAITORTIMERCALLBACKFUNC Callback,
915 PVOID Parameter, DWORD DueTime, DWORD Period,
916 ULONG Flags)
918 NTSTATUS status;
919 struct queue_timer *t;
920 struct timer_queue *q = get_timer_queue(TimerQueue);
922 if (!q) return STATUS_NO_MEMORY;
923 if (q->magic != TIMER_QUEUE_MAGIC) return STATUS_INVALID_HANDLE;
925 t = RtlAllocateHeap(GetProcessHeap(), 0, sizeof *t);
926 if (!t)
927 return STATUS_NO_MEMORY;
929 t->q = q;
930 t->runcount = 0;
931 t->callback = Callback;
932 t->param = Parameter;
933 t->period = Period;
934 t->flags = Flags;
935 t->destroy = FALSE;
936 t->event = NULL;
938 status = STATUS_SUCCESS;
939 RtlEnterCriticalSection(&q->cs);
940 if (q->quit)
941 status = STATUS_INVALID_HANDLE;
942 else
943 queue_add_timer(t, queue_current_time() + DueTime, TRUE);
944 RtlLeaveCriticalSection(&q->cs);
946 if (status == STATUS_SUCCESS)
947 *NewTimer = t;
948 else
949 RtlFreeHeap(GetProcessHeap(), 0, t);
951 return status;
954 /***********************************************************************
955 * RtlUpdateTimer (NTDLL.@)
957 * Changes the time at which a timer expires.
959 * PARAMS
960 * TimerQueue [I] The queue that holds the timer.
961 * Timer [I] The timer to update.
962 * DueTime [I] The delay, in milliseconds, before next firing the timer.
963 * Period [I] The period, in milliseconds, at which to fire the timer
964 * after the first callback. If zero, the timer will not
965 * refire once. It still needs to be deleted with
966 * RtlDeleteTimer.
968 * RETURNS
969 * Success: STATUS_SUCCESS.
970 * Failure: Any NTSTATUS code.
972 NTSTATUS WINAPI RtlUpdateTimer(HANDLE TimerQueue, HANDLE Timer,
973 DWORD DueTime, DWORD Period)
975 struct queue_timer *t = Timer;
976 struct timer_queue *q = t->q;
978 RtlEnterCriticalSection(&q->cs);
979 /* Can't change a timer if it was once-only or destroyed. */
980 if (t->expire != EXPIRE_NEVER)
982 t->period = Period;
983 queue_move_timer(t, queue_current_time() + DueTime, TRUE);
985 RtlLeaveCriticalSection(&q->cs);
987 return STATUS_SUCCESS;
990 /***********************************************************************
991 * RtlDeleteTimer (NTDLL.@)
993 * Cancels a timer-queue timer.
995 * PARAMS
996 * TimerQueue [I] The queue that holds the timer.
997 * Timer [I] The timer to update.
998 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
999 * wait until the timer is finished firing all pending
1000 * callbacks before returning. Otherwise, return
1001 * immediately and set the timer is done.
1003 * RETURNS
1004 * Success: STATUS_SUCCESS if the timer is done, STATUS_PENDING if not,
1005 or if the completion event is NULL.
1006 * Failure: Any NTSTATUS code.
1008 NTSTATUS WINAPI RtlDeleteTimer(HANDLE TimerQueue, HANDLE Timer,
1009 HANDLE CompletionEvent)
1011 struct queue_timer *t = Timer;
1012 struct timer_queue *q;
1013 NTSTATUS status = STATUS_PENDING;
1014 HANDLE event = NULL;
1016 if (!Timer)
1017 return STATUS_INVALID_PARAMETER_1;
1018 q = t->q;
1019 if (CompletionEvent == INVALID_HANDLE_VALUE)
1021 status = NtCreateEvent(&event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
1022 if (status == STATUS_SUCCESS)
1023 status = STATUS_PENDING;
1025 else if (CompletionEvent)
1026 event = CompletionEvent;
1028 RtlEnterCriticalSection(&q->cs);
1029 t->event = event;
1030 if (t->runcount == 0 && event)
1031 status = STATUS_SUCCESS;
1032 queue_destroy_timer(t);
1033 RtlLeaveCriticalSection(&q->cs);
1035 if (CompletionEvent == INVALID_HANDLE_VALUE && event)
1037 if (status == STATUS_PENDING)
1039 NtWaitForSingleObject(event, FALSE, NULL);
1040 status = STATUS_SUCCESS;
1042 NtClose(event);
1045 return status;