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, LONG_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
);
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.
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.
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
);
288 NTSTATUS res
= STATUS_SUCCESS
;
290 RtlEnterCriticalSection(&threadpool_compl_cs
);
295 res
= NtCreateIoCompletion( &cport
, IO_COMPLETION_ALL_ACCESS
, NULL
, 0 );
298 /* FIXME native can start additional threads in case of e.g. hung callback function. */
299 res
= RtlQueueWorkItem( iocp_poller
, NULL
, WT_EXECUTEDEFAULT
);
306 RtlLeaveCriticalSection(&threadpool_compl_cs
);
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;
323 struct wait_work_item
327 WAITORTIMERCALLBACK Callback
;
331 HANDLE CompletionEvent
;
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
;
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
;
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
;
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
)
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
);
395 /***********************************************************************
396 * RtlRegisterWait (NTDLL.@)
398 * Registers a wait for a handle to become signaled.
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.
409 * Success: STATUS_SUCCESS.
410 * Failure: Any NTSTATUS code.
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
;
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
) );
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
);
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
);
456 *NewWaitObject
= wait_work_item
;
460 /***********************************************************************
461 * RtlDeregisterWaitEx (NTDLL.@)
463 * Cancels a wait operation and frees the resources associated with calling
467 * WaitObject [I] Handle to the wait object to free.
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
)
490 interlocked_xchg_ptr( &wait_work_item
->CompletionEvent
, CompletionEvent
);
491 if (wait_work_item
->CallbackInProgress
)
492 NtWaitForSingleObject( CompletionEvent
, FALSE
, NULL
);
493 NtClose( CompletionEvent
);
497 interlocked_xchg_ptr( &wait_work_item
->CompletionEvent
, CompletionEvent
);
498 if (wait_work_item
->CallbackInProgress
)
499 status
= STATUS_PENDING
;
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
);
515 /***********************************************************************
516 * RtlDeregisterWait (NTDLL.@)
518 * Cancels a wait operation and frees the resources associated with calling
522 * WaitObject [I] Handle to the wait object to free.
525 * Success: STATUS_SUCCESS.
526 * Failure: Any NTSTATUS code.
528 NTSTATUS WINAPI
RtlDeregisterWait(HANDLE WaitHandle
)
530 return RtlDeregisterWaitEx(WaitHandle
, NULL
);
534 /************************** Timer Queue Impl **************************/
539 struct timer_queue
*q
;
541 ULONG runcount
; /* number of callbacks pending execution */
542 RTL_WAITORTIMERCALLBACKFUNC callback
;
547 BOOL destroy
; /* timer should be deleted; once set, never unset */
552 RTL_CRITICAL_SECTION cs
;
553 struct list timers
; /* sorted by expiration time */
554 BOOL quit
; /* queue should be deleted; once set, never unset */
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);
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
);
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
);
600 static inline ULONGLONG
queue_current_time(void)
603 NtQuerySystemTime(&now
);
604 return now
.QuadPart
/ 10000;
607 static void queue_add_timer(struct queue_timer
*t
, ULONGLONG time
,
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
)
623 list_add_before(ptr
, &t
->entry
);
627 /* If we insert at the head of the list, we need to expire sooner
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
,
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())
653 t
, t
->period
? queue_current_time() + t
->period
: EXPIRE_NEVER
,
659 RtlLeaveCriticalSection(&q
->cs
);
663 if (t
->flags
& WT_EXECUTEINTIMERTHREAD
)
664 timer_callback_wrapper(t
);
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
);
700 static void WINAPI
timer_queue_thread_proc(LPVOID p
)
702 struct timer_queue
*q
= p
;
705 timeout_ms
= INFINITE
;
708 LARGE_INTEGER timeout
;
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
721 RtlEnterCriticalSection(&q
->cs
);
722 if (q
->quit
&& list_count(&q
->timers
) == 0)
724 RtlLeaveCriticalSection(&q
->cs
);
726 else if (status
== STATUS_TIMEOUT
)
727 queue_timer_expire(q
);
732 timeout_ms
= queue_get_timeout(q
);
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. */
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
748 queue_remove_timer(t
);
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.
761 * NewTimerQueue [O] The newly created queue.
764 * Success: STATUS_SUCCESS.
765 * Failure: Any NTSTATUS code.
767 NTSTATUS WINAPI
RtlCreateTimerQueue(PHANDLE NewTimerQueue
)
770 struct timer_queue
*q
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof *q
);
772 return STATUS_NO_MEMORY
;
774 RtlInitializeCriticalSection(&q
->cs
);
775 list_init(&q
->timers
);
777 status
= NtCreateEvent(&q
->event
, EVENT_ALL_ACCESS
, NULL
, FALSE
, FALSE
);
778 if (status
!= STATUS_SUCCESS
)
780 RtlFreeHeap(GetProcessHeap(), 0, q
);
783 status
= RtlCreateUserThread(GetCurrentProcess(), NULL
, FALSE
, NULL
, 0, 0,
784 timer_queue_thread_proc
, q
, &q
->thread
, NULL
);
785 if (status
!= STATUS_SUCCESS
)
788 RtlFreeHeap(GetProcessHeap(), 0, q
);
793 return STATUS_SUCCESS
;
796 /***********************************************************************
797 * RtlDeleteTimerQueueEx (NTDLL.@)
799 * Deletes a timer queue object.
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.
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
;
819 RtlEnterCriticalSection(&q
->cs
);
821 if (list_head(&q
->timers
))
822 /* When the last timer is removed, it will signal the timer thread to
824 LIST_FOR_EACH_ENTRY_SAFE(t
, temp
, &q
->timers
, struct queue_timer
, entry
)
825 queue_destroy_timer(t
);
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
;
840 FIXME("asynchronous return on completion event unimplemented\n");
841 NtWaitForSingleObject(thread
, FALSE
, NULL
);
842 NtSetEvent(CompletionEvent
, NULL
);
844 status
= STATUS_PENDING
;
851 /***********************************************************************
852 * RtlCreateTimer (NTDLL.@)
854 * Creates a new timer associated with the given queue.
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
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
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.
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
,
882 struct timer_queue
*q
= TimerQueue
;
883 struct queue_timer
*t
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof *t
);
885 return STATUS_NO_MEMORY
;
889 t
->callback
= Callback
;
890 t
->param
= Parameter
;
895 status
= STATUS_SUCCESS
;
896 RtlEnterCriticalSection(&q
->cs
);
898 status
= STATUS_INVALID_HANDLE
;
900 queue_add_timer(t
, queue_current_time() + DueTime
, TRUE
);
901 RtlLeaveCriticalSection(&q
->cs
);
903 if (status
== STATUS_SUCCESS
)
906 RtlFreeHeap(GetProcessHeap(), 0, t
);