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 /* threadpool_cs must be held while modifying the following elements */
43 static struct list work_item_list
= LIST_INIT(work_item_list
);
44 static LONG num_workers
;
45 static LONG num_busy_workers
;
46 static LONG num_items_processed
;
48 static RTL_CONDITION_VARIABLE threadpool_cond
= RTL_CONDITION_VARIABLE_INIT
;
50 static RTL_CRITICAL_SECTION threadpool_cs
;
51 static RTL_CRITICAL_SECTION_DEBUG critsect_debug
=
54 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
55 0, 0, { (DWORD_PTR
)(__FILE__
": threadpool_cs") }
57 static RTL_CRITICAL_SECTION threadpool_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
59 static HANDLE compl_port
= NULL
;
60 static RTL_CRITICAL_SECTION threadpool_compl_cs
;
61 static RTL_CRITICAL_SECTION_DEBUG critsect_compl_debug
=
63 0, 0, &threadpool_compl_cs
,
64 { &critsect_compl_debug
.ProcessLocksList
, &critsect_compl_debug
.ProcessLocksList
},
65 0, 0, { (DWORD_PTR
)(__FILE__
": threadpool_compl_cs") }
67 static RTL_CRITICAL_SECTION threadpool_compl_cs
= { &critsect_compl_debug
, -1, 0, 0, 0, 0 };
72 PRTL_WORK_ITEM_ROUTINE function
;
76 static inline LONG
interlocked_inc( PLONG dest
)
78 return interlocked_xchg_add( dest
, 1 ) + 1;
81 static inline LONG
interlocked_dec( PLONG dest
)
83 return interlocked_xchg_add( dest
, -1 ) - 1;
86 static void WINAPI
worker_thread_proc(void * param
)
89 struct work_item
*work_item_ptr
, work_item
;
90 LARGE_INTEGER timeout
;
91 timeout
.QuadPart
= -(WORKER_TIMEOUT
* (ULONGLONG
)10000);
93 RtlEnterCriticalSection( &threadpool_cs
);
98 if ((item
= list_head( &work_item_list
)))
100 work_item_ptr
= LIST_ENTRY( item
, struct work_item
, entry
);
101 list_remove( &work_item_ptr
->entry
);
103 num_items_processed
++;
104 RtlLeaveCriticalSection( &threadpool_cs
);
106 /* copy item to stack and do the work */
107 work_item
= *work_item_ptr
;
108 RtlFreeHeap( GetProcessHeap(), 0, work_item_ptr
);
109 TRACE("executing %p(%p)\n", work_item
.function
, work_item
.context
);
110 work_item
.function( work_item
.context
);
112 RtlEnterCriticalSection( &threadpool_cs
);
115 else if (RtlSleepConditionVariableCS( &threadpool_cond
, &threadpool_cs
, &timeout
) != STATUS_SUCCESS
)
120 RtlLeaveCriticalSection( &threadpool_cs
);
121 RtlExitUserThread( 0 );
126 /***********************************************************************
127 * RtlQueueWorkItem (NTDLL.@)
129 * Queues a work item into a thread in the thread pool.
132 * Function [I] Work function to execute.
133 * Context [I] Context to pass to the work function when it is executed.
134 * Flags [I] Flags. See notes.
137 * Success: STATUS_SUCCESS.
138 * Failure: Any NTSTATUS code.
141 * Flags can be one or more of the following:
142 *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread.
143 *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread.
144 *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent.
145 *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
146 *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
148 NTSTATUS WINAPI
RtlQueueWorkItem(PRTL_WORK_ITEM_ROUTINE Function
, PVOID Context
, ULONG Flags
)
152 LONG items_processed
;
153 struct work_item
*work_item
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof(struct work_item
));
156 return STATUS_NO_MEMORY
;
158 work_item
->function
= Function
;
159 work_item
->context
= Context
;
161 if (Flags
& ~WT_EXECUTELONGFUNCTION
)
162 FIXME("Flags 0x%x not supported\n", Flags
);
164 RtlEnterCriticalSection( &threadpool_cs
);
165 list_add_tail( &work_item_list
, &work_item
->entry
);
166 status
= (num_workers
> num_busy_workers
) ? STATUS_SUCCESS
: STATUS_UNSUCCESSFUL
;
167 items_processed
= num_items_processed
;
168 RtlLeaveCriticalSection( &threadpool_cs
);
170 /* FIXME: tune this algorithm to not be as aggressive with creating threads
171 * if WT_EXECUTELONGFUNCTION isn't specified */
172 if (status
== STATUS_SUCCESS
)
173 RtlWakeConditionVariable( &threadpool_cond
);
176 status
= RtlCreateUserThread( GetCurrentProcess(), NULL
, FALSE
, NULL
, 0, 0,
177 worker_thread_proc
, NULL
, &thread
, NULL
);
179 /* NOTE: we don't care if we couldn't create the thread if there is at
180 * least one other available to process the request */
181 if (status
== STATUS_SUCCESS
)
185 RtlEnterCriticalSection( &threadpool_cs
);
186 if (num_workers
> 0 || num_items_processed
!= items_processed
)
187 status
= STATUS_SUCCESS
;
189 list_remove( &work_item
->entry
);
190 RtlLeaveCriticalSection( &threadpool_cs
);
192 if (status
!= STATUS_SUCCESS
)
193 RtlFreeHeap( GetProcessHeap(), 0, work_item
);
200 /***********************************************************************
201 * iocp_poller - get completion events and run callbacks
203 static DWORD CALLBACK
iocp_poller(LPVOID Arg
)
209 PRTL_OVERLAPPED_COMPLETION_ROUTINE callback
;
211 IO_STATUS_BLOCK iosb
;
212 NTSTATUS res
= NtRemoveIoCompletion( cport
, (PULONG_PTR
)&callback
, (PULONG_PTR
)&overlapped
, &iosb
, NULL
);
215 ERR("NtRemoveIoCompletion failed: 0x%x\n", res
);
219 DWORD transferred
= 0;
222 if (iosb
.u
.Status
== STATUS_SUCCESS
)
223 transferred
= iosb
.Information
;
225 err
= RtlNtStatusToDosError(iosb
.u
.Status
);
227 callback( err
, transferred
, overlapped
);
233 /***********************************************************************
234 * RtlSetIoCompletionCallback (NTDLL.@)
236 * Binds a handle to a thread pool's completion port, and possibly
237 * starts a non-I/O thread to monitor this port and call functions back.
240 * FileHandle [I] Handle to bind to a completion port.
241 * Function [I] Callback function to call on I/O completions.
242 * Flags [I] Not used.
245 * Success: STATUS_SUCCESS.
246 * Failure: Any NTSTATUS code.
249 NTSTATUS WINAPI
RtlSetIoCompletionCallback(HANDLE FileHandle
, PRTL_OVERLAPPED_COMPLETION_ROUTINE Function
, ULONG Flags
)
251 IO_STATUS_BLOCK iosb
;
252 FILE_COMPLETION_INFORMATION info
;
254 if (Flags
) FIXME("Unknown value Flags=0x%x\n", Flags
);
258 NTSTATUS res
= STATUS_SUCCESS
;
260 RtlEnterCriticalSection(&threadpool_compl_cs
);
265 res
= NtCreateIoCompletion( &cport
, IO_COMPLETION_ALL_ACCESS
, NULL
, 0 );
268 /* FIXME native can start additional threads in case of e.g. hung callback function. */
269 res
= RtlQueueWorkItem( iocp_poller
, cport
, WT_EXECUTEDEFAULT
);
276 RtlLeaveCriticalSection(&threadpool_compl_cs
);
280 info
.CompletionPort
= compl_port
;
281 info
.CompletionKey
= (ULONG_PTR
)Function
;
283 return NtSetInformationFile( FileHandle
, &iosb
, &info
, sizeof(info
), FileCompletionInformation
);
286 static inline PLARGE_INTEGER
get_nt_timeout( PLARGE_INTEGER pTime
, ULONG timeout
)
288 if (timeout
== INFINITE
) return NULL
;
289 pTime
->QuadPart
= (ULONGLONG
)timeout
* -10000;
293 struct wait_work_item
297 WAITORTIMERCALLBACK Callback
;
301 HANDLE CompletionEvent
;
303 BOOLEAN CallbackInProgress
;
306 static void delete_wait_work_item(struct wait_work_item
*wait_work_item
)
308 NtClose( wait_work_item
->CancelEvent
);
309 RtlFreeHeap( GetProcessHeap(), 0, wait_work_item
);
312 static DWORD CALLBACK
wait_thread_proc(LPVOID Arg
)
314 struct wait_work_item
*wait_work_item
= Arg
;
316 BOOLEAN alertable
= (wait_work_item
->Flags
& WT_EXECUTEINIOTHREAD
) != 0;
317 HANDLE handles
[2] = { wait_work_item
->Object
, wait_work_item
->CancelEvent
};
318 LARGE_INTEGER timeout
;
319 HANDLE completion_event
;
325 status
= NtWaitForMultipleObjects( 2, handles
, TRUE
, alertable
,
326 get_nt_timeout( &timeout
, wait_work_item
->Milliseconds
) );
327 if (status
== STATUS_WAIT_0
|| status
== STATUS_TIMEOUT
)
329 BOOLEAN TimerOrWaitFired
;
331 if (status
== STATUS_WAIT_0
)
333 TRACE( "object %p signaled, calling callback %p with context %p\n",
334 wait_work_item
->Object
, wait_work_item
->Callback
,
335 wait_work_item
->Context
);
336 TimerOrWaitFired
= FALSE
;
340 TRACE( "wait for object %p timed out, calling callback %p with context %p\n",
341 wait_work_item
->Object
, wait_work_item
->Callback
,
342 wait_work_item
->Context
);
343 TimerOrWaitFired
= TRUE
;
345 wait_work_item
->CallbackInProgress
= TRUE
;
346 wait_work_item
->Callback( wait_work_item
->Context
, TimerOrWaitFired
);
347 wait_work_item
->CallbackInProgress
= FALSE
;
349 if (wait_work_item
->Flags
& WT_EXECUTEONLYONCE
)
356 completion_event
= wait_work_item
->CompletionEvent
;
357 if (completion_event
) NtSetEvent( completion_event
, NULL
);
359 if (interlocked_inc( &wait_work_item
->DeleteCount
) == 2 )
360 delete_wait_work_item( wait_work_item
);
365 /***********************************************************************
366 * RtlRegisterWait (NTDLL.@)
368 * Registers a wait for a handle to become signaled.
371 * NewWaitObject [I] Handle to the new wait object. Use RtlDeregisterWait() to free it.
372 * Object [I] Object to wait to become signaled.
373 * Callback [I] Callback function to execute when the wait times out or the handle is signaled.
374 * Context [I] Context to pass to the callback function when it is executed.
375 * Milliseconds [I] Number of milliseconds to wait before timing out.
376 * Flags [I] Flags. See notes.
379 * Success: STATUS_SUCCESS.
380 * Failure: Any NTSTATUS code.
383 * Flags can be one or more of the following:
384 *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread.
385 *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread.
386 *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent.
387 *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
388 *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
390 NTSTATUS WINAPI
RtlRegisterWait(PHANDLE NewWaitObject
, HANDLE Object
,
391 RTL_WAITORTIMERCALLBACKFUNC Callback
,
392 PVOID Context
, ULONG Milliseconds
, ULONG Flags
)
394 struct wait_work_item
*wait_work_item
;
397 TRACE( "(%p, %p, %p, %p, %d, 0x%x)\n", NewWaitObject
, Object
, Callback
, Context
, Milliseconds
, Flags
);
399 wait_work_item
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*wait_work_item
) );
401 return STATUS_NO_MEMORY
;
403 wait_work_item
->Object
= Object
;
404 wait_work_item
->Callback
= Callback
;
405 wait_work_item
->Context
= Context
;
406 wait_work_item
->Milliseconds
= Milliseconds
;
407 wait_work_item
->Flags
= Flags
;
408 wait_work_item
->CallbackInProgress
= FALSE
;
409 wait_work_item
->DeleteCount
= 0;
410 wait_work_item
->CompletionEvent
= NULL
;
412 status
= NtCreateEvent( &wait_work_item
->CancelEvent
, EVENT_ALL_ACCESS
, NULL
, NotificationEvent
, FALSE
);
413 if (status
!= STATUS_SUCCESS
)
415 RtlFreeHeap( GetProcessHeap(), 0, wait_work_item
);
419 Flags
= Flags
& (WT_EXECUTEINIOTHREAD
| WT_EXECUTEINPERSISTENTTHREAD
|
420 WT_EXECUTELONGFUNCTION
| WT_TRANSFER_IMPERSONATION
);
421 status
= RtlQueueWorkItem( wait_thread_proc
, wait_work_item
, Flags
);
422 if (status
!= STATUS_SUCCESS
)
424 delete_wait_work_item( wait_work_item
);
428 *NewWaitObject
= wait_work_item
;
432 /***********************************************************************
433 * RtlDeregisterWaitEx (NTDLL.@)
435 * Cancels a wait operation and frees the resources associated with calling
439 * WaitObject [I] Handle to the wait object to free.
442 * Success: STATUS_SUCCESS.
443 * Failure: Any NTSTATUS code.
445 NTSTATUS WINAPI
RtlDeregisterWaitEx(HANDLE WaitHandle
, HANDLE CompletionEvent
)
447 struct wait_work_item
*wait_work_item
= WaitHandle
;
448 NTSTATUS status
= STATUS_SUCCESS
;
450 TRACE( "(%p)\n", WaitHandle
);
452 NtSetEvent( wait_work_item
->CancelEvent
, NULL
);
453 if (wait_work_item
->CallbackInProgress
)
455 if (CompletionEvent
!= NULL
)
457 if (CompletionEvent
== INVALID_HANDLE_VALUE
)
459 status
= NtCreateEvent( &CompletionEvent
, EVENT_ALL_ACCESS
, NULL
, NotificationEvent
, FALSE
);
460 if (status
!= STATUS_SUCCESS
)
462 interlocked_xchg_ptr( &wait_work_item
->CompletionEvent
, CompletionEvent
);
463 if (wait_work_item
->CallbackInProgress
)
464 NtWaitForSingleObject( CompletionEvent
, FALSE
, NULL
);
465 NtClose( CompletionEvent
);
469 interlocked_xchg_ptr( &wait_work_item
->CompletionEvent
, CompletionEvent
);
470 if (wait_work_item
->CallbackInProgress
)
471 status
= STATUS_PENDING
;
475 status
= STATUS_PENDING
;
478 if (interlocked_inc( &wait_work_item
->DeleteCount
) == 2 )
480 status
= STATUS_SUCCESS
;
481 delete_wait_work_item( wait_work_item
);
487 /***********************************************************************
488 * RtlDeregisterWait (NTDLL.@)
490 * Cancels a wait operation and frees the resources associated with calling
494 * WaitObject [I] Handle to the wait object to free.
497 * Success: STATUS_SUCCESS.
498 * Failure: Any NTSTATUS code.
500 NTSTATUS WINAPI
RtlDeregisterWait(HANDLE WaitHandle
)
502 return RtlDeregisterWaitEx(WaitHandle
, NULL
);
506 /************************** Timer Queue Impl **************************/
511 struct timer_queue
*q
;
513 ULONG runcount
; /* number of callbacks pending execution */
514 RTL_WAITORTIMERCALLBACKFUNC callback
;
519 BOOL destroy
; /* timer should be deleted; once set, never unset */
520 HANDLE event
; /* removal event */
526 RTL_CRITICAL_SECTION cs
;
527 struct list timers
; /* sorted by expiration time */
528 BOOL quit
; /* queue should be deleted; once set, never unset */
533 #define EXPIRE_NEVER (~(ULONGLONG) 0)
534 #define TIMER_QUEUE_MAGIC 0x516d6954 /* TimQ */
536 static void queue_remove_timer(struct queue_timer
*t
)
538 /* We MUST hold the queue cs while calling this function. This ensures
539 that we cannot queue another callback for this timer. The runcount
540 being zero makes sure we don't have any already queued. */
541 struct timer_queue
*q
= t
->q
;
543 assert(t
->runcount
== 0);
546 list_remove(&t
->entry
);
548 NtSetEvent(t
->event
, NULL
);
549 RtlFreeHeap(GetProcessHeap(), 0, t
);
551 if (q
->quit
&& list_empty(&q
->timers
))
552 NtSetEvent(q
->event
, NULL
);
555 static void timer_cleanup_callback(struct queue_timer
*t
)
557 struct timer_queue
*q
= t
->q
;
558 RtlEnterCriticalSection(&q
->cs
);
560 assert(0 < t
->runcount
);
563 if (t
->destroy
&& t
->runcount
== 0)
564 queue_remove_timer(t
);
566 RtlLeaveCriticalSection(&q
->cs
);
569 static DWORD WINAPI
timer_callback_wrapper(LPVOID p
)
571 struct queue_timer
*t
= p
;
572 t
->callback(t
->param
, TRUE
);
573 timer_cleanup_callback(t
);
577 static inline ULONGLONG
queue_current_time(void)
579 LARGE_INTEGER now
, freq
;
580 NtQueryPerformanceCounter(&now
, &freq
);
581 return now
.QuadPart
* 1000 / freq
.QuadPart
;
584 static void queue_add_timer(struct queue_timer
*t
, ULONGLONG time
,
587 /* We MUST hold the queue cs while calling this function. */
588 struct timer_queue
*q
= t
->q
;
589 struct list
*ptr
= &q
->timers
;
591 assert(!q
->quit
|| (t
->destroy
&& time
== EXPIRE_NEVER
));
593 if (time
!= EXPIRE_NEVER
)
594 LIST_FOR_EACH(ptr
, &q
->timers
)
596 struct queue_timer
*cur
= LIST_ENTRY(ptr
, struct queue_timer
, entry
);
597 if (time
< cur
->expire
)
600 list_add_before(ptr
, &t
->entry
);
604 /* If we insert at the head of the list, we need to expire sooner
606 if (set_event
&& &t
->entry
== list_head(&q
->timers
))
607 NtSetEvent(q
->event
, NULL
);
610 static inline void queue_move_timer(struct queue_timer
*t
, ULONGLONG time
,
613 /* We MUST hold the queue cs while calling this function. */
614 list_remove(&t
->entry
);
615 queue_add_timer(t
, time
, set_event
);
618 static void queue_timer_expire(struct timer_queue
*q
)
620 struct queue_timer
*t
= NULL
;
622 RtlEnterCriticalSection(&q
->cs
);
623 if (list_head(&q
->timers
))
626 t
= LIST_ENTRY(list_head(&q
->timers
), struct queue_timer
, entry
);
627 if (!t
->destroy
&& t
->expire
<= ((now
= queue_current_time())))
632 next
= t
->expire
+ t
->period
;
633 /* avoid trigger cascade if overloaded / hibernated */
635 next
= now
+ t
->period
;
639 queue_move_timer(t
, next
, FALSE
);
644 RtlLeaveCriticalSection(&q
->cs
);
648 if (t
->flags
& WT_EXECUTEINTIMERTHREAD
)
649 timer_callback_wrapper(t
);
654 & (WT_EXECUTEINIOTHREAD
| WT_EXECUTEINPERSISTENTTHREAD
655 | WT_EXECUTELONGFUNCTION
| WT_TRANSFER_IMPERSONATION
));
656 NTSTATUS status
= RtlQueueWorkItem(timer_callback_wrapper
, t
, flags
);
657 if (status
!= STATUS_SUCCESS
)
658 timer_cleanup_callback(t
);
663 static ULONG
queue_get_timeout(struct timer_queue
*q
)
665 struct queue_timer
*t
;
666 ULONG timeout
= INFINITE
;
668 RtlEnterCriticalSection(&q
->cs
);
669 if (list_head(&q
->timers
))
671 t
= LIST_ENTRY(list_head(&q
->timers
), struct queue_timer
, entry
);
672 assert(!t
->destroy
|| t
->expire
== EXPIRE_NEVER
);
674 if (t
->expire
!= EXPIRE_NEVER
)
676 ULONGLONG time
= queue_current_time();
677 timeout
= t
->expire
< time
? 0 : t
->expire
- time
;
680 RtlLeaveCriticalSection(&q
->cs
);
685 static void WINAPI
timer_queue_thread_proc(LPVOID p
)
687 struct timer_queue
*q
= p
;
690 timeout_ms
= INFINITE
;
693 LARGE_INTEGER timeout
;
697 status
= NtWaitForSingleObject(
698 q
->event
, FALSE
, get_nt_timeout(&timeout
, timeout_ms
));
700 if (status
== STATUS_WAIT_0
)
702 /* There are two possible ways to trigger the event. Either
703 we are quitting and the last timer got removed, or a new
704 timer got put at the head of the list so we need to adjust
706 RtlEnterCriticalSection(&q
->cs
);
707 if (q
->quit
&& list_empty(&q
->timers
))
709 RtlLeaveCriticalSection(&q
->cs
);
711 else if (status
== STATUS_TIMEOUT
)
712 queue_timer_expire(q
);
717 timeout_ms
= queue_get_timeout(q
);
721 RtlDeleteCriticalSection(&q
->cs
);
723 RtlFreeHeap(GetProcessHeap(), 0, q
);
726 static void queue_destroy_timer(struct queue_timer
*t
)
728 /* We MUST hold the queue cs while calling this function. */
730 if (t
->runcount
== 0)
731 /* Ensure a timer is promptly removed. If callbacks are pending,
732 it will be removed after the last one finishes by the callback
734 queue_remove_timer(t
);
736 /* Make sure no destroyed timer masks an active timer at the head
737 of the sorted list. */
738 queue_move_timer(t
, EXPIRE_NEVER
, FALSE
);
741 /***********************************************************************
742 * RtlCreateTimerQueue (NTDLL.@)
744 * Creates a timer queue object and returns a handle to it.
747 * NewTimerQueue [O] The newly created queue.
750 * Success: STATUS_SUCCESS.
751 * Failure: Any NTSTATUS code.
753 NTSTATUS WINAPI
RtlCreateTimerQueue(PHANDLE NewTimerQueue
)
756 struct timer_queue
*q
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof *q
);
758 return STATUS_NO_MEMORY
;
760 RtlInitializeCriticalSection(&q
->cs
);
761 list_init(&q
->timers
);
763 q
->magic
= TIMER_QUEUE_MAGIC
;
764 status
= NtCreateEvent(&q
->event
, EVENT_ALL_ACCESS
, NULL
, SynchronizationEvent
, FALSE
);
765 if (status
!= STATUS_SUCCESS
)
767 RtlFreeHeap(GetProcessHeap(), 0, q
);
770 status
= RtlCreateUserThread(GetCurrentProcess(), NULL
, FALSE
, NULL
, 0, 0,
771 timer_queue_thread_proc
, q
, &q
->thread
, NULL
);
772 if (status
!= STATUS_SUCCESS
)
775 RtlFreeHeap(GetProcessHeap(), 0, q
);
780 return STATUS_SUCCESS
;
783 /***********************************************************************
784 * RtlDeleteTimerQueueEx (NTDLL.@)
786 * Deletes a timer queue object.
789 * TimerQueue [I] The timer queue to destroy.
790 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
791 * wait until all timers are finished firing before
792 * returning. Otherwise, return immediately and set the
793 * event when all timers are done.
796 * Success: STATUS_SUCCESS if synchronous, STATUS_PENDING if not.
797 * Failure: Any NTSTATUS code.
799 NTSTATUS WINAPI
RtlDeleteTimerQueueEx(HANDLE TimerQueue
, HANDLE CompletionEvent
)
801 struct timer_queue
*q
= TimerQueue
;
802 struct queue_timer
*t
, *temp
;
806 if (!q
|| q
->magic
!= TIMER_QUEUE_MAGIC
)
807 return STATUS_INVALID_HANDLE
;
811 RtlEnterCriticalSection(&q
->cs
);
813 if (list_head(&q
->timers
))
814 /* When the last timer is removed, it will signal the timer thread to
816 LIST_FOR_EACH_ENTRY_SAFE(t
, temp
, &q
->timers
, struct queue_timer
, entry
)
817 queue_destroy_timer(t
);
819 /* However if we have none, we must do it ourselves. */
820 NtSetEvent(q
->event
, NULL
);
821 RtlLeaveCriticalSection(&q
->cs
);
823 if (CompletionEvent
== INVALID_HANDLE_VALUE
)
825 NtWaitForSingleObject(thread
, FALSE
, NULL
);
826 status
= STATUS_SUCCESS
;
832 FIXME("asynchronous return on completion event unimplemented\n");
833 NtWaitForSingleObject(thread
, FALSE
, NULL
);
834 NtSetEvent(CompletionEvent
, NULL
);
836 status
= STATUS_PENDING
;
843 static struct timer_queue
*default_timer_queue
;
845 static struct timer_queue
*get_timer_queue(HANDLE TimerQueue
)
851 if (!default_timer_queue
)
854 NTSTATUS status
= RtlCreateTimerQueue(&q
);
855 if (status
== STATUS_SUCCESS
)
857 PVOID p
= interlocked_cmpxchg_ptr(
858 (void **) &default_timer_queue
, q
, NULL
);
860 /* Got beat to the punch. */
861 RtlDeleteTimerQueueEx(q
, NULL
);
864 return default_timer_queue
;
868 /***********************************************************************
869 * RtlCreateTimer (NTDLL.@)
871 * Creates a new timer associated with the given queue.
874 * NewTimer [O] The newly created timer.
875 * TimerQueue [I] The queue to hold the timer.
876 * Callback [I] The callback to fire.
877 * Parameter [I] The argument for the callback.
878 * DueTime [I] The delay, in milliseconds, before first firing the
880 * Period [I] The period, in milliseconds, at which to fire the timer
881 * after the first callback. If zero, the timer will only
882 * fire once. It still needs to be deleted with
884 * Flags [I] Flags controlling the execution of the callback. In
885 * addition to the WT_* thread pool flags (see
886 * RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and
887 * WT_EXECUTEONLYONCE are supported.
890 * Success: STATUS_SUCCESS.
891 * Failure: Any NTSTATUS code.
893 NTSTATUS WINAPI
RtlCreateTimer(PHANDLE NewTimer
, HANDLE TimerQueue
,
894 RTL_WAITORTIMERCALLBACKFUNC Callback
,
895 PVOID Parameter
, DWORD DueTime
, DWORD Period
,
899 struct queue_timer
*t
;
900 struct timer_queue
*q
= get_timer_queue(TimerQueue
);
902 if (!q
) return STATUS_NO_MEMORY
;
903 if (q
->magic
!= TIMER_QUEUE_MAGIC
) return STATUS_INVALID_HANDLE
;
905 t
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof *t
);
907 return STATUS_NO_MEMORY
;
911 t
->callback
= Callback
;
912 t
->param
= Parameter
;
918 status
= STATUS_SUCCESS
;
919 RtlEnterCriticalSection(&q
->cs
);
921 status
= STATUS_INVALID_HANDLE
;
923 queue_add_timer(t
, queue_current_time() + DueTime
, TRUE
);
924 RtlLeaveCriticalSection(&q
->cs
);
926 if (status
== STATUS_SUCCESS
)
929 RtlFreeHeap(GetProcessHeap(), 0, t
);
934 /***********************************************************************
935 * RtlUpdateTimer (NTDLL.@)
937 * Changes the time at which a timer expires.
940 * TimerQueue [I] The queue that holds the timer.
941 * Timer [I] The timer to update.
942 * DueTime [I] The delay, in milliseconds, before next firing the timer.
943 * Period [I] The period, in milliseconds, at which to fire the timer
944 * after the first callback. If zero, the timer will not
945 * refire once. It still needs to be deleted with
949 * Success: STATUS_SUCCESS.
950 * Failure: Any NTSTATUS code.
952 NTSTATUS WINAPI
RtlUpdateTimer(HANDLE TimerQueue
, HANDLE Timer
,
953 DWORD DueTime
, DWORD Period
)
955 struct queue_timer
*t
= Timer
;
956 struct timer_queue
*q
= t
->q
;
958 RtlEnterCriticalSection(&q
->cs
);
959 /* Can't change a timer if it was once-only or destroyed. */
960 if (t
->expire
!= EXPIRE_NEVER
)
963 queue_move_timer(t
, queue_current_time() + DueTime
, TRUE
);
965 RtlLeaveCriticalSection(&q
->cs
);
967 return STATUS_SUCCESS
;
970 /***********************************************************************
971 * RtlDeleteTimer (NTDLL.@)
973 * Cancels a timer-queue timer.
976 * TimerQueue [I] The queue that holds the timer.
977 * Timer [I] The timer to update.
978 * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE,
979 * wait until the timer is finished firing all pending
980 * callbacks before returning. Otherwise, return
981 * immediately and set the timer is done.
984 * Success: STATUS_SUCCESS if the timer is done, STATUS_PENDING if not,
985 or if the completion event is NULL.
986 * Failure: Any NTSTATUS code.
988 NTSTATUS WINAPI
RtlDeleteTimer(HANDLE TimerQueue
, HANDLE Timer
,
989 HANDLE CompletionEvent
)
991 struct queue_timer
*t
= Timer
;
992 struct timer_queue
*q
;
993 NTSTATUS status
= STATUS_PENDING
;
997 return STATUS_INVALID_PARAMETER_1
;
999 if (CompletionEvent
== INVALID_HANDLE_VALUE
)
1001 status
= NtCreateEvent(&event
, EVENT_ALL_ACCESS
, NULL
, SynchronizationEvent
, FALSE
);
1002 if (status
== STATUS_SUCCESS
)
1003 status
= STATUS_PENDING
;
1005 else if (CompletionEvent
)
1006 event
= CompletionEvent
;
1008 RtlEnterCriticalSection(&q
->cs
);
1010 if (t
->runcount
== 0 && event
)
1011 status
= STATUS_SUCCESS
;
1012 queue_destroy_timer(t
);
1013 RtlLeaveCriticalSection(&q
->cs
);
1015 if (CompletionEvent
== INVALID_HANDLE_VALUE
&& event
)
1017 if (status
== STATUS_PENDING
)
1019 NtWaitForSingleObject(event
, FALSE
, NULL
);
1020 status
= STATUS_SUCCESS
;