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