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