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 */
548 HANDLE event
; /* removal event */
553 RTL_CRITICAL_SECTION cs
;
554 struct list timers
; /* sorted by expiration time */
555 BOOL quit
; /* queue should be deleted; once set, never unset */
560 #define EXPIRE_NEVER (~(ULONGLONG) 0)
562 static void queue_remove_timer(struct queue_timer
*t
)
564 /* We MUST hold the queue cs while calling this function. This ensures
565 that we cannot queue another callback for this timer. The runcount
566 being zero makes sure we don't have any already queued. */
567 struct timer_queue
*q
= t
->q
;
569 assert(t
->runcount
== 0);
572 list_remove(&t
->entry
);
574 NtSetEvent(t
->event
, NULL
);
575 RtlFreeHeap(GetProcessHeap(), 0, t
);
577 if (q
->quit
&& list_count(&q
->timers
) == 0)
578 NtSetEvent(q
->event
, NULL
);
581 static void timer_cleanup_callback(struct queue_timer
*t
)
583 struct timer_queue
*q
= t
->q
;
584 RtlEnterCriticalSection(&q
->cs
);
586 assert(0 < t
->runcount
);
589 if (t
->destroy
&& t
->runcount
== 0)
590 queue_remove_timer(t
);
592 RtlLeaveCriticalSection(&q
->cs
);
595 static DWORD WINAPI
timer_callback_wrapper(LPVOID p
)
597 struct queue_timer
*t
= p
;
598 t
->callback(t
->param
, TRUE
);
599 timer_cleanup_callback(t
);
603 static inline ULONGLONG
queue_current_time(void)
606 NtQuerySystemTime(&now
);
607 return now
.QuadPart
/ 10000;
610 static void queue_add_timer(struct queue_timer
*t
, ULONGLONG time
,
613 /* We MUST hold the queue cs while calling this function. */
614 struct timer_queue
*q
= t
->q
;
615 struct list
*ptr
= &q
->timers
;
617 assert(!q
->quit
|| (t
->destroy
&& time
== EXPIRE_NEVER
));
619 if (time
!= EXPIRE_NEVER
)
620 LIST_FOR_EACH(ptr
, &q
->timers
)
622 struct queue_timer
*cur
= LIST_ENTRY(ptr
, struct queue_timer
, entry
);
623 if (time
< cur
->expire
)
626 list_add_before(ptr
, &t
->entry
);
630 /* If we insert at the head of the list, we need to expire sooner
632 if (set_event
&& &t
->entry
== list_head(&q
->timers
))
633 NtSetEvent(q
->event
, NULL
);
636 static inline void queue_move_timer(struct queue_timer
*t
, ULONGLONG time
,
639 /* We MUST hold the queue cs while calling this function. */
640 list_remove(&t
->entry
);
641 queue_add_timer(t
, time
, set_event
);
644 static void queue_timer_expire(struct timer_queue
*q
)
646 struct queue_timer
*t
;
648 RtlEnterCriticalSection(&q
->cs
);
649 if (list_head(&q
->timers
))
651 t
= LIST_ENTRY(list_head(&q
->timers
), struct queue_timer
, entry
);
652 if (!t
->destroy
&& t
->expire
<= queue_current_time())
656 t
, t
->period
? queue_current_time() + t
->period
: EXPIRE_NEVER
,
662 RtlLeaveCriticalSection(&q
->cs
);
666 if (t
->flags
& WT_EXECUTEINTIMERTHREAD
)
667 timer_callback_wrapper(t
);
672 & (WT_EXECUTEINIOTHREAD
| WT_EXECUTEINPERSISTENTTHREAD
673 | WT_EXECUTELONGFUNCTION
| WT_TRANSFER_IMPERSONATION
));
674 NTSTATUS status
= RtlQueueWorkItem(timer_callback_wrapper
, t
, flags
);
675 if (status
!= STATUS_SUCCESS
)
676 timer_cleanup_callback(t
);
681 static ULONG
queue_get_timeout(struct timer_queue
*q
)
683 struct queue_timer
*t
;
684 ULONG timeout
= INFINITE
;
686 RtlEnterCriticalSection(&q
->cs
);
687 if (list_head(&q
->timers
))
689 t
= LIST_ENTRY(list_head(&q
->timers
), struct queue_timer
, entry
);
690 assert(!t
->destroy
|| t
->expire
== EXPIRE_NEVER
);
692 if (t
->expire
!= EXPIRE_NEVER
)
694 ULONGLONG time
= queue_current_time();
695 timeout
= t
->expire
< time
? 0 : t
->expire
- time
;
698 RtlLeaveCriticalSection(&q
->cs
);
703 static void WINAPI
timer_queue_thread_proc(LPVOID p
)
705 struct timer_queue
*q
= p
;
708 timeout_ms
= INFINITE
;
711 LARGE_INTEGER timeout
;
715 status
= NtWaitForSingleObject(
716 q
->event
, FALSE
, get_nt_timeout(&timeout
, timeout_ms
));
718 if (status
== STATUS_WAIT_0
)
720 /* There are two possible ways to trigger the event. Either
721 we are quitting and the last timer got removed, or a new
722 timer got put at the head of the list so we need to adjust
724 RtlEnterCriticalSection(&q
->cs
);
725 if (q
->quit
&& list_count(&q
->timers
) == 0)
727 RtlLeaveCriticalSection(&q
->cs
);
729 else if (status
== STATUS_TIMEOUT
)
730 queue_timer_expire(q
);
735 timeout_ms
= queue_get_timeout(q
);
739 RtlDeleteCriticalSection(&q
->cs
);
740 RtlFreeHeap(GetProcessHeap(), 0, q
);
743 static void queue_destroy_timer(struct queue_timer
*t
)
745 /* We MUST hold the queue cs while calling this function. */
747 if (t
->runcount
== 0)
748 /* Ensure a timer is promptly removed. If callbacks are pending,
749 it will be removed after the last one finishes by the callback
751 queue_remove_timer(t
);
753 /* Make sure no destroyed timer masks an active timer at the head
754 of the sorted list. */
755 queue_move_timer(t
, EXPIRE_NEVER
, FALSE
);
758 /***********************************************************************
759 * RtlCreateTimerQueue (NTDLL.@)
761 * Creates a timer queue object and returns a handle to it.
764 * NewTimerQueue [O] The newly created queue.
767 * Success: STATUS_SUCCESS.
768 * Failure: Any NTSTATUS code.
770 NTSTATUS WINAPI
RtlCreateTimerQueue(PHANDLE NewTimerQueue
)
773 struct timer_queue
*q
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof *q
);
775 return STATUS_NO_MEMORY
;
777 RtlInitializeCriticalSection(&q
->cs
);
778 list_init(&q
->timers
);
780 status
= NtCreateEvent(&q
->event
, EVENT_ALL_ACCESS
, NULL
, FALSE
, FALSE
);
781 if (status
!= STATUS_SUCCESS
)
783 RtlFreeHeap(GetProcessHeap(), 0, q
);
786 status
= RtlCreateUserThread(GetCurrentProcess(), NULL
, FALSE
, NULL
, 0, 0,
787 timer_queue_thread_proc
, q
, &q
->thread
, NULL
);
788 if (status
!= STATUS_SUCCESS
)
791 RtlFreeHeap(GetProcessHeap(), 0, q
);
796 return STATUS_SUCCESS
;
799 /***********************************************************************
800 * RtlDeleteTimerQueueEx (NTDLL.@)
802 * Deletes a timer queue object.
805 * TimerQueue [I] The timer queue to destroy.
806 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
807 * wait until all timers are finished firing before
808 * returning. Otherwise, return immediately and set the
809 * event when all timers are done.
812 * Success: STATUS_SUCCESS if synchronous, STATUS_PENDING if not.
813 * Failure: Any NTSTATUS code.
815 NTSTATUS WINAPI
RtlDeleteTimerQueueEx(HANDLE TimerQueue
, HANDLE CompletionEvent
)
817 struct timer_queue
*q
= TimerQueue
;
818 struct queue_timer
*t
, *temp
;
819 HANDLE thread
= q
->thread
;
822 RtlEnterCriticalSection(&q
->cs
);
824 if (list_head(&q
->timers
))
825 /* When the last timer is removed, it will signal the timer thread to
827 LIST_FOR_EACH_ENTRY_SAFE(t
, temp
, &q
->timers
, struct queue_timer
, entry
)
828 queue_destroy_timer(t
);
830 /* However if we have none, we must do it ourselves. */
831 NtSetEvent(q
->event
, NULL
);
832 RtlLeaveCriticalSection(&q
->cs
);
834 if (CompletionEvent
== INVALID_HANDLE_VALUE
)
836 NtWaitForSingleObject(thread
, FALSE
, NULL
);
837 status
= STATUS_SUCCESS
;
843 FIXME("asynchronous return on completion event unimplemented\n");
844 NtWaitForSingleObject(thread
, FALSE
, NULL
);
845 NtSetEvent(CompletionEvent
, NULL
);
847 status
= STATUS_PENDING
;
854 /***********************************************************************
855 * RtlCreateTimer (NTDLL.@)
857 * Creates a new timer associated with the given queue.
860 * NewTimer [O] The newly created timer.
861 * TimerQueue [I] The queue to hold the timer.
862 * Callback [I] The callback to fire.
863 * Parameter [I] The argument for the callback.
864 * DueTime [I] The delay, in milliseconds, before first firing the
866 * Period [I] The period, in milliseconds, at which to fire the timer
867 * after the first callback. If zero, the timer will only
868 * fire once. It still needs to be deleted with
870 * Flags [I] Flags controling the execution of the callback. In
871 * addition to the WT_* thread pool flags (see
872 * RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and
873 * WT_EXECUTEONLYONCE are supported.
876 * Success: STATUS_SUCCESS.
877 * Failure: Any NTSTATUS code.
879 NTSTATUS WINAPI
RtlCreateTimer(PHANDLE NewTimer
, HANDLE TimerQueue
,
880 RTL_WAITORTIMERCALLBACKFUNC Callback
,
881 PVOID Parameter
, DWORD DueTime
, DWORD Period
,
885 struct timer_queue
*q
= TimerQueue
;
886 struct queue_timer
*t
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof *t
);
888 return STATUS_NO_MEMORY
;
892 t
->callback
= Callback
;
893 t
->param
= Parameter
;
899 status
= STATUS_SUCCESS
;
900 RtlEnterCriticalSection(&q
->cs
);
902 status
= STATUS_INVALID_HANDLE
;
904 queue_add_timer(t
, queue_current_time() + DueTime
, TRUE
);
905 RtlLeaveCriticalSection(&q
->cs
);
907 if (status
== STATUS_SUCCESS
)
910 RtlFreeHeap(GetProcessHeap(), 0, t
);
915 /***********************************************************************
916 * RtlUpdateTimer (NTDLL.@)
918 * Changes the time at which a timer expires.
921 * TimerQueue [I] The queue that holds the timer.
922 * Timer [I] The timer to update.
923 * DueTime [I] The delay, in milliseconds, before next firing the timer.
924 * Period [I] The period, in milliseconds, at which to fire the timer
925 * after the first callback. If zero, the timer will not
926 * refire once. It still needs to be deleted with
930 * Success: STATUS_SUCCESS.
931 * Failure: Any NTSTATUS code.
933 NTSTATUS WINAPI
RtlUpdateTimer(HANDLE TimerQueue
, HANDLE Timer
,
934 DWORD DueTime
, DWORD Period
)
936 struct timer_queue
*q
= TimerQueue
;
937 struct queue_timer
*t
= Timer
;
939 RtlEnterCriticalSection(&q
->cs
);
940 /* Can't change a timer if it was once-only or destroyed. */
941 if (t
->expire
!= EXPIRE_NEVER
)
944 queue_move_timer(t
, queue_current_time() + DueTime
, TRUE
);
946 RtlLeaveCriticalSection(&q
->cs
);
948 return STATUS_SUCCESS
;
951 /***********************************************************************
952 * RtlDeleteTimer (NTDLL.@)
954 * Cancels a timer-queue timer.
957 * TimerQueue [I] The queue that holds the timer.
958 * Timer [I] The timer to update.
959 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
960 * wait until the timer is finished firing all pending
961 * callbacks before returning. Otherwise, return
962 * immediately and set the timer is done.
965 * Success: STATUS_SUCCESS if the timer is done, STATUS_PENDING if not,
966 or if the completion event is NULL.
967 * Failure: Any NTSTATUS code.
969 NTSTATUS WINAPI
RtlDeleteTimer(HANDLE TimerQueue
, HANDLE Timer
,
970 HANDLE CompletionEvent
)
972 struct timer_queue
*q
= TimerQueue
;
973 struct queue_timer
*t
= Timer
;
974 NTSTATUS status
= STATUS_PENDING
;
977 if (CompletionEvent
== INVALID_HANDLE_VALUE
)
978 status
= NtCreateEvent(&event
, EVENT_ALL_ACCESS
, NULL
, FALSE
, FALSE
);
979 else if (CompletionEvent
)
980 event
= CompletionEvent
;
982 RtlEnterCriticalSection(&q
->cs
);
984 if (t
->runcount
== 0 && event
)
985 status
= STATUS_SUCCESS
;
986 queue_destroy_timer(t
);
987 RtlLeaveCriticalSection(&q
->cs
);
989 if (CompletionEvent
== INVALID_HANDLE_VALUE
&& event
)
991 if (status
== STATUS_PENDING
)
992 NtWaitForSingleObject(event
, FALSE
, NULL
);