Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

threads.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1989-1998 Microsoft Corporation 00004 00005 Module Name: 00006 00007 threads.c 00008 00009 Abstract: 00010 00011 This module defines functions for thread pools. Thread pools can be used for 00012 one time execution of tasks, for waits and for one shot or periodic timers. 00013 00014 Author: 00015 00016 Gurdeep Singh Pall (gurdeep) Nov 13, 1997 00017 00018 Environment: 00019 00020 These routines are statically linked in the caller's executable and 00021 are callable in only from user mode. They make use of Nt system services. 00022 00023 00024 Revision History: 00025 00026 Aug-19 lokeshs - modifications to thread pool apis. 00027 00028 --*/ 00029 00030 00031 // There are 3 types of thread pool functions supported 00032 // 00033 // 1. Wait Thread Pool 00034 // 2. Worker Thread Pool 00035 // 3. Timer Thread Pool 00036 // 00037 // Wait Thread Pool 00038 // ---------------- 00039 // Clients can submit a waitable object with an optional timeout to wait on. 00040 // One thread is created per 63 of such waitable objects. These threads are 00041 // never killed. 00042 // 00043 // Worker Thread Pool 00044 // ------------------ 00045 // Clients can submit functions to be executed by a worker thread. Threads are 00046 // created if the work queue exceeds a threshold. Clients can request that the 00047 // function be invoked in the context of a I/O thread. I/O worker threads 00048 // can be used for initiating asynchronous I/O requests. They are not terminated if 00049 // there are pending IO requests. Worker threads terminate if inactivity exceeds a 00050 // threshold. 00051 // Clients can also associate IO completion requests with the IO completion port 00052 // waited upon by the non I/O worker threads. One should not post overlapped IO requests 00053 // in worker threads. 00054 // 00055 // Timer Thread Pool 00056 // ----------------- 00057 // Clients create one or more Timer Queues and insert one shot or periodic 00058 // timers in them. All timers in a queue are kept in a "Delta List" with each 00059 // timer's firing time relative to the timer before it. All Queues are also 00060 // kept in a "Delta List" with each Queue's firing time (set to the firing time 00061 // of the nearest firing timer) relative to the Queue before it. One NT Timer 00062 // is used to service all timers in all queues. 00063 00064 00065 #include <ntos.h> 00066 #include <ntrtl.h> 00067 #include <nturtl.h> 00068 #include "ntrtlp.h" 00069 #include "threads.h" 00070 00071 00072 ULONG DPRN = 0x0000000; 00073 00074 00075 00076 NTSTATUS 00077 RtlRegisterWait ( 00078 OUT PHANDLE WaitHandle, 00079 IN HANDLE Handle, 00080 IN WAITORTIMERCALLBACKFUNC Function, 00081 IN PVOID Context, 00082 IN ULONG Milliseconds, 00083 IN ULONG Flags 00084 ) 00085 00086 /*++ 00087 00088 Routine Description: 00089 00090 This routine adds a new wait request to the pool of objects being waited on. 00091 00092 Arguments: 00093 00094 WaitHandle - Handle returned on successful completion of this routine. 00095 00096 Handle - Handle to the object to be waited on 00097 00098 Function - Routine that is called when the wait completes or a timeout occurs 00099 00100 Context - Opaque pointer passed in as an argument to Function 00101 00102 Milliseconds - Timeout for the wait in milliseconds. 0xffffffff means dont 00103 timeout. 00104 00105 Flags - Can be one of: 00106 00107 WT_EXECUTEINWAITTHREAD - if WorkerProc should be invoked in the wait 00108 thread itself. This should only be used for small routines. 00109 WT_EXECUTEINIOTHREAD - use only if the WorkerProc should be invoked in 00110 an IO Worker thread. Avoid using it. 00111 00112 If Flags is not WT_EXECUTEINWAITTHREAD, the following flag can also be set: 00113 00114 WT_EXECUTELONGFUNCTION - indicates that the callback might be blocked 00115 for a long duration. Use only if the callback is being queued to a 00116 worker thread. 00117 00118 Return Value: 00119 00120 NTSTATUS - Result code from call. The following are returned 00121 00122 STATUS_SUCCESS - The registration was successful. 00123 00124 STATUS_NO_MEMORY - There was not sufficient heap to perform the requested 00125 operation. 00126 00127 or other NTSTATUS error code 00128 00129 --*/ 00130 00131 { 00132 PRTLP_WAIT Wait ; 00133 NTSTATUS Status ; 00134 PRTLP_EVENT Event ; 00135 LARGE_INTEGER TimeOut ; 00136 PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB = NULL; 00137 00138 *WaitHandle = NULL ; 00139 00140 00141 // Initialize thread pool if it isnt already done 00142 00143 if ( CompletedWaitInitialization != 1) { 00144 00145 Status = RtlpInitializeWaitThreadPool () ; 00146 00147 if (! NT_SUCCESS( Status ) ) 00148 return Status ; 00149 } 00150 00151 00152 // Initialize Wait request 00153 00154 Wait = (PRTLP_WAIT) RtlpAllocateTPHeap ( sizeof (RTLP_WAIT), 00155 HEAP_ZERO_MEMORY) ; 00156 00157 if (!Wait) { 00158 return STATUS_NO_MEMORY ; 00159 } 00160 00161 Wait->WaitHandle = Handle ; 00162 Wait->Flags = Flags ; 00163 Wait->Function = Function ; 00164 Wait->Context = Context ; 00165 Wait->Timeout = Milliseconds ; 00166 SET_SIGNATURE(Wait) ; 00167 00168 00169 // timer part of wait is initialized by wait thread in RtlpAddWait 00170 00171 00172 // Get a wait thread that can accomodate another wait request. 00173 00174 Status = RtlpFindWaitThread (&ThreadCB) ; 00175 00176 if (Status != STATUS_SUCCESS) { 00177 00178 RtlpFreeTPHeap( Wait ) ; 00179 00180 return Status ; 00181 } 00182 00183 Wait->ThreadCB = ThreadCB ; 00184 00185 #if DBG1 00186 Wait->DbgId = ++NextWaitDbgId ; 00187 Wait->ThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ; 00188 if (DPRN0) 00189 DbgPrint("<%d:%d> Wait %x created by thread:<%x:%x>\n\n", 00190 Wait->DbgId, 1, (ULONG_PTR)Wait, 00191 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 00192 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ; 00193 #endif 00194 00195 // Set the wait handle 00196 00197 *WaitHandle = Wait ; 00198 00199 00200 // Queue an APC to the Wait Thread 00201 00202 Status = NtQueueApcThread( 00203 ThreadCB->ThreadHandle, 00204 (PPS_APC_ROUTINE)RtlpAddWait, 00205 (PVOID)Wait, 00206 NULL, 00207 NULL 00208 ); 00209 00210 00211 if ( NT_SUCCESS(Status) ) { 00212 00213 Status = STATUS_SUCCESS ; 00214 00215 } else { 00216 00217 *WaitHandle = NULL ; 00218 RtlpFreeTPHeap( Wait ) ; 00219 } 00220 00221 return Status ; 00222 00223 } 00224 00225 00226 00227 NTSTATUS 00228 RtlDeregisterWait( 00229 IN HANDLE WaitHandle 00230 ) 00231 /*++ 00232 00233 Routine Description: 00234 00235 This routine removes the specified wait from the pool of objects being 00236 waited on. This routine is non-blocking. Once this call returns, no new 00237 Callbacks are invoked. However, Callbacks that might already have been queued 00238 to worker threads are not cancelled. 00239 00240 Arguments: 00241 00242 WaitHandle - Handle indentifying the wait. 00243 00244 Return Value: 00245 00246 STATUS_SUCCESS - The deregistration was successful. 00247 STATUS_PENDING - Some callbacks associated with this Wait, are still executing. 00248 --*/ 00249 00250 { 00251 return RtlDeregisterWaitEx( WaitHandle, NULL ) ; 00252 } 00253 00254 00255 NTSTATUS 00256 RtlDeregisterWaitEx( 00257 IN HANDLE WaitHandle, 00258 IN HANDLE Event 00259 ) 00260 /*++ 00261 00262 Routine Description: 00263 00264 This routine removes the specified wait from the pool of objects being 00265 waited on. Once this call returns, no new Callbacks will be invoked. 00266 Depending on the value of Event, the call can be blocking or non-blocking. 00267 Blocking calls MUST NOT be invoked inside the callback routines, except 00268 when a callback being executed in the Wait thread context deregisters 00269 its associated Wait (in this case there is no reason for making blocking calls), 00270 or when a callback queued to a worker thread is deregistering some other wait item 00271 (be careful of deadlocks here). 00272 00273 Arguments: 00274 00275 WaitHandle - Handle indentifying the wait. 00276 00277 Event - Event to wait upon. 00278 (HANDLE)-1: The function creates an event and waits on it. 00279 Event : The caller passes an Event. The function removes the wait handle, 00280 but does not wait for all callbacks to complete. The Event is 00281 released after all callbacks have completed. 00282 NULL : The function is non-blocking. The function removes the wait handle, 00283 but does not wait for all callbacks to complete. 00284 00285 Return Value: 00286 00287 STATUS_SUCCESS - The deregistration was successful. 00288 STATUS_PENDING - Some callback is still pending. 00289 00290 --*/ 00291 00292 { 00293 NTSTATUS Status, StatusAsync = STATUS_SUCCESS ; 00294 PRTLP_WAIT Wait = (PRTLP_WAIT) WaitHandle ; 00295 ULONG CurrentThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ; 00296 PRTLP_EVENT CompletionEvent = NULL ; 00297 HANDLE ThreadHandle ; 00298 ULONG NonBlocking = ( Event != (HANDLE) -1 ) ; //The call returns non-blocking 00299 00300 00301 if (!Wait) { 00302 ASSERT( FALSE ) ; 00303 return STATUS_INVALID_PARAMETER ; 00304 } 00305 00306 ThreadHandle = Wait->ThreadCB->ThreadHandle ; 00307 00308 00309 CHECK_DEL_SIGNATURE( Wait ) ; 00310 SET_DEL_SIGNATURE( Wait ) ; 00311 00312 #if DBG1 00313 Wait->ThreadId2 = CurrentThreadId ; 00314 #endif 00315 00316 if (Event == (HANDLE)-1) { 00317 00318 // Get an event from the event cache 00319 00320 CompletionEvent = RtlpGetWaitEvent () ; 00321 00322 if (!CompletionEvent) { 00323 00324 return STATUS_NO_MEMORY ; 00325 00326 } 00327 } 00328 00329 00330 Wait = (PRTLP_WAIT) WaitHandle ; 00331 00332 #if DBG1 00333 if (DPRN0) 00334 DbgPrint("<%d:%d> Wait %x deregistering by thread:<%x:%x>\n\n", Wait->DbgId, 00335 Wait->RefCount, (ULONG_PTR)Wait, 00336 CurrentThreadId, 00337 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ; 00338 #endif 00339 00340 00341 Wait->CompletionEvent = CompletionEvent 00342 ? CompletionEvent->Handle 00343 : Event ; 00344 00345 // 00346 // RtlDeregisterWaitEx is being called from within the Wait thread callback 00347 // 00348 00349 if ( CurrentThreadId == Wait->ThreadCB->ThreadId ) { 00350 00351 Status = RtlpDeregisterWait ( Wait, NULL, NULL ) ; 00352 00353 00354 // all callback functions run in the wait thread. So cannot return PENDING 00355 00356 ASSERT ( Status != STATUS_PENDING ) ; 00357 00358 00359 } else { 00360 00361 PRTLP_EVENT PartialCompletionEvent = NULL ; 00362 00363 if (NonBlocking) { 00364 00365 PartialCompletionEvent = RtlpGetWaitEvent () ; 00366 00367 if (!PartialCompletionEvent) { 00368 00369 return STATUS_NO_MEMORY ; 00370 } 00371 } 00372 00373 // Queue an APC to the Wait Thread 00374 00375 Status = NtQueueApcThread( 00376 Wait->ThreadCB->ThreadHandle, 00377 (PPS_APC_ROUTINE)RtlpDeregisterWait, 00378 (PVOID) Wait, 00379 NonBlocking ? PartialCompletionEvent->Handle : NULL , 00380 NonBlocking ? (PVOID)&StatusAsync : NULL 00381 ); 00382 00383 if (! NT_SUCCESS(Status)) { 00384 00385 if (CompletionEvent) RtlpFreeWaitEvent( CompletionEvent ) ; 00386 if (PartialCompletionEvent) RtlpFreeWaitEvent( PartialCompletionEvent ) ; 00387 00388 return Status ; 00389 } 00390 00391 00392 // block till the wait entry has been deactivated 00393 00394 if (NonBlocking) { 00395 00396 Status = RtlpWaitForEvent( PartialCompletionEvent->Handle, ThreadHandle ) ; 00397 } 00398 00399 00400 if (PartialCompletionEvent) RtlpFreeWaitEvent( PartialCompletionEvent ) ; 00401 00402 } 00403 00404 if ( CompletionEvent ) { 00405 00406 // wait for Event to be fired. Return if the thread has been killed. 00407 00408 #if DBG1 00409 if (DPRN0) 00410 DbgPrint("Wait %x deregister waiting ThreadId<%x:%x>\n\n", 00411 (ULONG_PTR)Wait, 00412 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 00413 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ; 00414 #endif 00415 00416 Status = RtlpWaitForEvent( CompletionEvent->Handle, ThreadHandle ) ; 00417 00418 #if DBG1 00419 if (DPRN0) 00420 DbgPrint("Wait %x deregister completed\n\n", (ULONG_PTR)Wait) ; 00421 #endif 00422 00423 RtlpFreeWaitEvent( CompletionEvent ) ; 00424 00425 return NT_SUCCESS( Status ) ? STATUS_SUCCESS : Status ; 00426 00427 } else { 00428 00429 return StatusAsync ; 00430 } 00431 } 00432 00433 00434 00435 00436 NTSTATUS 00437 RtlQueueWorkItem( 00438 IN WORKERCALLBACKFUNC Function, 00439 IN PVOID Context, 00440 IN ULONG Flags 00441 ) 00442 00443 /*++ 00444 00445 Routine Description: 00446 00447 This routine queues up the request to be executed in a worker thread. 00448 00449 Arguments: 00450 00451 Function - Routine that is called by the worker thread 00452 00453 Context - Opaque pointer passed in as an argument to WorkerProc 00454 00455 Flags - Can be: 00456 00457 WT_EXECUTEINIOTHREAD - Specifies that the WorkerProc should be invoked 00458 by a thread that is never destroyed when there are pending IO requests. 00459 This can be used by threads that invoke I/O and/or schedule APCs. 00460 00461 The below flag can also be set: 00462 WT_EXECUTELONGFUNCTION - Specifies that the function might block for a 00463 long duration. 00464 00465 Return Value: 00466 00467 STATUS_SUCCESS - Queued successfully. 00468 00469 STATUS_NO_MEMORY - There was not sufficient heap to perform the 00470 requested operation. 00471 00472 --*/ 00473 00474 { 00475 ULONG Threshold ; 00476 ULONG CurrentTickCount ; 00477 NTSTATUS Status = STATUS_SUCCESS ; 00478 00479 00480 // Make sure the worker thread pool is initialized 00481 00482 if (CompletedWorkerInitialization != 1) { 00483 00484 Status = RtlpInitializeWorkerThreadPool () ; 00485 00486 if (! NT_SUCCESS(Status) ) 00487 return Status ; 00488 } 00489 00490 00491 // Take lock for the global worker thread pool 00492 00493 RtlEnterCriticalSection (&WorkerCriticalSection) ; 00494 00495 00496 if (Flags & (WT_EXECUTEINIOTHREAD |WT_EXECUTEINUITHREAD |WT_EXECUTEINPERSISTENTIOTHREAD) ){ 00497 00498 // 00499 // execute in IO Worker thread 00500 // 00501 00502 ULONG NumEffIOWorkerThreads = NumIOWorkerThreads - NumLongIOWorkRequests ; 00503 ULONG ThreadCreationDampingTime = NumIOWorkerThreads < NEW_THREAD_THRESHOLD 00504 ? THREAD_CREATION_DAMPING_TIME1 00505 : THREAD_CREATION_DAMPING_TIME2 ; 00506 00507 if (PersistentIOTCB && (Flags&WT_EXECUTELONGFUNCTION)) 00508 NumEffIOWorkerThreads -- ; 00509 00510 00511 // Check if we need to grow I/O worker thread pool 00512 00513 Threshold = (NumEffIOWorkerThreads < MAX_WORKER_THREADS 00514 ? NEW_THREAD_THRESHOLD * NumEffIOWorkerThreads 00515 : 0xffffffff) ; 00516 00517 if (LastThreadCreationTickCount > NtGetTickCount()) 00518 LastThreadCreationTickCount = NtGetTickCount() ; 00519 00520 if (NumEffIOWorkerThreads == 0 00521 || ((NumIOWorkRequests - NumLongIOWorkRequests > Threshold) 00522 && (LastThreadCreationTickCount + ThreadCreationDampingTime 00523 < NtGetTickCount()))) { 00524 00525 // Grow the IO worker thread pool 00526 00527 Status = RtlpStartIOWorkerThread () ; 00528 00529 } 00530 00531 if (Status == STATUS_SUCCESS) { 00532 00533 // Queue the work request 00534 00535 Status = RtlpQueueIOWorkerRequest (Function, Context, Flags) ; 00536 } 00537 00538 00539 } else { 00540 00541 // 00542 // execute in regular worker thread 00543 // 00544 00545 ULONG NumEffWorkerThreads = (NumWorkerThreads - NumLongWorkRequests) ; 00546 ULONG ThreadCreationDampingTime = NumWorkerThreads < NEW_THREAD_THRESHOLD 00547 ? THREAD_CREATION_DAMPING_TIME1 00548 : (NumWorkerThreads < 50 00549 ? THREAD_CREATION_DAMPING_TIME2 00550 : NumWorkerThreads << 7); // *100ms 00551 00552 // if io completion set, then have 1 more thread 00553 00554 if (NumMinWorkerThreads && NumEffWorkerThreads) 00555 NumEffWorkerThreads -- ; 00556 00557 00558 // Check if we need to grow worker thread pool 00559 00560 Threshold = (NumWorkerThreads < MAX_WORKER_THREADS 00561 ? (NumEffWorkerThreads < 7 00562 ? NumEffWorkerThreads*NumEffWorkerThreads 00563 : NEW_THREAD_THRESHOLD * NumEffWorkerThreads ) 00564 : 0xffffffff) ; 00565 00566 if (LastThreadCreationTickCount > NtGetTickCount()) 00567 LastThreadCreationTickCount = NtGetTickCount() ; 00568 00569 if (NumEffWorkerThreads == 0 || 00570 ( (NumWorkRequests - NumLongWorkRequests >= Threshold) 00571 && (LastThreadCreationTickCount + ThreadCreationDampingTime 00572 < NtGetTickCount()))) 00573 { 00574 00575 // Grow the worker thread pool 00576 00577 Status = RtlpStartWorkerThread () ; 00578 00579 } 00580 00581 // Queue the work request 00582 00583 if (Status == STATUS_SUCCESS) { 00584 00585 Status = RtlpQueueWorkerRequest (Function, Context, Flags) ; 00586 } 00587 00588 } 00589 00590 // Release lock on the worker thread pool 00591 00592 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 00593 00594 return Status ; 00595 } 00596 00597 00598 00599 NTSTATUS 00600 RtlSetIoCompletionCallback ( 00601 IN HANDLE FileHandle, 00602 IN APC_CALLBACK_FUNCTION CompletionProc, 00603 IN ULONG Flags 00604 ) 00605 00606 /*++ 00607 00608 Routine Description: 00609 00610 This routine binds an Handle and an associated callback function to the 00611 IoCompletionPort which queues work items to worker threads. 00612 00613 Arguments: 00614 00615 Handle - handle to be bound to the IO completion port 00616 00617 CompletionProc - callback function to be executed when an IO request 00618 pending on the IO handle completes. 00619 00620 Flags - Reserved. pass 0. 00621 00622 --*/ 00623 00624 { 00625 IO_STATUS_BLOCK IoSb ; 00626 FILE_COMPLETION_INFORMATION CompletionInfo ; 00627 NTSTATUS Status; 00628 00629 00630 // Make sure that the worker thread pool is initialized as the file handle 00631 // is bound to IO completion port. 00632 00633 if (CompletedWorkerInitialization != 1) { 00634 00635 Status = RtlpInitializeWorkerThreadPool () ; 00636 00637 if (! NT_SUCCESS(Status) ) 00638 return Status ; 00639 00640 } 00641 00642 00643 // 00644 // from now on NumMinWorkerThreads should be 1. If there is only 1 worker thread 00645 // create a new one. 00646 // 00647 00648 if ( NumMinWorkerThreads == 0 ) { 00649 00650 // Take lock for the global worker thread pool 00651 00652 RtlEnterCriticalSection (&WorkerCriticalSection) ; 00653 00654 if (NumWorkerThreads == 0) { 00655 00656 Status = RtlpStartWorkerThread () ; 00657 00658 if ( ! NT_SUCCESS(Status) ) { 00659 00660 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 00661 return Status ; 00662 } 00663 } 00664 00665 // from now on, there will be at least 1 worker thread 00666 NumMinWorkerThreads = 1 ; 00667 00668 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 00669 00670 } 00671 00672 00673 // bind to IoCompletionPort, which queues work items to worker threads 00674 00675 CompletionInfo.Port = WorkerCompletionPort ; 00676 CompletionInfo.Key = (PVOID) CompletionProc ; 00677 00678 Status = NtSetInformationFile ( 00679 FileHandle, 00680 &IoSb, //not initialized 00681 &CompletionInfo, 00682 sizeof(CompletionInfo), 00683 FileCompletionInformation //enum flag 00684 ) ; 00685 return Status ; 00686 } 00687 00688 00689 00690 NTSTATUS 00691 RtlCreateTimerQueue( 00692 OUT PHANDLE TimerQueueHandle 00693 ) 00694 00695 /*++ 00696 00697 Routine Description: 00698 00699 This routine creates a queue that can be used to queue time based tasks. 00700 00701 Arguments: 00702 00703 TimerQueueHandle - Returns back the Handle identifying the timer queue created. 00704 00705 Return Value: 00706 00707 NTSTATUS - Result code from call. The following are returned 00708 00709 STATUS_SUCCESS - Timer Queue created successfully. 00710 00711 STATUS_NO_MEMORY - There was not sufficient heap to perform the 00712 requested operation. 00713 00714 --*/ 00715 00716 { 00717 PRTLP_TIMER_QUEUE Queue ; 00718 NTSTATUS Status; 00719 00720 00721 // Initialize the timer component if it hasnt been done already 00722 00723 if (CompletedTimerInitialization != 1) { 00724 00725 Status = RtlpInitializeTimerThreadPool () ; 00726 00727 if ( !NT_SUCCESS(Status) ) 00728 return Status ; 00729 00730 } 00731 00732 00733 InterlockedIncrement( &NumTimerQueues ) ; 00734 00735 00736 // Allocate a Queue structure 00737 00738 Queue = (PRTLP_TIMER_QUEUE) RtlpAllocateTPHeap ( 00739 sizeof (RTLP_TIMER_QUEUE), 00740 HEAP_ZERO_MEMORY 00741 ) ; 00742 00743 if (Queue == NULL) { 00744 00745 InterlockedDecrement( &NumTimerQueues ) ; 00746 00747 return STATUS_NO_MEMORY ; 00748 } 00749 00750 Queue->RefCount = 1 ; 00751 00752 00753 // Initialize the allocated queue 00754 00755 InitializeListHead (&Queue->List) ; 00756 InitializeListHead (&Queue->TimerList) ; 00757 InitializeListHead (&Queue->UncancelledTimerList) ; 00758 SET_SIGNATURE( Queue ) ; 00759 00760 Queue->DeltaFiringTime = 0 ; 00761 00762 #if DBG1 00763 Queue->DbgId = ++NextTimerDbgId ; 00764 Queue->ThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ; 00765 if (DPRN0) 00766 DbgPrint("<%d:%d> TimerQueue %x created by thread:<%x:%x>\n\n", 00767 Queue->DbgId, 1, (ULONG_PTR)Queue, 00768 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 00769 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ; 00770 #endif 00771 00772 *TimerQueueHandle = Queue ; 00773 00774 return STATUS_SUCCESS ; 00775 } 00776 00777 00778 NTSTATUS 00779 RtlDeleteTimerQueue( 00780 IN HANDLE TimerQueueHandle 00781 ) 00782 00783 /*++ 00784 00785 Routine Description: 00786 00787 This routine deletes a previously created queue. This call is non-blocking and 00788 can be made from Callbacks. Pending callbacks already queued to worker threads 00789 are not cancelled. 00790 00791 Arguments: 00792 00793 TimerQueueHandle - Handle identifying the timer queue created. 00794 00795 Return Value: 00796 00797 NTSTATUS - Result code from call. 00798 00799 STATUS_PENDING - Timer Queue created successfully. 00800 00801 --*/ 00802 00803 { 00804 return RtlDeleteTimerQueueEx( TimerQueueHandle, NULL ) ; 00805 } 00806 00807 00808 NTSTATUS 00809 RtlDeleteTimerQueueEx ( 00810 HANDLE QueueHandle, 00811 HANDLE Event 00812 ) 00813 /*++ 00814 00815 Routine Description: 00816 00817 This routine deletes the queue specified in the Request and frees all timers. 00818 This call is blocking or non-blocking depending on the value passed for Event. 00819 Blocking calls cannot be made from ANY Timer callbacks. After this call returns, 00820 no new Callbacks will be fired for any timer associated with the queue. 00821 00822 Arguments: 00823 00824 QueueHandle - queue to delete 00825 00826 Event - Event to wait upon. 00827 (HANDLE)-1: The function creates an event and waits on it. 00828 Event : The caller passes an event. The function marks the queue for deletion, 00829 but does not wait for all callbacks to complete. The event is 00830 signalled after all callbacks have completed. 00831 NULL : The function is non-blocking. The function marks the queue for deletion, 00832 but does not wait for all callbacks to complete. 00833 00834 Return Value: 00835 00836 STATUS_SUCCESS - All timer callbacks have completed. 00837 STATUS_PENDING - Non-Blocking call. Some timer callbacks associated with timers 00838 in this queue may not have completed. 00839 00840 --*/ 00841 { 00842 NTSTATUS Status; 00843 LARGE_INTEGER TimeOut ; 00844 PRTLP_EVENT CompletionEvent = NULL ; 00845 PRTLP_TIMER_QUEUE Queue = (PRTLP_TIMER_QUEUE)QueueHandle ; 00846 00847 if (!Queue) { 00848 ASSERT( FALSE ) ; 00849 return STATUS_INVALID_PARAMETER ; 00850 } 00851 00852 CHECK_DEL_SIGNATURE( Queue ) ; 00853 SET_DEL_SIGNATURE( Queue ) ; 00854 00855 00856 #if DBG1 00857 Queue->ThreadId2 = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ; 00858 if (DPRN0) 00859 DbgPrint("\n<%d:%d> Queue Delete(Queue:%x Event:%x by Thread:<%x:%x>)\n\n", 00860 Queue->DbgId, Queue->RefCount, (ULONG_PTR)Queue, (ULONG_PTR)Event, 00861 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 00862 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ; 00863 #endif 00864 00865 00866 if (Event == (HANDLE)-1 ) { 00867 00868 // Get an event from the event cache 00869 00870 CompletionEvent = RtlpGetWaitEvent () ; 00871 00872 if (!CompletionEvent) { 00873 00874 return STATUS_NO_MEMORY ; 00875 00876 } 00877 } 00878 00879 Queue->CompletionEvent = CompletionEvent 00880 ? CompletionEvent->Handle 00881 : Event ; 00882 00883 00884 // once this flag is set, no timer will be fired 00885 00886 ACQUIRE_GLOBAL_TIMER_LOCK(); 00887 Queue->State |= STATE_DONTFIRE; 00888 RELEASE_GLOBAL_TIMER_LOCK(); 00889 00890 00891 00892 // queue an APC 00893 00894 Status = NtQueueApcThread( 00895 TimerThreadHandle, 00896 (PPS_APC_ROUTINE)RtlpDeleteTimerQueue, 00897 (PVOID) QueueHandle, 00898 NULL, 00899 NULL 00900 ); 00901 00902 if (! NT_SUCCESS(Status)) { 00903 00904 RtlpFreeWaitEvent( CompletionEvent ) ; 00905 00906 return Status ; 00907 } 00908 00909 if (CompletionEvent) { 00910 00911 // wait for Event to be fired. Return if the thread has been killed. 00912 00913 00914 #if DBG1 00915 if (DPRN0) 00916 DbgPrint("<%x> Queue delete waiting Thread<%d:%d>\n\n", 00917 (ULONG_PTR)Queue, 00918 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 00919 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ; 00920 #endif 00921 00922 00923 Status = RtlpWaitForEvent( CompletionEvent->Handle, TimerThreadHandle ) ; 00924 00925 00926 #if DBG1 00927 if (DPRN0) 00928 DbgPrint("<%x> Queue delete completed\n\n", (ULONG_PTR) Queue) ; 00929 #endif 00930 00931 RtlpFreeWaitEvent( CompletionEvent ) ; 00932 00933 return NT_SUCCESS( Status ) ? STATUS_SUCCESS : Status ; 00934 00935 } else { 00936 00937 return STATUS_PENDING ; 00938 } 00939 } 00940 00941 00942 00943 NTSTATUS 00944 RtlCreateTimer( 00945 IN HANDLE TimerQueueHandle, 00946 OUT HANDLE *Handle, 00947 IN WAITORTIMERCALLBACKFUNC Function, 00948 IN PVOID Context, 00949 IN ULONG DueTime, 00950 IN ULONG Period, 00951 IN ULONG Flags 00952 ) 00953 /*++ 00954 00955 Routine Description: 00956 00957 This routine puts a timer request in the queue identified in by TimerQueueHandle. 00958 The timer request can be one shot or periodic. 00959 00960 Arguments: 00961 00962 TimerQueueHandle - Handle identifying the timer queue in which to insert the timer 00963 request. 00964 00965 Handle - Specifies a location to return a handle to this timer request 00966 00967 Function - Routine that is called when the timer fires 00968 00969 Context - Opaque pointer passed in as an argument to WorkerProc 00970 00971 DueTime - Specifies the time in milliseconds after which the timer fires. 00972 00973 Period - Specifies the period of the timer in milliseconds. This should be 0 for 00974 one shot requests. 00975 00976 Flags - Can be one of: 00977 00978 WT_EXECUTEINTIMERTHREAD - if WorkerProc should be invoked in the wait thread 00979 it this should only be used for small routines. 00980 00981 WT_EXECUTELONGFUNCTION - if WorkerProc can possibly block for a long time. 00982 00983 WT_EXECUTEINIOTHREAD - if WorkerProc should be invoked in IO worker thread 00984 00985 Return Value: 00986 00987 NTSTATUS - Result code from call. The following are returned 00988 00989 STATUS_SUCCESS - Timer Queue created successfully. 00990 00991 STATUS_NO_MEMORY - There was not sufficient heap to perform the 00992 requested operation. 00993 00994 --*/ 00995 00996 { 00997 NTSTATUS Status; 00998 PRTLP_TIMER Timer ; 00999 01000 Timer = (PRTLP_TIMER) RtlpAllocateTPHeap ( 01001 sizeof (RTLP_TIMER), 01002 HEAP_ZERO_MEMORY 01003 ) ; 01004 01005 if (Timer == NULL) { 01006 01007 return STATUS_NO_MEMORY ; 01008 01009 } 01010 01011 // Initialize the allocated timer 01012 01013 Timer->DeltaFiringTime = DueTime ; 01014 Timer->Queue = (PRTLP_TIMER_QUEUE) TimerQueueHandle ; 01015 Timer->RefCount = 1 ; 01016 Timer->Flags = Flags ; 01017 Timer->Function = Function ; 01018 Timer->Context = Context ; 01019 //todo:remove below 01020 Timer->Period = (Period == -1) ? 0 : Period; 01021 InitializeListHead( &Timer->TimersToFireList ) ; 01022 SET_SIGNATURE( Timer ) ; 01023 01024 01025 #if DBG1 01026 Timer->DbgId = ++ Timer->Queue->NextDbgId ; 01027 Timer->ThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ; 01028 if (DPRN1) 01029 DbgPrint("\n<%d:%d:%d> Timer: created by Thread:<%x:%x>\n\n", 01030 Timer->Queue->DbgId, Timer->DbgId, 1, 01031 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 01032 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ; 01033 #endif 01034 01035 *Handle = Timer ; 01036 01037 01038 // Increment the total number of timers in the queue 01039 01040 InterlockedIncrement( &((PRTLP_TIMER_QUEUE)TimerQueueHandle)->RefCount ) ; 01041 01042 01043 // Queue APC to timer thread 01044 01045 Status = NtQueueApcThread( 01046 TimerThreadHandle, 01047 (PPS_APC_ROUTINE)RtlpAddTimer, 01048 (PVOID)Timer, 01049 NULL, 01050 NULL 01051 ) ; 01052 01053 return Status ; 01054 } 01055 01056 01057 NTSTATUS 01058 RtlUpdateTimer( 01059 IN HANDLE TimerQueueHandle, 01060 IN HANDLE Timer, 01061 IN ULONG DueTime, 01062 IN ULONG Period 01063 ) 01064 /*++ 01065 01066 Routine Description: 01067 01068 This routine updates the timer 01069 01070 Arguments: 01071 01072 TimerQueueHandle - Handle identifying the queue in which the timer to be updated exists 01073 01074 Timer - Specifies a handle to the timer which needs to be updated 01075 01076 DueTime - Specifies the time in milliseconds after which the timer fires. 01077 01078 Period - Specifies the period of the timer in milliseconds. This should be 01079 0 for one shot requests. 01080 01081 Return Value: 01082 01083 NTSTATUS - Result code from call. The following are returned 01084 01085 STATUS_SUCCESS - Timer updated successfully. 01086 01087 --*/ 01088 { 01089 NTSTATUS Status; 01090 PRTLP_TIMER TmpTimer ; 01091 01092 if (!TimerQueueHandle || !Timer) { 01093 ASSERT( FALSE ) ; 01094 return STATUS_INVALID_PARAMETER ; 01095 } 01096 01097 CHECK_DEL_SIGNATURE( (PRTLP_TIMER)Timer ) ; 01098 01099 01100 TmpTimer = (PRTLP_TIMER) RtlpAllocateTPHeap ( 01101 sizeof (RTLP_TIMER), 01102 0 01103 ) ; 01104 01105 if (TmpTimer == NULL) { 01106 01107 return STATUS_NO_MEMORY ; 01108 } 01109 01110 TmpTimer->DeltaFiringTime = DueTime; 01111 //todo:remove below 01112 if (Period==-1) Period = 0; 01113 TmpTimer->Period = Period ; 01114 01115 #if DBG1 01116 ((PRTLP_TIMER)Timer)->ThreadId2 = 01117 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ; 01118 #endif 01119 #if DBG1 01120 if (DPRN1) 01121 DbgPrint("<%d:%d:%d> Timer: updated by Thread:<%x:%x>\n\n", 01122 ((PRTLP_TIMER)Timer)->Queue->DbgId, 01123 ((PRTLP_TIMER)Timer)->DbgId, ((PRTLP_TIMER)Timer)->RefCount, 01124 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 01125 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ; 01126 #endif 01127 01128 01129 // queue APC to update timer 01130 01131 Status = NtQueueApcThread ( 01132 TimerThreadHandle, 01133 (PPS_APC_ROUTINE)RtlpUpdateTimer, 01134 (PVOID)Timer, //Actual timer 01135 (PVOID)TmpTimer, 01136 NULL 01137 ); 01138 01139 01140 return Status ; 01141 } 01142 01143 01144 NTSTATUS 01145 RtlDeleteTimer ( 01146 IN HANDLE TimerQueueHandle, 01147 IN HANDLE TimerToCancel, 01148 IN HANDLE Event 01149 ) 01150 /*++ 01151 01152 Routine Description: 01153 01154 This routine cancels the timer 01155 01156 Arguments: 01157 01158 TimerQueueHandle - Handle identifying the queue from which to delete timer 01159 01160 TimerToCancel - Handle identifying the timer to cancel 01161 01162 Event - Event to be signalled when the timer is deleted 01163 (HANDLE)-1: The function creates an event and waits on it. 01164 Event : The caller passes an event. The function marks the timer for deletion, 01165 but does not wait for all callbacks to complete. The event is 01166 signalled after all callbacks have completed. 01167 NULL : The function is non-blocking. The function marks the timer for deletion, 01168 but does not wait for all callbacks to complete. 01169 01170 Return Value: 01171 01172 NTSTATUS - Result code from call. The following are returned 01173 01174 STATUS_SUCCESS - Timer cancelled. No pending callbacks. 01175 STATUS_PENDING - Timer cancelled. Some callbacks still not completed. 01176 01177 --*/ 01178 { 01179 NTSTATUS Status; 01180 PRTLP_EVENT CompletionEvent = NULL ; 01181 PRTLP_TIMER Timer = (PRTLP_TIMER) TimerToCancel ; 01182 ULONG TimerRefCount ; 01183 #if DBG1 01184 ULONG QueueDbgId ; 01185 #endif 01186 01187 01188 if (!TimerQueueHandle || !TimerToCancel) { 01189 ASSERT( FALSE ) ; 01190 return STATUS_INVALID_PARAMETER ; 01191 } 01192 01193 #if DBG1 01194 QueueDbgId = Timer->Queue->DbgId ; 01195 #endif 01196 01197 01198 CHECK_DEL_SIGNATURE( Timer ) ; 01199 SET_DEL_SIGNATURE( Timer ) ; 01200 CHECK_DEL_SIGNATURE( (PRTLP_TIMER_QUEUE)TimerQueueHandle ) ; 01201 01202 01203 if (Event == (HANDLE)-1 ) { 01204 01205 // Get an event from the event cache 01206 01207 CompletionEvent = RtlpGetWaitEvent () ; 01208 01209 if (!CompletionEvent) { 01210 01211 return STATUS_NO_MEMORY ; 01212 } 01213 } 01214 01215 #if DBG1 01216 Timer->ThreadId2 = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ; 01217 #endif 01218 #if DBG1 01219 if (DPRN0) 01220 DbgPrint("\n<%d:%d:%d> Timer: Cancel:(Timer:%x, Event:%x)\n\n", 01221 Timer->Queue->DbgId, Timer->DbgId, Timer->RefCount, 01222 (ULONG_PTR)Timer, (ULONG_PTR)Event) ; 01223 #endif 01224 01225 Timer->CompletionEvent = CompletionEvent 01226 ? CompletionEvent->Handle 01227 : Event ; 01228 01229 01230 ACQUIRE_GLOBAL_TIMER_LOCK(); 01231 Timer->State |= STATE_DONTFIRE ; 01232 TimerRefCount = Timer->RefCount ; 01233 RELEASE_GLOBAL_TIMER_LOCK(); 01234 01235 01236 Status = NtQueueApcThread( 01237 TimerThreadHandle, 01238 (PPS_APC_ROUTINE)RtlpCancelTimer, 01239 (PVOID)TimerToCancel, 01240 NULL, 01241 NULL 01242 ); 01243 01244 if (! NT_SUCCESS(Status)) { 01245 01246 RtlpFreeWaitEvent( CompletionEvent ) ; 01247 01248 return Status ; 01249 } 01250 01251 01252 01253 if ( CompletionEvent ) { 01254 01255 // wait for the event to be signalled 01256 01257 #if DBG1 01258 if (DPRN0) 01259 DbgPrint("<%d> Timer: %x: Cancel waiting Thread<%d:%d>\n\n", 01260 QueueDbgId, (ULONG_PTR)Timer, 01261 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 01262 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ; 01263 #endif 01264 01265 01266 Status = RtlpWaitForEvent( CompletionEvent->Handle, TimerThreadHandle ) ; 01267 01268 01269 #if DBG1 01270 if (DPRN0) 01271 DbgPrint("<%d> Timer: %x: Cancel waiting done\n\n", QueueDbgId, 01272 (ULONG_PTR)Timer) ; 01273 #endif 01274 01275 01276 RtlpFreeWaitEvent( CompletionEvent ) ; 01277 01278 return NT_SUCCESS(Status) ? STATUS_SUCCESS : Status ; 01279 01280 } else { 01281 01282 return (TimerRefCount > 1) ? STATUS_PENDING : STATUS_SUCCESS; 01283 } 01284 } 01285 01286 01287 01288 01289 NTSTATUS 01290 NTAPI 01291 RtlSetThreadPoolStartFunc( 01292 PRTLP_START_THREAD StartFunc, 01293 PRTLP_EXIT_THREAD ExitFunc 01294 ) 01295 /*++ 01296 01297 Routine Description: 01298 01299 This routine sets the thread pool's thread creation function. This is not 01300 thread safe, because it is intended solely for kernel32 to call for processes 01301 that aren't csrss/smss. 01302 01303 Arguments: 01304 01305 StartFunc - Function to create a new thread 01306 01307 Return Value: 01308 01309 --*/ 01310 01311 { 01312 RtlpStartThreadFunc = StartFunc ; 01313 RtlpExitThreadFunc = ExitFunc ; 01314 return STATUS_SUCCESS ; 01315 } 01316 01317 01318 01319 NTSTATUS 01320 RtlThreadPoolCleanup ( 01321 ULONG Flags 01322 ) 01323 /*++ 01324 01325 Routine Description: 01326 This routine cleans up the thread pool. 01327 01328 Arguments: 01329 01330 None 01331 01332 Return Value: 01333 01334 STATUS_SUCCESS : if none of the components are in use. 01335 STATUS_UNSUCCESSFUL : if some components are still in use. 01336 01337 --*/ 01338 { 01339 BOOLEAN Cleanup ; 01340 PLIST_ENTRY Node ; 01341 ULONG i ; 01342 HANDLE TmpHandle ; 01343 01344 return STATUS_UNSUCCESSFUL; 01345 01346 // cleanup timer thread 01347 01348 IS_COMPONENT_INITIALIZED(StartedTimerInitialization, 01349 CompletedTimerInitialization, 01350 Cleanup ) ; 01351 01352 if ( Cleanup ) { 01353 01354 ACQUIRE_GLOBAL_TIMER_LOCK() ; 01355 01356 if (NumTimerQueues != 0 ) { 01357 01358 ASSERTMSG( FALSE, 01359 "Trying to deinitialize ThreadPool when timers exist\n" ) ; 01360 RELEASE_GLOBAL_TIMER_LOCK() ; 01361 01362 return STATUS_UNSUCCESSFUL ; 01363 } 01364 01365 NtQueueApcThread( 01366 TimerThreadHandle, 01367 (PPS_APC_ROUTINE)RtlpThreadCleanup, 01368 NULL, 01369 NULL, 01370 NULL 01371 ); 01372 01373 NtClose( TimerThreadHandle ) ; 01374 TimerThreadHandle = NULL ; 01375 01376 RELEASE_GLOBAL_TIMER_LOCK() ; 01377 01378 } 01379 01380 01381 // 01382 // cleanup wait threads 01383 // 01384 01385 IS_COMPONENT_INITIALIZED(StartedWaitInitialization, 01386 CompletedWaitInitialization, 01387 Cleanup ) ; 01388 01389 if ( Cleanup ) { 01390 01391 PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB ; 01392 01393 ACQUIRE_GLOBAL_WAIT_LOCK() ; 01394 01395 // Queue an APC to all Wait Threads 01396 01397 for (Node = WaitThreads.Flink ; Node != &WaitThreads ; 01398 Node = Node->Flink) 01399 { 01400 01401 ThreadCB = CONTAINING_RECORD(Node, 01402 RTLP_WAIT_THREAD_CONTROL_BLOCK, 01403 WaitThreadsList) ; 01404 01405 if ( ThreadCB->NumWaits != 0 ) { 01406 01407 ASSERTMSG( FALSE, 01408 "Cannot cleanup ThreadPool. Registered Wait events exist." ) ; 01409 RELEASE_GLOBAL_WAIT_LOCK( ) ; 01410 01411 return STATUS_UNSUCCESSFUL ; 01412 } 01413 01414 RemoveEntryList( &ThreadCB->WaitThreadsList ) ; 01415 TmpHandle = ThreadCB->ThreadHandle ; 01416 01417 NtQueueApcThread( 01418 ThreadCB->ThreadHandle, 01419 (PPS_APC_ROUTINE)RtlpThreadCleanup, 01420 NULL, 01421 NULL, 01422 NULL 01423 ); 01424 01425 NtClose( TmpHandle ) ; 01426 } 01427 01428 RELEASE_GLOBAL_WAIT_LOCK( ) ; 01429 01430 } 01431 01432 01433 // cleanup worker threads 01434 01435 IS_COMPONENT_INITIALIZED( StartedWorkerInitialization, 01436 CompletedWorkerInitialization, 01437 Cleanup ) ; 01438 01439 if ( Cleanup ) { 01440 01441 RtlEnterCriticalSection (&WorkerCriticalSection) ; 01442 01443 if ( (NumWorkRequests != 0) || (NumIOWorkRequests != 0) ) { 01444 01445 ASSERTMSG( FALSE, 01446 "Cannot cleanup ThreadPool. Work requests pending." ) ; 01447 01448 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 01449 01450 return STATUS_UNSUCCESSFUL ; 01451 } 01452 01453 // queue a cleanup for each worker thread 01454 01455 for (i = 0 ; i < NumWorkerThreads ; i ++ ) { 01456 01457 NtSetIoCompletion ( 01458 WorkerCompletionPort, 01459 RtlpThreadCleanup, 01460 NULL, 01461 STATUS_SUCCESS, 01462 0 01463 ); 01464 } 01465 01466 // queue an apc to cleanup all IO worker threads 01467 01468 for (Node = IOWorkerThreads.Flink ; Node != &IOWorkerThreads ; 01469 Node = Node->Flink ) 01470 { 01471 PRTLP_IOWORKER_TCB ThreadCB ; 01472 01473 ThreadCB = CONTAINING_RECORD (Node, RTLP_IOWORKER_TCB, List) ; 01474 RemoveEntryList( &ThreadCB->List) ; 01475 TmpHandle = ThreadCB->ThreadHandle ; 01476 01477 NtQueueApcThread( 01478 ThreadCB->ThreadHandle, 01479 (PPS_APC_ROUTINE)RtlpThreadCleanup, 01480 NULL, 01481 NULL, 01482 NULL 01483 ); 01484 01485 NtClose( TmpHandle ) ; 01486 } 01487 01488 NumWorkerThreads = NumIOWorkerThreads = 0 ; 01489 01490 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 01491 01492 } 01493 01494 return STATUS_SUCCESS ; 01495 01496 } 01497 01498 01499 // Private Functions 01500 01501 01502 // Worker functions 01503 01504 01505 NTSTATUS 01506 RtlpQueueWorkerRequest ( 01507 WORKERCALLBACKFUNC Function, 01508 PVOID Context, 01509 ULONG Flags 01510 ) 01511 /*++ 01512 01513 Routine Description: 01514 01515 This routine queues up the request to be executed in a worker thread. 01516 01517 Arguments: 01518 01519 Function - Routine that is called by the worker thread 01520 01521 Context - Opaque pointer passed in as an argument to WorkerProc 01522 01523 Flags - flags passed to RtlQueueWorkItem 01524 01525 Return Value: 01526 01527 --*/ 01528 01529 { 01530 NTSTATUS Status ; 01531 PRTLP_WORK WorkEntry ; 01532 01533 // Increment the outstanding work request counter 01534 01535 InterlockedIncrement (&NumWorkRequests) ; 01536 if (Flags & WT_EXECUTELONGFUNCTION) { 01537 InterlockedIncrement( & NumLongWorkRequests ) ; 01538 } 01539 01540 WorkEntry = (PRTLP_WORK) RtlpForceAllocateTPHeap ( sizeof (RTLP_WORK), 01541 HEAP_ZERO_MEMORY) ; 01542 WorkEntry->Function = Function ; 01543 WorkEntry->Flags = Flags ; 01544 01545 if (Flags & WT_EXECUTEINPERSISTENTTHREAD) { 01546 01547 // Queue APC to timer thread 01548 01549 Status = NtQueueApcThread( 01550 TimerThreadHandle, 01551 (PPS_APC_ROUTINE)RtlpExecuteWorkerRequest, 01552 (PVOID) STATUS_SUCCESS, 01553 (PVOID) Context, 01554 (PVOID) WorkEntry 01555 ) ; 01556 01557 } else { 01558 01559 Status = NtSetIoCompletion ( 01560 WorkerCompletionPort, 01561 RtlpExecuteWorkerRequest, 01562 (PVOID) WorkEntry, 01563 STATUS_SUCCESS, 01564 (ULONG_PTR)Context 01565 ); 01566 } 01567 01568 if ( ! NT_SUCCESS(Status) ) { 01569 01570 InterlockedDecrement (&NumWorkRequests) ; 01571 if (Flags && WT_EXECUTELONGFUNCTION) { 01572 InterlockedDecrement( &NumLongWorkRequests ) ; 01573 } 01574 01575 RtlpFreeTPHeap( WorkEntry ) ; 01576 01577 ASSERT(FALSE) ; 01578 01579 #if DBG 01580 DbgPrint("ERROR!! Thread Pool (RtlQeueuWorkItem): could not queue work item\n"); 01581 #endif 01582 } 01583 01584 return Status ; 01585 } 01586 01587 01588 VOID 01589 RtlpExecuteWorkerRequest ( 01590 NTSTATUS Status, //not used 01591 PVOID Context, 01592 PVOID WorkContext 01593 ) 01594 /*++ 01595 01596 Routine Description: 01597 01598 This routine executes a work item. 01599 01600 Arguments: 01601 01602 Context - contains context to be passed to the callback function. 01603 01604 WorkContext - contains callback function ptr and flags 01605 01606 Return Value: 01607 01608 Notes: 01609 This function executes in a worker thread or a timer thread if 01610 WT_EXECUTEINTIMERTHREAD flag is set. 01611 01612 --*/ 01613 01614 { 01615 PRTLP_WORK WorkEntry = (PRTLP_WORK) WorkContext; 01616 01617 #if (DBG1) 01618 DBG_SET_FUNCTION( WorkEntry->Function, Context ) ; 01619 #endif 01620 01621 try { 01622 ((WORKERCALLBACKFUNC) WorkEntry->Function) ( Context ) ; 01623 01624 } except (EXCEPTION_EXECUTE_HANDLER) { 01625 // ASSERT(FALSE); 01626 } 01627 01628 InterlockedDecrement( &NumWorkRequests ) ; 01629 if (WorkEntry->Flags & WT_EXECUTELONGFUNCTION) { 01630 InterlockedDecrement( &NumLongWorkRequests ) ; 01631 } 01632 01633 RtlpFreeTPHeap( WorkEntry ) ; 01634 } 01635 01636 01637 NTSTATUS 01638 RtlpQueueIOWorkerRequest ( 01639 WORKERCALLBACKFUNC Function, 01640 PVOID Context, 01641 ULONG Flags 01642 ) 01643 01644 /*++ 01645 01646 Routine Description: 01647 01648 This routine queues up the request to be executed in an IO worker thread. 01649 01650 Arguments: 01651 01652 Function - Routine that is called by the worker thread 01653 01654 Context - Opaque pointer passed in as an argument to WorkerProc 01655 01656 Return Value: 01657 01658 --*/ 01659 01660 { 01661 NTSTATUS Status ; 01662 PRTLP_IOWORKER_TCB TCB ; 01663 BOOLEAN LongFunction = (Flags & WT_EXECUTELONGFUNCTION) ? TRUE : FALSE ; 01664 PLIST_ENTRY ple ; 01665 01666 01667 if (Flags & WT_EXECUTEINPERSISTENTIOTHREAD) { 01668 01669 if (!PersistentIOTCB) { 01670 for (ple=IOWorkerThreads.Flink; ple!=&IOWorkerThreads; ple=ple->Flink) { 01671 TCB = CONTAINING_RECORD (ple, RTLP_IOWORKER_TCB, List) ; 01672 if (! TCB->LongFunctionFlag) 01673 break; 01674 } 01675 01676 if (ple == &IOWorkerThreads) { 01677 // ASSERT(FALSE); 01678 return STATUS_NO_MEMORY; 01679 } 01680 01681 01682 PersistentIOTCB = TCB ; 01683 TCB->Flags |= WT_EXECUTEINPERSISTENTIOTHREAD ; 01684 01685 } else { 01686 TCB = PersistentIOTCB ; 01687 } 01688 01689 } else { 01690 for (ple=IOWorkerThreads.Flink; ple!=&IOWorkerThreads; ple=ple->Flink) { 01691 01692 TCB = CONTAINING_RECORD (ple, RTLP_IOWORKER_TCB, List) ; 01693 01694 // do not queue to the thread if it is executing a long function, or 01695 // if you are queueing a long function and the thread is a persistent thread 01696 01697 if (! TCB->LongFunctionFlag 01698 && (! ((TCB->Flags&WT_EXECUTEINPERSISTENTIOTHREAD) 01699 && (Flags&WT_EXECUTELONGFUNCTION)))) { 01700 break ; 01701 } 01702 01703 } 01704 01705 if ((ple == &IOWorkerThreads) && (NumIOWorkerThreads<1)) { 01706 01707 #if DBG 01708 DbgPrint("ThreadPool:ntdll.dll: Out of memory. " 01709 "Could not execute IOWorkItem(%x)\n", (ULONG_PTR)Function); 01710 #endif 01711 01712 return STATUS_NO_MEMORY; 01713 } 01714 else { 01715 ple = IOWorkerThreads.Flink; 01716 TCB = CONTAINING_RECORD (ple, RTLP_IOWORKER_TCB, List) ; 01717 01718 // treat it as a short function so that counters work fine. 01719 01720 LongFunction = FALSE; 01721 } 01722 01723 // In order to implement "fair" assignment of work items between IO worker threads 01724 // each time remove the entry and reinsert at back. 01725 01726 RemoveEntryList (&TCB->List) ; 01727 InsertTailList (&IOWorkerThreads, &TCB->List) ; 01728 } 01729 01730 01731 // Increment the outstanding work request counter 01732 01733 InterlockedIncrement (&NumIOWorkRequests) ; 01734 if (LongFunction) { 01735 InterlockedIncrement( &NumLongIOWorkRequests ) ; 01736 TCB->LongFunctionFlag = TRUE ; 01737 } 01738 01739 // Queue an APC to the IoWorker Thread 01740 01741 Status = NtQueueApcThread( 01742 TCB->ThreadHandle, 01743 LongFunction? (PPS_APC_ROUTINE)RtlpExecuteLongIOWorkItem: 01744 (PPS_APC_ROUTINE)RtlpExecuteIOWorkItem, 01745 (PVOID)Function, 01746 Context, 01747 TCB 01748 ); 01749 01750 if (! NT_SUCCESS( Status ) ) { 01751 InterlockedDecrement( &NumIOWorkRequests ) ; 01752 if (LongFunction) 01753 InterlockedDecrement( &NumLongIOWorkRequests ) ; 01754 } 01755 01756 return Status ; 01757 01758 } 01759 01760 01761 01762 NTSTATUS 01763 RtlpStartWorkerThread ( 01764 ) 01765 /*++ 01766 01767 Routine Description: 01768 01769 This routine starts a regular worker thread 01770 01771 Arguments: 01772 01773 01774 Return Value: 01775 01776 NTSTATUS error codes resulting from attempts to create a thread 01777 STATUS_SUCCESS 01778 01779 --*/ 01780 { 01781 HANDLE ThreadHandle ; 01782 ULONG CurrentTickCount ; 01783 NTSTATUS Status ; 01784 01785 // Create worker thread 01786 01787 Status = RtlpStartThreadFunc (RtlpWorkerThread, &ThreadHandle) ; 01788 01789 if (Status == STATUS_SUCCESS ) { 01790 01791 // Update the time at which the current thread was created 01792 01793 LastThreadCreationTickCount = NtGetTickCount() ; 01794 01795 // Increment the count of the thread type created 01796 01797 InterlockedIncrement(&NumWorkerThreads) ; 01798 01799 // Close Thread handle, we dont need it. 01800 01801 NtClose (ThreadHandle) ; 01802 01803 } else { 01804 01805 // Thread creation failed. If there is even one thread present do not return 01806 // failure - else queue the request anyway. 01807 01808 if (NumWorkerThreads == 0) { 01809 01810 return Status ; 01811 } 01812 01813 } 01814 01815 return STATUS_SUCCESS ; 01816 } 01817 01818 01819 NTSTATUS 01820 RtlpStartIOWorkerThread ( 01821 ) 01822 /*++ 01823 01824 Routine Description: 01825 01826 This routine starts an I/O worker thread 01827 01828 Arguments: 01829 01830 01831 Return Value: 01832 01833 NTSTATUS error codes resulting from attempts to create a thread 01834 STATUS_SUCCESS 01835 01836 --*/ 01837 { 01838 HANDLE ThreadHandle ; 01839 ULONG CurrentTickCount ; 01840 NTSTATUS Status ; 01841 01842 01843 // Create worker thread 01844 01845 Status = RtlpStartThreadFunc (RtlpIOWorkerThread, &ThreadHandle) ; 01846 01847 if (Status == STATUS_SUCCESS ) { 01848 01849 NtClose( ThreadHandle ) ; 01850 01851 // Update the time at which the current thread was created 01852 01853 LastThreadCreationTickCount = NtGetTickCount() ; 01854 01855 } else { 01856 01857 // Thread creation failed. If there is even one thread present do not return 01858 // failure since we can still service the work request. 01859 01860 if (NumIOWorkerThreads == 0) { 01861 01862 return Status ; 01863 01864 } 01865 } 01866 01867 return STATUS_SUCCESS ; 01868 } 01869 01870 01871 VOID 01872 RtlpWorkerThreadTimerCallback( 01873 PVOID Context, 01874 BOOLEAN NotUsed 01875 ) 01876 /*++ 01877 01878 Routine Description: 01879 01880 This routine checks if new worker thread has to be created 01881 01882 Arguments: 01883 None 01884 01885 Return Value: 01886 None 01887 01888 --*/ 01889 { 01890 IO_COMPLETION_BASIC_INFORMATION Info ; 01891 BOOLEAN bCreateThread = FALSE ; 01892 NTSTATUS Status ; 01893 ULONG QueueLength, Threshold, ShortWorkRequests ; 01894 01895 01896 Status = NtQueryIoCompletion( 01897 WorkerCompletionPort, 01898 IoCompletionBasicInformation, 01899 &Info, 01900 sizeof(Info), 01901 NULL 01902 ) ; 01903 01904 if (!NT_SUCCESS(Status)) 01905 return ; 01906 01907 QueueLength = Info.Depth ; 01908 01909 if (!QueueLength) { 01910 OldTotalExecutedWorkRequests = TotalExecutedWorkRequests ; 01911 return ; 01912 } 01913 01914 01915 RtlEnterCriticalSection (&WorkerCriticalSection) ; 01916 01917 01918 // if there are queued work items and no new work items have been scheduled 01919 // in the last 30 seconds then create a new thread. 01920 // this will take care of deadlocks. 01921 01922 // this will create a problem only if some thread is running for a long time 01923 01924 if (TotalExecutedWorkRequests == OldTotalExecutedWorkRequests) { 01925 01926 bCreateThread = TRUE ; 01927 } 01928 01929 01930 // if there are a lot of queued work items, then create a new thread 01931 { 01932 ULONG NumEffWorkerThreads = (NumWorkerThreads - NumLongWorkRequests) ; 01933 ULONG ShortWorkRequests ; 01934 01935 Threshold = (NumWorkerThreads < MAX_WORKER_THREADS 01936 ? (NumEffWorkerThreads < 7 01937 ? NumEffWorkerThreads*NumEffWorkerThreads 01938 : NEW_THREAD_THRESHOLD * NumEffWorkerThreads ) 01939 : 0xffffffff) ; 01940 01941 ShortWorkRequests = QueueLength + NumExecutingWorkerThreads 01942 - NumLongWorkRequests ; 01943 01944 if (ShortWorkRequests > Threshold) 01945 { 01946 bCreateThread = TRUE ; 01947 } 01948 } 01949 01950 if (bCreateThread) { 01951 01952 RtlpStartWorkerThread () ; 01953 } 01954 01955 01956 OldTotalExecutedWorkRequests = TotalExecutedWorkRequests ; 01957 01958 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 01959 01960 } 01961 01962 01963 01964 NTSTATUS 01965 RtlpInitializeWorkerThreadPool ( 01966 ) 01967 /*++ 01968 01969 Routine Description: 01970 01971 This routine initializes all aspects of the thread pool. 01972 01973 Arguments: 01974 01975 None 01976 01977 Return Value: 01978 01979 None 01980 01981 --*/ 01982 { 01983 NTSTATUS Status = STATUS_SUCCESS ; 01984 LARGE_INTEGER TimeOut ; 01985 01986 01987 // Initialize the timer component if it hasnt been done already 01988 01989 if (CompletedTimerInitialization != 1) { 01990 01991 Status = RtlpInitializeTimerThreadPool () ; 01992 01993 if ( !NT_SUCCESS(Status) ) 01994 return Status ; 01995 01996 } 01997 01998 01999 // In order to avoid an explicit RtlInitialize() function to initialize the thread pool 02000 // we use StartedInitialization and CompletedInitialization to provide us the necessary 02001 // synchronization to avoid multiple threads from initializing the thread pool. 02002 // This scheme does not work if RtlInitializeCriticalSection() fails - but in this case the 02003 // caller has no choices left. 02004 02005 if (!InterlockedExchange(&StartedWorkerInitialization, 1L)) { 02006 02007 if (CompletedWorkerInitialization) 02008 InterlockedExchange( &CompletedWorkerInitialization, 0 ) ; 02009 02010 02011 do { 02012 02013 // Initialize Critical Sections 02014 02015 Status = RtlInitializeCriticalSection( &WorkerCriticalSection ); 02016 if (!NT_SUCCESS(Status)) 02017 break ; 02018 02019 02020 InitializeListHead (&IOWorkerThreads) ; 02021 02022 { 02023 SYSTEM_BASIC_INFORMATION BasicInfo; 02024 02025 // get number of processors 02026 02027 Status = NtQuerySystemInformation ( 02028 SystemBasicInformation, 02029 &BasicInfo, 02030 sizeof(BasicInfo), 02031 NULL 02032 ) ; 02033 02034 if ( !NT_SUCCESS(Status) ) { 02035 BasicInfo.NumberOfProcessors = 1 ; 02036 } 02037 02038 // Create completion port used by worker threads 02039 02040 Status = NtCreateIoCompletion ( 02041 &WorkerCompletionPort, 02042 IO_COMPLETION_ALL_ACCESS, 02043 NULL, 02044 BasicInfo.NumberOfProcessors 02045 ); 02046 02047 if (!NT_SUCCESS(Status)) 02048 break ; 02049 02050 } 02051 02052 } while ( FALSE ) ; 02053 02054 if (!NT_SUCCESS(Status) ) { 02055 02056 ASSERT ( Status == STATUS_SUCCESS ) ; 02057 StartedWorkerInitialization = 0 ; 02058 InterlockedExchange( &CompletedWorkerInitialization, ~0 ) ; 02059 return Status ; 02060 } 02061 02062 // Signal that initialization has completed 02063 02064 InterlockedExchange (&CompletedWorkerInitialization, 1L) ; 02065 02066 } else { 02067 02068 LARGE_INTEGER Timeout ; 02069 02070 // Sleep 1 ms and see if the other thread has completed initialization 02071 02072 ONE_MILLISECOND_TIMEOUT(TimeOut) ; 02073 02074 while (!(volatile ULONG) CompletedWorkerInitialization) { 02075 02076 NtDelayExecution (FALSE, &TimeOut) ; 02077 } 02078 02079 if (CompletedWorkerInitialization != 1) 02080 return STATUS_NO_MEMORY ; 02081 02082 } 02083 02084 02085 // 02086 // create timer for worker thread. it should be created outside any worker 02087 // lock and the above wait loop. It should not make the RtlpInitializeWorkerThread 02088 // call blocking on a timer thread. so queue a work item. 02089 // 02090 02091 if (NT_SUCCESS(Status) 02092 && (InterlockedIncrement(&WorkerThreadTimerQueueInit) == 1) ) 02093 { 02094 RtlQueueWorkItem(RtlpWorkerThreadInitializeTimers, NULL, 0); 02095 } 02096 02097 02098 return NT_SUCCESS(Status) ? STATUS_SUCCESS : Status ; 02099 } 02100 02101 02102 02103 VOID 02104 RtlpWorkerThreadInitializeTimers( 02105 PVOID Context 02106 ) 02107 { 02108 NTSTATUS Status; 02109 02110 02111 // create a timer that will fire every 30 sec and check if new thread 02112 // should be created 02113 02114 Status = RtlCreateTimerQueue(&WorkerThreadTimerQueue) ; 02115 if (!NT_SUCCESS(Status)) 02116 return ; 02117 02118 Status = RtlCreateTimer( 02119 WorkerThreadTimerQueue, 02120 &WorkerThreadTimer, 02121 RtlpWorkerThreadTimerCallback, 02122 NULL, 02123 30000, 02124 30000, 02125 WT_EXECUTEINTIMERTHREAD 02126 ) ; 02127 02128 return; 02129 } 02130 02131 02132 02133 02134 02135 02136 LONG 02137 RtlpWorkerThread ( 02138 PVOID Initialized 02139 ) 02140 /*++ 02141 02142 Routine Description: 02143 02144 All non I/O worker threads execute in this routine. Worker thread will try to 02145 terminate when it has not serviced a request for 02146 02147 STARTING_WORKER_SLEEP_TIME + 02148 STARTING_WORKER_SLEEP_TIME << 1 + 02149 ... 02150 STARTING_WORKER_SLEEP_TIME << MAX_WORKER_SLEEP_TIME_EXPONENT 02151 02152 Arguments: 02153 02154 Initialized - Set to 1 when we are initialized 02155 02156 Return Value: 02157 02158 --*/ 02159 { 02160 NTSTATUS Status ; 02161 PVOID WorkerProc ; 02162 PVOID Context ; 02163 IO_STATUS_BLOCK IoSb ; 02164 ULONG SleepTime ; 02165 LARGE_INTEGER TimeOut ; 02166 ULONG Terminate ; 02167 PVOID Overlapped ; 02168 02169 02170 // We are all initialized now. Notify the starter to queue the task. 02171 02172 InterlockedExchange ((ULONG *)Initialized, 1L) ; 02173 02174 02175 // Set default sleep time for 40 seconds. This time is doubled each time a timeout 02176 // occurs after which the thread terminates 02177 02178 #define WORKER_IDLE_TIMEOUT 40000 // In Milliseconds 02179 #define MAX_WORKER_SLEEP_TIME_EXPONENT 4 02180 02181 SleepTime = WORKER_IDLE_TIMEOUT ; 02182 02183 // Loop servicing I/O completion requests 02184 02185 for ( ; ; ) { 02186 02187 TimeOut.QuadPart = Int32x32To64( SleepTime, -10000 ) ; 02188 02189 Status = NtRemoveIoCompletion( 02190 WorkerCompletionPort, 02191 (PVOID) &WorkerProc, 02192 &Overlapped, 02193 &IoSb, 02194 &TimeOut 02195 ) ; 02196 02197 if (Status == STATUS_SUCCESS) { 02198 02199 02200 TotalExecutedWorkRequests ++ ;//interlocked op not req 02201 InterlockedIncrement(&NumExecutingWorkerThreads) ; 02202 02203 // Call the work item. 02204 // If IO APC, context1 contains number of IO bytes transferred, and context2 02205 // contains the overlapped structure. 02206 // If (IO)WorkerFunction, context1 contains the actual WorkerFunction to be 02207 // executed and context2 contains the actual context 02208 02209 Context = (PVOID) IoSb.Information ; 02210 02211 try { 02212 ((APC_CALLBACK_FUNCTION)WorkerProc) ( 02213 IoSb.Status, 02214 Context, // Number of IO bytes transferred 02215 Overlapped // Overlapped structure 02216 ) ; 02217 } except (EXCEPTION_EXECUTE_HANDLER) { 02218 ASSERT(FALSE); 02219 } 02220 02221 SleepTime = WORKER_IDLE_TIMEOUT ; 02222 02223 InterlockedDecrement(&NumExecutingWorkerThreads) ; 02224 02225 } else if (Status == STATUS_TIMEOUT) { 02226 02227 // NtRemoveIoCompletion timed out. Check to see if have hit our limit 02228 // on waiting. If so terminate. 02229 02230 Terminate = FALSE ; 02231 02232 RtlEnterCriticalSection (&WorkerCriticalSection) ; 02233 02234 // The thread terminates if there are > 1 threads and the queue is small 02235 // OR if there is only 1 thread and there is no request pending 02236 02237 if (NumWorkerThreads > 1) { 02238 02239 ULONG NumEffWorkerThreads = (NumWorkerThreads - NumLongWorkRequests) ; 02240 02241 if (NumEffWorkerThreads == 0) { 02242 02243 Terminate = FALSE ; 02244 02245 } else if (SleepTime >= (WORKER_IDLE_TIMEOUT << MAX_WORKER_SLEEP_TIME_EXPONENT)) { 02246 02247 // 02248 // have been idle for very long time. terminate irrespective of number of 02249 // work items. (This is useful when the set of runnable threads is taking 02250 // care of all the work items being queued). dont terminate if 02251 // (NumEffWorkerThreads == 1) 02252 // 02253 02254 if (NumEffWorkerThreads > 1) 02255 Terminate = TRUE ; 02256 02257 } else { 02258 02259 ULONG Threshold ; 02260 02261 // Check if we need to shrink worker thread pool 02262 02263 Threshold = NumEffWorkerThreads < 7 02264 ? NumEffWorkerThreads*(NumEffWorkerThreads-1) 02265 : NEW_THREAD_THRESHOLD * (NumEffWorkerThreads-1); 02266 02267 02268 02269 if (NumWorkRequests-NumLongWorkRequests < Threshold) { 02270 02271 Terminate = TRUE ; 02272 02273 } else { 02274 02275 Terminate = FALSE ; 02276 SleepTime <<= 1 ; 02277 } 02278 } 02279 02280 } else { 02281 02282 if ( (NumMinWorkerThreads == 0) && (NumWorkRequests == 0) ) { 02283 02284 // delay termination of last thread 02285 02286 if (SleepTime < (WORKER_IDLE_TIMEOUT << MAX_WORKER_SLEEP_TIME_EXPONENT)) { 02287 SleepTime <<= 1 ; 02288 Terminate = FALSE ; 02289 } 02290 else { 02291 Terminate = TRUE ; 02292 } 02293 02294 } else { 02295 02296 Terminate = FALSE ; 02297 02298 } 02299 02300 } 02301 02302 if (Terminate) { 02303 02304 THREAD_BASIC_INFORMATION ThreadInfo; 02305 ULONG IsIoPending ; 02306 HANDLE CurThreadHandle ; 02307 02308 Status = NtDuplicateObject( 02309 NtCurrentProcess(), 02310 NtCurrentThread(), 02311 NtCurrentProcess(), 02312 &CurThreadHandle, 02313 0, 02314 FALSE, 02315 DUPLICATE_SAME_ACCESS 02316 ) ; 02317 02318 ASSERT (Status == STATUS_SUCCESS) ; 02319 02320 Terminate = FALSE ; 02321 02322 Status = NtQueryInformationThread( CurThreadHandle, 02323 ThreadIsIoPending, 02324 &IsIoPending, 02325 sizeof( IsIoPending ), 02326 NULL 02327 ); 02328 if (NT_SUCCESS( Status )) { 02329 02330 if (! IsIoPending ) 02331 Terminate = TRUE ; 02332 } 02333 02334 NtClose( CurThreadHandle ) ; 02335 } 02336 02337 if (Terminate) { 02338 02339 InterlockedDecrement (&NumWorkerThreads) ; 02340 02341 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 02342 02343 RtlpExitThreadFunc( 0 ); 02344 02345 } else { 02346 02347 // This is the condition where a request was queued *after* the 02348 // thread woke up - ready to terminate because of inactivity. In 02349 // this case dont terminate - service the completion port. 02350 02351 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 02352 02353 } 02354 02355 } else { 02356 02357 ASSERT (FALSE) ; 02358 02359 } 02360 02361 } 02362 02363 02364 return 1 ; 02365 } 02366 02367 02368 02369 LONG 02370 RtlpIOWorkerThread ( 02371 PVOID Initialized 02372 ) 02373 /*++ 02374 02375 Routine Description: 02376 02377 All I/O worker threads execute in this routine. All the work requests execute as APCs 02378 in this thread. 02379 02380 Arguments: 02381 02382 Initialized - set to 1 when the initialization has completed 02383 02384 Return Value: 02385 02386 --*/ 02387 { 02388 #define IOWORKER_IDLE_TIMEOUT 40000 // In Milliseconds 02389 02390 LARGE_INTEGER TimeOut ; 02391 ULONG SleepTime = IOWORKER_IDLE_TIMEOUT ; 02392 RTLP_IOWORKER_TCB ThreadCB ; // Control Block allocated on the stack 02393 NTSTATUS Status ; 02394 BOOLEAN Terminate ; 02395 02396 02397 // 02398 // Initialize thread control block 02399 // and insert it into list of IOWorker Threads 02400 // 02401 02402 Status = NtDuplicateObject( 02403 NtCurrentProcess(), 02404 NtCurrentThread(), 02405 NtCurrentProcess(), 02406 &ThreadCB.ThreadHandle, 02407 0, 02408 FALSE, 02409 DUPLICATE_SAME_ACCESS 02410 ) ; 02411 02412 if (!NT_SUCCESS(Status)) { 02413 ASSERT (FALSE) ; 02414 InterlockedExchange ((ULONG *)Initialized, (ULONG)~0) ; 02415 return STATUS_NO_MEMORY; 02416 } 02417 02418 InsertHeadList (&IOWorkerThreads, &ThreadCB.List) ; 02419 ThreadCB.Flags = 0 ; 02420 ThreadCB.LongFunctionFlag = FALSE ; 02421 02422 InterlockedIncrement(&NumIOWorkerThreads) ; 02423 02424 02425 // We are all initialized now. Notify the starter to queue the task. 02426 02427 InterlockedExchange ((ULONG *)Initialized, 1L) ; 02428 02429 02430 02431 // Sleep alertably so that all the activity can take place 02432 // in APCs 02433 02434 for ( ; ; ) { 02435 02436 // Set timeout for IdleTimeout 02437 02438 TimeOut.QuadPart = Int32x32To64( SleepTime, -10000 ) ; 02439 02440 02441 Status = NtDelayExecution (TRUE, &TimeOut) ; 02442 02443 02444 // Status is STATUS_SUCCESS only when it has timed out 02445 02446 if (Status != STATUS_SUCCESS) { 02447 continue ; 02448 } 02449 02450 02451 // 02452 // idle timeout. check if you can terminate the thread 02453 // 02454 02455 Terminate = FALSE ; 02456 02457 RtlEnterCriticalSection (&WorkerCriticalSection) ; 02458 02459 02460 // dont terminate if it is a persistent thread 02461 02462 if (ThreadCB.Flags & WT_EXECUTEINPERSISTENTIOTHREAD) { 02463 02464 TimeOut.LowPart = 0x0; 02465 TimeOut.HighPart = 0x80000000; 02466 02467 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 02468 02469 continue ; 02470 } 02471 02472 02473 // The thread terminates if there are > 1 threads and the queue is small 02474 // OR if there is only 1 thread and there is no request pending 02475 02476 if (NumIOWorkerThreads > 1) { 02477 02478 02479 ULONG NumEffIOWorkerThreads = NumIOWorkerThreads - NumLongIOWorkRequests ; 02480 ULONG Threshold ; 02481 02482 if (NumEffIOWorkerThreads == 0) { 02483 02484 Terminate = FALSE ; 02485 02486 } else { 02487 02488 // Check if we need to shrink worker thread pool 02489 02490 Threshold = NEW_THREAD_THRESHOLD * (NumEffIOWorkerThreads-1); 02491 02492 02493 02494 if (NumIOWorkRequests-NumLongIOWorkRequests < Threshold) { 02495 02496 Terminate = TRUE ; 02497 02498 } else { 02499 02500 Terminate = FALSE ; 02501 SleepTime <<= 1 ; 02502 } 02503 } 02504 02505 } else { 02506 02507 if (NumIOWorkRequests == 0) { 02508 02509 // delay termination of last thread 02510 02511 if (SleepTime < 4*IOWORKER_IDLE_TIMEOUT) { 02512 02513 SleepTime <<= 1 ; 02514 Terminate = FALSE ; 02515 02516 } else { 02517 02518 Terminate = TRUE ; 02519 } 02520 02521 } else { 02522 02523 Terminate = FALSE ; 02524 02525 } 02526 02527 } 02528 02529 // 02530 // terminate only if no io is pending 02531 // 02532 02533 if (Terminate) { 02534 02535 NTSTATUS Status; 02536 THREAD_BASIC_INFORMATION ThreadInfo; 02537 ULONG IsIoPending ; 02538 02539 Terminate = FALSE ; 02540 02541 Status = NtQueryInformationThread( ThreadCB.ThreadHandle, 02542 ThreadIsIoPending, 02543 &IsIoPending, 02544 sizeof( IsIoPending ), 02545 NULL 02546 ); 02547 if (NT_SUCCESS( Status )) { 02548 02549 if (! IsIoPending ) 02550 Terminate = TRUE ; 02551 } 02552 } 02553 02554 if (Terminate) { 02555 02556 InterlockedDecrement (&NumIOWorkerThreads) ; 02557 02558 RemoveEntryList (&ThreadCB.List) ; 02559 NtClose( ThreadCB.ThreadHandle ) ; 02560 02561 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 02562 02563 RtlpExitThreadFunc( 0 ); 02564 02565 } else { 02566 02567 // This is the condition where a request was queued *after* the 02568 // thread woke up - ready to terminate because of inactivity. In 02569 // this case dont terminate - service the completion port. 02570 02571 RtlLeaveCriticalSection (&WorkerCriticalSection) ; 02572 02573 } 02574 } 02575 02576 return 0 ; // Keep compiler happy 02577 02578 } 02579 02580 02581 02582 VOID 02583 RtlpExecuteLongIOWorkItem ( 02584 PVOID Function, 02585 PVOID Context, 02586 PVOID ThreadCB 02587 ) 02588 /*++ 02589 02590 Routine Description: 02591 02592 Executes an IO Work function. RUNs in a APC in the IO Worker thread. 02593 02594 Arguments: 02595 02596 Function - Worker function to call 02597 02598 Context - Argument for the worker function. 02599 02600 NotUsed - Argument is not used in this function. 02601 02602 Return Value: 02603 02604 --*/ 02605 { 02606 #if (DBG1) 02607 DBG_SET_FUNCTION( Function, Context ) ; 02608 #endif 02609 02610 // Invoke the function 02611 02612 try { 02613 ((WORKERCALLBACKFUNC) Function)((PVOID)Context) ; 02614 } except (EXCEPTION_EXECUTE_HANDLER) { 02615 ASSERT(FALSE); 02616 } 02617 02618 02619 ((PRTLP_IOWORKER_TCB)ThreadCB)->LongFunctionFlag = FALSE ; 02620 02621 // Decrement pending IO requests count 02622 02623 InterlockedDecrement (&NumIOWorkRequests) ; 02624 02625 // decrement pending long funcitons 02626 02627 InterlockedDecrement (&NumLongIOWorkRequests ) ; 02628 } 02629 02630 02631 VOID 02632 RtlpExecuteIOWorkItem ( 02633 PVOID Function, 02634 PVOID Context, 02635 PVOID NotUsed 02636 ) 02637 /*++ 02638 02639 Routine Description: 02640 02641 Executes an IO Work function. RUNs in a APC in the IO Worker thread. 02642 02643 Arguments: 02644 02645 Function - Worker function to call 02646 02647 Context - Argument for the worker function. 02648 02649 NotUsed - Argument is not used in this function. 02650 02651 Return Value: 02652 02653 02654 --*/ 02655 { 02656 #if (DBG1) 02657 DBG_SET_FUNCTION( Function, Context ) ; 02658 #endif 02659 02660 // Invoke the function 02661 02662 try { 02663 ((WORKERCALLBACKFUNC) Function)((PVOID)Context) ; 02664 } except (EXCEPTION_EXECUTE_HANDLER) { 02665 ASSERT(FALSE); 02666 } 02667 02668 // Decrement pending IO requests count 02669 02670 InterlockedDecrement (&NumIOWorkRequests) ; 02671 02672 } 02673 02674 02675 NTSTATUS 02676 NTAPI 02677 RtlpStartThread ( 02678 PUSER_THREAD_START_ROUTINE Function, 02679 HANDLE *ThreadHandle 02680 ) 02681 /*++ 02682 02683 Routine Description: 02684 02685 This routine is used start a new wait thread in the pool. 02686 Arguments: 02687 02688 None 02689 02690 Return Value: 02691 02692 STATUS_SUCCESS - Timer Queue created successfully. 02693 02694 STATUS_NO_MEMORY - There was not sufficient heap to perform the requested operation. 02695 02696 --*/ 02697 { 02698 NTSTATUS Status ; 02699 ULONG Initialized ; 02700 LARGE_INTEGER TimeOut ; 02701 02702 Initialized = FALSE ; 02703 02704 // Create the first thread. This thread never dies until the process exits 02705 02706 Status = RtlCreateUserThread( 02707 NtCurrentProcess(), // process handle 02708 NULL, // security descriptor 02709 FALSE, // Create suspended? 02710 0L, // ZeroBits: default 02711 0L, // Max stack size: default 02712 0L, // Committed stack size: default 02713 Function, // Function to start in 02714 &Initialized, // Event the thread signals when the thread is ready 02715 ThreadHandle, // Thread handle 02716 NULL // Thread id 02717 ); 02718 02719 if ( Status == STATUS_SUCCESS ) { 02720 02721 // Sleep 1 ms and see if the other thread has completed initialization 02722 02723 ONE_MILLISECOND_TIMEOUT(TimeOut) ; 02724 02725 while (!(volatile ULONG) Initialized) { 02726 02727 NtDelayExecution (FALSE, &TimeOut) ; 02728 02729 } 02730 02731 } 02732 02733 02734 return Status ; 02735 } 02736 02737 NTSTATUS 02738 RtlpExitThread( 02739 NTSTATUS Status 02740 ) 02741 { 02742 return NtTerminateThread( NtCurrentThread(), Status ); 02743 } 02744 02745 02746 02747 // Wait functions 02748 02749 02750 NTSTATUS 02751 RtlpInitializeWaitThreadPool ( 02752 ) 02753 /*++ 02754 02755 Routine Description: 02756 02757 This routine initializes all aspects of the thread pool. 02758 02759 Arguments: 02760 02761 None 02762 02763 Return Value: 02764 02765 None 02766 02767 --*/ 02768 { 02769 NTSTATUS Status = STATUS_SUCCESS; 02770 LARGE_INTEGER TimeOut ; 02771 02772 // In order to avoid an explicit RtlInitialize() function to initialize the wait thread pool 02773 // we use StartedWaitInitialization and CompletedWait Initialization to provide us the 02774 // necessary synchronization to avoid multiple threads from initializing the thread pool. 02775 // This scheme does not work if RtlInitializeCriticalSection() or NtCreateEvent fails - but in this case the 02776 // caller has not choices left. 02777 02778 if (!InterlockedExchange(&StartedWaitInitialization, 1L)) { 02779 02780 if (CompletedWaitInitialization) 02781 InterlockedExchange (&CompletedWaitInitialization, 0L) ; 02782 02783 // Initialize Critical Section 02784 02785 Status = RtlInitializeCriticalSection( &WaitCriticalSection ) ; 02786 02787 if (! NT_SUCCESS( Status ) ) { 02788 02789 ASSERT ( NT_SUCCESS( Status ) ) ; 02790 02791 StartedWaitInitialization = 0 ; 02792 InterlockedExchange (&CompletedWaitInitialization, ~0) ; 02793 02794 return Status ; 02795 } 02796 02797 InitializeListHead (&WaitThreads); // Initialize global wait threads list 02798 02799 InterlockedExchange (&CompletedWaitInitialization, 1L) ; 02800 02801 } else { 02802 02803 // Sleep 1 ms and see if the other thread has completed initialization 02804 02805 ONE_MILLISECOND_TIMEOUT(TimeOut) ; 02806 02807 while (!(volatile ULONG) CompletedWaitInitialization) { 02808 02809 NtDelayExecution (FALSE, &TimeOut) ; 02810 02811 } 02812 02813 if (CompletedWaitInitialization != 1) { 02814 Status = STATUS_NO_MEMORY ; 02815 } 02816 } 02817 02818 return Status ; 02819 } 02820 02821 02822 02823 LONG 02824 RtlpWaitThread ( 02825 PVOID Initialized 02826 ) 02827 /*++ 02828 02829 Routine Description: 02830 02831 This routine is used for all waits in the wait thread pool 02832 02833 Arguments: 02834 02835 Initialized - This is set to 1 when the thread has initialized 02836 02837 Return Value: 02838 02839 Nothing. The thread never terminates. 02840 02841 --*/ 02842 { 02843 ULONG i ; // Used as an index 02844 NTSTATUS Status ; 02845 LARGE_INTEGER TimeOut; // Timeout used for waits 02846 PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB ; // Control Block allocated on the stack 02847 #define WAIT_IDLE_TIMEOUT 400000 02848 02849 02850 try { 02851 ThreadCB = (PRTLP_WAIT_THREAD_CONTROL_BLOCK) 02852 _alloca(sizeof(RTLP_WAIT_THREAD_CONTROL_BLOCK)); 02853 02854 } except (EXCEPTION_EXECUTE_HANDLER) { 02855 02856 ASSERT(FALSE); 02857 InterlockedExchange ((ULONG *)Initialized, (ULONG)~0) ; 02858 return STATUS_NO_MEMORY; 02859 } 02860 02861 02862 02863 // Initialize thread control block 02864 02865 InitializeListHead (&ThreadCB->WaitThreadsList) ; 02866 02867 Status = NtDuplicateObject( 02868 NtCurrentProcess(), 02869 NtCurrentThread(), 02870 NtCurrentProcess(), 02871 &ThreadCB->ThreadHandle, 02872 0, 02873 FALSE, 02874 DUPLICATE_SAME_ACCESS 02875 ) ; 02876 02877 if (!NT_SUCCESS(Status)) { 02878 ASSERT (FALSE) ; 02879 InterlockedExchange ((ULONG *)Initialized, (ULONG)~0) ; 02880 return STATUS_NO_MEMORY; 02881 } 02882 02883 ThreadCB->ThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ; 02884 02885 RtlZeroMemory (&ThreadCB->ActiveWaitArray[0], sizeof (HANDLE) * 64) ; 02886 02887 RtlZeroMemory (&ThreadCB->ActiveWaitPointers[0], sizeof (HANDLE) * 64) ; 02888 02889 02890 02891 // Initialize the timer related fields. 02892 02893 Status = NtCreateTimer( 02894 &ThreadCB->TimerHandle, 02895 TIMER_ALL_ACCESS, 02896 NULL, 02897 NotificationTimer 02898 ) ; 02899 02900 if (! NT_SUCCESS( Status )) { 02901 ASSERT (FALSE); 02902 NtClose(ThreadCB->ThreadHandle) ; 02903 InterlockedExchange ((ULONG *)Initialized, (ULONG)~0) ; 02904 return STATUS_NO_MEMORY; 02905 } 02906 02907 02908 ThreadCB->Firing64BitTickCount = 0 ; 02909 ThreadCB->Current64BitTickCount.QuadPart = NtGetTickCount() ; 02910 02911 // Reset the NT Timer to never fire initially 02912 02913 RtlpResetTimer (ThreadCB->TimerHandle, -1, ThreadCB) ; 02914 02915 InitializeListHead (&ThreadCB->TimerQueue.TimerList) ; 02916 InitializeListHead (&ThreadCB->TimerQueue.UncancelledTimerList) ; 02917 02918 02919 // Initialize the timer blocks 02920 02921 RtlZeroMemory (&ThreadCB->TimerBlocks[0], sizeof (RTLP_TIMER) * 63) ; 02922 02923 InitializeListHead (&ThreadCB->FreeTimerBlocks) ; 02924 02925 for (i = 0 ; i < 63 ; i++) { 02926 02927 InitializeListHead (&(&ThreadCB->TimerBlocks[i])->List) ; 02928 InsertHeadList (&ThreadCB->FreeTimerBlocks, &(&ThreadCB->TimerBlocks[i])->List) ; 02929 02930 } 02931 02932 02933 // Insert this new wait thread in the WaitThreads list. Insert at the head so that 02934 // the request that caused this thread to be created can find it right away. 02935 02936 InsertHeadList (&WaitThreads, &ThreadCB->WaitThreadsList) ; 02937 02938 02939 // The first wait element is the timer object 02940 02941 ThreadCB->ActiveWaitArray[0] = ThreadCB->TimerHandle ; 02942 02943 ThreadCB->NumActiveWaits = ThreadCB->NumWaits = 1 ; 02944 02945 02946 // till here, the function is running under the global wait lock 02947 02948 02949 02950 // We are all initialized now. Notify the starter to queue the task. 02951 02952 InterlockedExchange ((ULONG *)Initialized, 1) ; 02953 02954 02955 // Loop forever - wait threads never, never die. 02956 02957 for ( ; ; ) { 02958 02959 if (ThreadCB->NumActiveWaits == 1) 02960 TimeOut.QuadPart = Int32x32To64( WAIT_IDLE_TIMEOUT, -10000 ) ; 02961 02962 Status = NtWaitForMultipleObjects ( 02963 (CHAR) ThreadCB->NumActiveWaits, 02964 ThreadCB->ActiveWaitArray, 02965 WaitAny, 02966 TRUE, // Wait Alertably 02967 ThreadCB->NumWaits!=1 ? NULL : &TimeOut // Wait forever 02968 ) ; 02969 02970 if (Status == STATUS_ALERTED || Status == STATUS_USER_APC) { 02971 02972 continue ; 02973 02974 } else if (Status >= STATUS_WAIT_0 && Status <= STATUS_WAIT_63) { 02975 02976 if (Status == STATUS_WAIT_0) { 02977 02978 RtlpProcessTimeouts (ThreadCB) ; 02979 02980 } else { 02981 02982 // Wait completed call Callback function 02983 02984 RtlpProcessWaitCompletion ( 02985 ThreadCB->ActiveWaitPointers[Status], Status) ; 02986 02987 } 02988 02989 } else if (Status >= STATUS_ABANDONED_WAIT_0 02990 && Status <= STATUS_ABANDONED_WAIT_63) { 02991 02992 #if DBG 02993 DbgPrint ("RTL ThreadPool Wait Thread: Abandoned wait: %d\n", 02994 Status - STATUS_ABANDONED_WAIT_0 ) ; 02995 #endif 02996 02997 02998 // Abandoned wait 02999 03000 ASSERT (FALSE) ; 03001 03002 } else if (Status == STATUS_TIMEOUT) { 03003 03004 // 03005 // remove this thread from the wait list and terminate 03006 // 03007 03008 { 03009 ULONG NumWaits; 03010 03011 ACQUIRE_GLOBAL_WAIT_LOCK() ; 03012 03013 NumWaits = ThreadCB->NumWaits; 03014 03015 if (ThreadCB->NumWaits <= 1) { 03016 RemoveEntryList(&ThreadCB->WaitThreadsList) ; 03017 NtClose(ThreadCB->ThreadHandle) ; 03018 NtClose(ThreadCB->TimerHandle) ; 03019 } 03020 03021 RELEASE_GLOBAL_WAIT_LOCK() ; 03022 03023 if (NumWaits <= 1) { 03024 03025 RtlpExitThreadFunc( 0 ); 03026 } 03027 } 03028 03029 } else { 03030 03031 // Some other error: fatal condition 03032 ULONG i ; 03033 03034 // ASSERTMSG( "Press 'i', and note the dbgprint\n", FALSE ) ; 03035 03036 #if DBG 03037 DbgPrint ("RTL Thread Pool: Application closed an object handle " 03038 "that the wait thread was waiting on: Code:%x ThreadId:<%x:%x>\n", 03039 Status, HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 03040 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ; 03041 03042 TimeOut.QuadPart = 0 ; 03043 03044 for (i=0; i<ThreadCB->NumActiveWaits; i++) { 03045 03046 Status = NtWaitForMultipleObjects( 03047 (CHAR) 1, 03048 &ThreadCB->ActiveWaitArray[i], 03049 WaitAny, 03050 TRUE, // Wait Alertably 03051 &TimeOut // Dont 0 03052 ) ; 03053 03054 if (Status == STATUS_INVALID_HANDLE) { 03055 DbgPrint("Bad Handle index:%d WaitEntry Ptr:%x\n", 03056 i, ThreadCB->ActiveWaitPointers[i]) ; 03057 } 03058 } 03059 03060 #endif 03061 03062 ASSERT( FALSE ) ; 03063 03064 03065 // Set timeout for the largest timeout possible 03066 03067 TimeOut.LowPart = 0 ; 03068 TimeOut.HighPart = 0x80000000 ; 03069 03070 NtDelayExecution (TRUE, &TimeOut) ; 03071 03072 } 03073 03074 } // forever 03075 03076 return 0 ; // Keep compiler happy 03077 03078 } 03079 03080 03081 VOID 03082 RtlpAsyncCallbackCompletion( 03083 PVOID Context 03084 ) 03085 /*++ 03086 03087 Routine Description: 03088 03089 This routine is called in a (IO)worker thread and is used to decrement the 03090 RefCount at the end and call RtlpDelete(Wait/Timer) if required 03091 03092 Arguments: 03093 03094 Context - AsyncCallback: containing pointer to Wait/Timer object, 03095 03096 Return Value: 03097 03098 --*/ 03099 { 03100 PRTLP_ASYNC_CALLBACK AsyncCallback ; 03101 03102 AsyncCallback = (PRTLP_ASYNC_CALLBACK) Context ; 03103 03104 // callback queued by WaitThread (event or timer) 03105 03106 if ( AsyncCallback->WaitThreadCallback ) { 03107 03108 PRTLP_WAIT Wait = AsyncCallback->Wait ; 03109 03110 //DPRN5 03111 if (DPRN4) 03112 DbgPrint("Calling WaitOrTimer: fn:%x context:%x bool:%d Thread<%d:%d>\n", 03113 (ULONG_PTR)Wait->Function, (ULONG_PTR)Wait->Context, 03114 (ULONG_PTR)AsyncCallback->TimerCondition, 03115 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 03116 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess) 03117 ) ; 03118 03119 #if (DBG1) 03120 DBG_SET_FUNCTION( Wait->Function, Wait->Context ) ; 03121 #endif 03122 03123 ((WAITORTIMERCALLBACKFUNC) Wait->Function) 03124 ( Wait->Context, AsyncCallback->TimerCondition ) ; 03125 03126 if ( InterlockedDecrement( &Wait->RefCount ) == 0 ) { 03127 03128 RtlpDeleteWait( Wait ) ; 03129 } 03130 03131 } 03132 03133 // callback queued by TimerThread 03134 03135 else { 03136 03137 PRTLP_TIMER Timer = AsyncCallback->Timer ; 03138 03139 //DPRN5 03140 if (DPRN4) 03141 DbgPrint("Calling WaitOrTimer:Timer: fn:%x context:%x bool:%d Thread<%d:%d>\n", 03142 (ULONG_PTR)Timer->Function, (ULONG_PTR)Timer->Context, 03143 TRUE, 03144 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 03145 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess) 03146 ) ; 03147 03148 #if (DBG1) 03149 DBG_SET_FUNCTION( Timer->Function, Timer->Context ) ; 03150 #endif 03151 03152 03153 ((WAITORTIMERCALLBACKFUNC) Timer->Function) ( Timer->Context , TRUE) ; 03154 03155 03156 // decrement RefCount after function is executed so that the context is not deleted 03157 03158 if ( InterlockedDecrement( &Timer->RefCount ) == 0 ) { 03159 03160 RtlpDeleteTimer( Timer ) ; 03161 } 03162 03163 } 03164 03165 03166 RtlpFreeTPHeap( AsyncCallback ); 03167 03168 } 03169 03170 03171 VOID 03172 RtlpProcessWaitCompletion ( 03173 PRTLP_WAIT Wait, 03174 ULONG ArrayIndex 03175 ) 03176 /*++ 03177 03178 Routine Description: 03179 03180 This routine is used for processing a completed wait 03181 03182 Arguments: 03183 03184 Wait - Wait that completed 03185 03186 Return Value: 03187 03188 --*/ 03189 { 03190 ULONG TimeRemaining ; 03191 ULONG NewFiringTime ; 03192 LARGE_INTEGER DueTime ; 03193 PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB ; 03194 PRTLP_ASYNC_CALLBACK AsyncCallback ; 03195 03196 ThreadCB = Wait->ThreadCB ; 03197 03198 // deactivate wait if it is meant for single execution 03199 03200 if ( Wait->Flags & WT_EXECUTEONLYONCE ) { 03201 03202 RtlpDeactivateWait (Wait) ; 03203 } 03204 03205 else { 03206 // if wait being reactivated, then reset the timer now itself as 03207 // it can be deleted in the callback function 03208 03209 if ( Wait->Timer ) { 03210 03211 TimeRemaining = RtlpGetTimeRemaining (ThreadCB->TimerHandle) ; 03212 03213 if (RtlpReOrderDeltaList ( 03214 &ThreadCB->TimerQueue.TimerList, 03215 Wait->Timer, 03216 TimeRemaining, 03217 &NewFiringTime, 03218 Wait->Timer->Period)) { 03219 03220 // There is a new element at the head of the queue we need to reset the NT 03221 // timer to fire later 03222 03223 RtlpResetTimer (ThreadCB->TimerHandle, NewFiringTime, ThreadCB) ; 03224 } 03225 03226 } 03227 03228 // move the wait entry to the end, and shift elements to its right one pos towards left 03229 { 03230 HANDLE HandlePtr = ThreadCB->ActiveWaitArray[ArrayIndex]; 03231 PRTLP_WAIT WaitPtr = ThreadCB->ActiveWaitPointers[ArrayIndex]; 03232 03233 RtlpShiftWaitArray(ThreadCB, ArrayIndex+1, ArrayIndex, 03234 ThreadCB->NumActiveWaits -1 - ArrayIndex) 03235 ThreadCB->ActiveWaitArray[ThreadCB->NumActiveWaits-1] = HandlePtr ; 03236 ThreadCB->ActiveWaitPointers[ThreadCB->NumActiveWaits-1] = WaitPtr ; 03237 } 03238 } 03239 03240 // call callback function (FALSE since this isnt a timeout related callback) 03241 03242 if ( Wait->Flags & WT_EXECUTEINWAITTHREAD ) { 03243 03244 // executing callback after RtlpDeactivateWait allows the Callback to call 03245 // RtlDeregisterWait Wait->RefCount is not incremented so that RtlDeregisterWait 03246 // will work on this Wait. Though Wait->RefCount is not incremented, others cannot 03247 // deregister this Wait as it has to be queued as an APC. 03248 03249 if (DPRN4) 03250 DbgPrint("Calling WaitOrTimer(wait): fn:%x context:%x bool:%d Thread<%d:%d>\n", 03251 (ULONG_PTR)Wait->Function, (ULONG_PTR)Wait->Context, 03252 FALSE, 03253 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 03254 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess) 03255 ) ; 03256 03257 #if (DBG1) 03258 DBG_SET_FUNCTION( Wait->Function, Wait->Context ) ; 03259 #endif 03260 03261 03262 ((WAITORTIMERCALLBACKFUNC)(Wait->Function))(Wait->Context, FALSE) ; 03263 03264 03265 // Wait object could have been deleted in the above callback 03266 03267 return ; 03268 03269 03270 } else { 03271 03272 AsyncCallback = RtlpForceAllocateTPHeap( sizeof( RTLP_ASYNC_CALLBACK ), 0 ); 03273 03274 if ( AsyncCallback ) { 03275 03276 NTSTATUS Status; 03277 03278 AsyncCallback->Wait = Wait ; 03279 AsyncCallback->WaitThreadCallback = TRUE ; 03280 AsyncCallback->TimerCondition = FALSE ; 03281 03282 InterlockedIncrement( &Wait->RefCount ) ; 03283 03284 Status = RtlQueueWorkItem( RtlpAsyncCallbackCompletion, AsyncCallback, 03285 Wait->Flags ); 03286 03287 03288 if (!NT_SUCCESS(Status)) { 03289 03290 RtlpFreeTPHeap( AsyncCallback ); 03291 03292 if ( InterlockedDecrement( &Wait->RefCount ) == 0 ) { 03293 RtlpDeleteWait( Wait ) ; 03294 } 03295 } 03296 } 03297 } 03298 } 03299 03300 03301 VOID 03302 RtlpAddWait ( 03303 PRTLP_WAIT Wait 03304 ) 03305 /*++ 03306 03307 Routine Description: 03308 03309 This routine is used for adding waits to the wait thread. It is executed in 03310 an APC. 03311 03312 Arguments: 03313 03314 Wait - The wait to add 03315 03316 Return Value: 03317 03318 --*/ 03319 { 03320 PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB = Wait->ThreadCB; 03321 03322 03323 // if the state is deleted, it implies that RtlDeregister was called in a 03324 // WaitThreadCallback for a Wait other than that which was fired. This is 03325 // an application bug. I Assert, but also handle it. 03326 03327 if ( Wait->State & STATE_DELETE ) { 03328 03329 ASSERT(FALSE) ; 03330 03331 InterlockedDecrement( &ThreadCB->NumWaits ) ; 03332 03333 RtlpDeleteWait (Wait) ; 03334 03335 return ; 03336 } 03337 03338 03339 // activate Wait 03340 03341 ThreadCB->ActiveWaitArray [ThreadCB->NumActiveWaits] = Wait->WaitHandle ; 03342 ThreadCB->ActiveWaitPointers[ThreadCB->NumActiveWaits] = Wait ; 03343 ThreadCB->NumActiveWaits ++ ; 03344 Wait->State |= (STATE_REGISTERED | STATE_ACTIVE) ; 03345 Wait->RefCount = 1 ; 03346 03347 03348 // Fill in the wait timer 03349 03350 if (Wait->Timeout != INFINITE_TIME) { 03351 03352 ULONG TimeRemaining ; 03353 ULONG NewFiringTime ; 03354 03355 // Initialize timer related fields and insert the timer in the timer queue for 03356 // this wait thread 03357 03358 Wait->Timer = (PRTLP_TIMER) RemoveHeadList(&ThreadCB->FreeTimerBlocks); 03359 Wait->Timer->Function = Wait->Function ; 03360 Wait->Timer->Context = Wait->Context ; 03361 Wait->Timer->Flags = Wait->Flags ; 03362 Wait->Timer->DeltaFiringTime = Wait->Timeout ; 03363 Wait->Timer->Period = ( Wait->Flags & WT_EXECUTEONLYONCE ) 03364 ? 0 03365 : Wait->Timeout == INFINITE_TIME 03366 ? 0 : Wait->Timeout ; 03367 03368 Wait->Timer->State = ( STATE_REGISTERED | STATE_ACTIVE ) ; ; 03369 Wait->Timer->Wait = Wait ; 03370 Wait->Timer->RefCountPtr = &Wait->RefCount ; 03371 Wait->Timer->Queue = &ThreadCB->TimerQueue ; 03372 03373 03374 TimeRemaining = RtlpGetTimeRemaining (ThreadCB->TimerHandle) ; 03375 03376 if (RtlpInsertInDeltaList (&ThreadCB->TimerQueue.TimerList, Wait->Timer, 03377 TimeRemaining, &NewFiringTime)) 03378 { 03379 // If the element was inserted at head of list then reset the timers 03380 03381 RtlpResetTimer (ThreadCB->TimerHandle, NewFiringTime, ThreadCB) ; 03382 } 03383 03384 } else { 03385 03386 // No timer with this wait 03387 03388 Wait->Timer = NULL ; 03389 03390 } 03391 03392 return ; 03393 } 03394 03395 03396 NTSTATUS 03397 RtlpDeregisterWait ( 03398 PRTLP_WAIT Wait, 03399 HANDLE PartialCompletionEvent, 03400 PULONG RetStatusPtr 03401 ) 03402 /*++ 03403 03404 Routine Description: 03405 03406 This routine is used for deregistering the specified wait. 03407 03408 Arguments: 03409 03410 Wait - The wait to deregister 03411 03412 Return Value: 03413 03414 --*/ 03415 { 03416 ULONG Status = STATUS_SUCCESS ; 03417 ULONG DontUse ; 03418 PULONG RetStatus = RetStatusPtr ? RetStatusPtr : &DontUse; 03419 03420 CHECK_SIGNATURE(Wait) ; 03421 03422 03423 // RtlpDeregisterWait can be called on a wait that has not yet been 03424 // registered. This indicates that someone calls a RtlDeregisterWait 03425 // inside a WaitThreadCallback for a Wait other than that was fired. 03426 // Application bug!! I assert but handle it 03427 03428 if ( ! (Wait->State & STATE_REGISTERED) ) { 03429 03430 // set state to deleted, so that it does not get registered 03431 03432 Wait->State |= STATE_DELETE ; 03433 03434 InterlockedDecrement( &Wait->RefCount ) ; 03435 03436 if ( PartialCompletionEvent ) { 03437 03438 NtSetEvent( PartialCompletionEvent, NULL ) ; 03439 } 03440 03441 *RetStatus = STATUS_SUCCESS ; 03442 return STATUS_SUCCESS ; 03443 } 03444 03445 03446 // deactivate wait. 03447 03448 if ( Wait->State & STATE_ACTIVE ) { 03449 03450 if ( ! NT_SUCCESS( RtlpDeactivateWait ( Wait ) ) ) { 03451 03452 *RetStatus = STATUS_NOT_FOUND ; 03453 return STATUS_NOT_FOUND ; 03454 } 03455 } 03456 03457 // delete wait if RefCount == 0 03458 03459 Wait->State |= STATE_DELETE ; 03460 03461 if ( InterlockedDecrement (&Wait->RefCount) == 0 ) { 03462 03463 RtlpDeleteWait ( Wait ) ; 03464 03465 Status = *RetStatus = STATUS_SUCCESS ; 03466 03467 } else { 03468 03469 Status = *RetStatus = STATUS_PENDING ; 03470 } 03471 03472 if ( PartialCompletionEvent ) { 03473 03474 NtSetEvent( PartialCompletionEvent, NULL ) ; 03475 } 03476 03477 03478 return Status ; 03479 } 03480 03481 03482 NTSTATUS 03483 RtlpDeactivateWait ( 03484 PRTLP_WAIT Wait 03485 ) 03486 /*++ 03487 03488 Routine Description: 03489 03490 This routine is used for deactivating the specified wait. It is executed in a APC. 03491 03492 Arguments: 03493 03494 Wait - The wait to deactivate 03495 03496 Return Value: 03497 03498 --*/ 03499 { 03500 PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB = Wait->ThreadCB ; 03501 ULONG ArrayIndex ; //Index in ActiveWaitArray where the Wait object is placed 03502 ULONG EndIndex = ThreadCB->NumActiveWaits -1; 03503 03504 // get the index in ActiveWaitArray 03505 03506 for (ArrayIndex = 0; ArrayIndex <= EndIndex; ArrayIndex++) { 03507 03508 if (ThreadCB->ActiveWaitPointers[ArrayIndex] == Wait) 03509 break ; 03510 } 03511 03512 if ( ArrayIndex > EndIndex ) { 03513 03514 ASSERT (FALSE) ; 03515 return STATUS_NOT_FOUND; 03516 } 03517 03518 03519 // Move the remaining ActiveWaitArray left. 03520 03521 RtlpShiftWaitArray( ThreadCB, ArrayIndex+1, ArrayIndex, 03522 EndIndex - ArrayIndex ) ; 03523 03524 03525 // 03526 // delete timer if associated with this wait 03527 // 03528 // Though timer is being "freed" here, if it is in the timersToBeFired 03529 // list, some of its fields will be used later 03530 // 03531 03532 if ( Wait->Timer ) { 03533 03534 ULONG TimeRemaining ; 03535 ULONG NewFiringTime ; 03536 03537 if (! (Wait->Timer->State & STATE_ACTIVE) ) { 03538 03539 RemoveEntryList( &Wait->Timer->List ) ; 03540 03541 } else { 03542 03543 TimeRemaining = RtlpGetTimeRemaining (ThreadCB->TimerHandle) ; 03544 03545 03546 if (RtlpRemoveFromDeltaList (&ThreadCB->TimerQueue.TimerList, Wait->Timer, 03547 TimeRemaining, &NewFiringTime)) 03548 { 03549 03550 RtlpResetTimer (ThreadCB->TimerHandle, NewFiringTime, ThreadCB) ; 03551 } 03552 } 03553 03554 03555 InsertTailList (&ThreadCB->FreeTimerBlocks, &Wait->Timer->List) ; 03556 03557 Wait->Timer = NULL ; 03558 } 03559 03560 // Decrement the (active) wait count 03561 03562 ThreadCB->NumActiveWaits-- ; 03563 InterlockedDecrement( &ThreadCB->NumWaits ) ; 03564 03565 Wait->State &= ~STATE_ACTIVE ; 03566 03567 return STATUS_SUCCESS; 03568 03569 } 03570 03571 03572 VOID 03573 RtlpDeleteWait ( 03574 PRTLP_WAIT Wait 03575 ) 03576 /*++ 03577 03578 Routine Description: 03579 03580 This routine is used for deleting the specified wait. It can be executed 03581 outside the context of the wait thread. So structure except the WaitEntry 03582 can be changed. It also sets the event. 03583 03584 Arguments: 03585 03586 Wait - The wait to delete 03587 03588 Return Value: 03589 03590 --*/ 03591 { 03592 CHECK_SIGNATURE( Wait ) ; 03593 CLEAR_SIGNATURE( Wait ) ; 03594 03595 #if DBG1 03596 if (DPRN1) 03597 DbgPrint("<%d> Wait %x deleted in thread:%d\n\n", Wait->DbgId, 03598 (ULONG_PTR)Wait, 03599 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread)) ; 03600 #endif 03601 03602 03603 if ( Wait->CompletionEvent ) { 03604 03605 NtSetEvent( Wait->CompletionEvent, NULL ) ; 03606 } 03607 03608 RtlpFreeTPHeap( Wait) ; 03609 03610 return ; 03611 } 03612 03613 03614 03615 03616 VOID 03617 RtlpDoNothing ( 03618 PVOID NotUsed1, 03619 PVOID NotUsed2, 03620 PVOID NotUsed3 03621 ) 03622 /*++ 03623 03624 Routine Description: 03625 03626 This routine is used to see if the thread is alive 03627 03628 Arguments: 03629 03630 NotUsed1, NotUsed2 and NotUsed 3 - not used 03631 03632 Return Value: 03633 03634 None 03635 03636 --*/ 03637 { 03638 03639 } 03640 03641 03642 __inline 03643 LONGLONG 03644 RtlpGet64BitTickCount( 03645 LARGE_INTEGER *Last64BitTickCount 03646 ) 03647 /*++ 03648 03649 Routine Description: 03650 03651 This routine is used for getting the latest 64bit tick count. 03652 03653 Arguments: 03654 03655 Return Value: 64bit tick count 03656 03657 --*/ 03658 { 03659 LARGE_INTEGER liCurTime ; 03660 03661 liCurTime.QuadPart = NtGetTickCount() + Last64BitTickCount->HighPart ; 03662 03663 // see if timer has wrapped. 03664 03665 if (liCurTime.LowPart < Last64BitTickCount->LowPart) { 03666 liCurTime.HighPart++ ; 03667 } 03668 03669 return (Last64BitTickCount->QuadPart = liCurTime.QuadPart) ; 03670 } 03671 03672 __inline 03673 LONGLONG 03674 RtlpResync64BitTickCount( 03675 ) 03676 /*++ 03677 03678 Routine Description: 03679 03680 This routine is used for getting the latest 64bit tick count. 03681 03682 Arguments: 03683 03684 Return Value: 64bit tick count 03685 03686 Remarks: This call should be made in the first line of any APC queued 03687 to the timer thread and nowhere else. It is used to reduce the drift 03688 03689 --*/ 03690 { 03691 return Resync64BitTickCount.QuadPart = 03692 RtlpGet64BitTickCount(&Last64BitTickCount); 03693 } 03694 03695 03696 VOID 03697 RtlpProcessTimeouts ( 03698 PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB 03699 ) 03700 /*++ 03701 03702 Routine Description: 03703 03704 This routine processes timeouts for the wait thread 03705 03706 Arguments: 03707 03708 ThreadCB - The wait thread to add the wait to 03709 03710 Return Value: 03711 03712 --*/ 03713 { 03714 ULONG NewFiringTime, TimeRemaining ; 03715 LIST_ENTRY TimersToFireList ; 03716 03717 // 03718 // check if incorrect timer fired 03719 // 03720 if (ThreadCB->Firing64BitTickCount > 03721 RtlpGet64BitTickCount(&ThreadCB->Current64BitTickCount) + 200 ) 03722 { 03723 RtlpResetTimer (ThreadCB->TimerHandle, 03724 RtlpGetTimeRemaining (ThreadCB->TimerHandle), 03725 ThreadCB) ; 03726 03727 return ; 03728 } 03729 03730 InitializeListHead( &TimersToFireList ) ; 03731 03732 03733 // Walk thru the timer list and fire all waits with DeltaFiringTime == 0 03734 03735 RtlpFireTimersAndReorder (&ThreadCB->TimerQueue, &NewFiringTime, &TimersToFireList) ; 03736 03737 // Reset the NT timer 03738 03739 RtlpResetTimer (ThreadCB->TimerHandle, NewFiringTime, ThreadCB) ; 03740 03741 03742 RtlpFireTimers( &TimersToFireList ) ; 03743 } 03744 03745 03746 VOID 03747 RtlpFireTimers ( 03748 PLIST_ENTRY TimersToFireList 03749 ) 03750 /*++ 03751 03752 Routine Description: 03753 03754 Finally all the timers are fired here. 03755 03756 Arguments: 03757 03758 TimersToFireList: List of timers to fire 03759 03760 --*/ 03761 03762 { 03763 PLIST_ENTRY Node ; 03764 PRTLP_TIMER Timer ; 03765 NTSTATUS Status; 03766 03767 03768 for (Node = TimersToFireList->Flink; Node != TimersToFireList; Node = TimersToFireList->Flink) 03769 { 03770 Timer = CONTAINING_RECORD (Node, RTLP_TIMER, TimersToFireList) ; 03771 03772 RemoveEntryList( Node ) ; 03773 InitializeListHead( Node ) ; 03774 03775 03776 if ( (Timer->State & STATE_DONTFIRE) 03777 || (Timer->Queue->State & STATE_DONTFIRE) ) 03778 { 03779 ; 03780 03781 } else if ( Timer->Flags & (WT_EXECUTEINTIMERTHREAD | WT_EXECUTEINWAITTHREAD ) ) { 03782 03783 //DPRN5 03784 if (DPRN4) 03785 DbgPrint("Calling WaitOrTimer(Timer): fn:%x context:%x bool:%d Thread<%d:%d>\n", 03786 (ULONG_PTR)Timer->Function, (ULONG_PTR)Timer->Context, 03787 TRUE, 03788 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 03789 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess) 03790 ) ; 03791 03792 #if (DBG1) 03793 DBG_SET_FUNCTION( Timer->Function, Timer->Context ) ; 03794 #endif 03795 03796 03797 ((WAITORTIMERCALLBACKFUNC) Timer->Function) (Timer->Context, TRUE) ; 03798 03799 } else { 03800 03801 // create context for Callback and queue it appropriately 03802 03803 PRTLP_ASYNC_CALLBACK AsyncCallback ; 03804 03805 AsyncCallback = RtlpForceAllocateTPHeap( 03806 sizeof( RTLP_ASYNC_CALLBACK ), 03807 0 ); 03808 AsyncCallback->TimerCondition = TRUE ; 03809 03810 // timer associated with WaitEvents should be treated differently 03811 03812 if ( Timer->Wait != NULL ) { 03813 03814 AsyncCallback->Wait = Timer->Wait ; 03815 AsyncCallback->WaitThreadCallback = TRUE ; 03816 03817 InterlockedIncrement( Timer->RefCountPtr ) ; 03818 03819 } else { 03820 03821 AsyncCallback->Timer = Timer ; 03822 AsyncCallback->WaitThreadCallback = FALSE ; 03823 03824 InterlockedIncrement( &Timer->RefCount ) ; 03825 } 03826 03827 03828 // queue work item 03829 03830 Status = RtlQueueWorkItem( RtlpAsyncCallbackCompletion, AsyncCallback, 03831 Timer->Flags ); 03832 03833 if (!NT_SUCCESS(Status)) { 03834 03835 RtlpFreeTPHeap( AsyncCallback ); 03836 if ( Timer->Wait != NULL ) { 03837 InterlockedDecrement( Timer->RefCountPtr ) ; 03838 } else { 03839 InterlockedDecrement( &Timer->RefCount ) ; 03840 } 03841 } 03842 03843 } 03844 03845 03846 } 03847 } 03848 03849 03850 NTSTATUS 03851 RtlpFindWaitThread ( 03852 PRTLP_WAIT_THREAD_CONTROL_BLOCK *ThreadCB 03853 ) 03854 /*++ 03855 03856 Routine Description: 03857 03858 Walks thru the list of wait threads and finds one which can accomodate another wait. 03859 If one is not found then a new thread is created. 03860 03861 This routine assumes that the caller has the GlobalWaitLock. 03862 03863 Arguments: 03864 03865 ThreadCB: returns the ThreadCB of the wait thread that will service the wait request. 03866 03867 Return Value: 03868 03869 STATUS_SUCCESS if a wait thread was allocated, 03870 03871 --*/ 03872 { 03873 NTSTATUS Status = STATUS_SUCCESS; 03874 PLIST_ENTRY Node ; 03875 HANDLE ThreadHandle ; 03876 PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCBTmp; 03877 03878 03879 ACQUIRE_GLOBAL_WAIT_LOCK() ; 03880 03881 do { 03882 03883 // Walk thru the list of Wait Threads and find a Wait thread that can accomodate a 03884 // new wait request. 03885 03886 // *Consider* finding a wait thread with least # of waits to facilitate better 03887 // load balancing of waits. 03888 03889 03890 for (Node = WaitThreads.Flink ; Node != &WaitThreads ; Node = Node->Flink) { 03891 03892 ThreadCBTmp = CONTAINING_RECORD (Node, RTLP_WAIT_THREAD_CONTROL_BLOCK, 03893 WaitThreadsList) ; 03894 03895 03896 // Wait Threads can accomodate upto 64 waits (NtWaitForMultipleObject limit) 03897 03898 if ((ThreadCBTmp)->NumWaits < 64) { 03899 03900 // Found a thread with some wait slots available. 03901 03902 InterlockedIncrement ( &(ThreadCBTmp)->NumWaits) ; 03903 03904 *ThreadCB = ThreadCBTmp; 03905 03906 RELEASE_GLOBAL_WAIT_LOCK() ; 03907 03908 return STATUS_SUCCESS ; 03909 } 03910 03911 } 03912 03913 03914 // If we reach here, we dont have any more wait threads. so create a new wait thread. 03915 03916 Status = RtlpStartThreadFunc (RtlpWaitThread, &ThreadHandle) ; 03917 03918 03919 // If thread creation fails then return the failure to caller 03920 03921 if (Status != STATUS_SUCCESS ) { 03922 03923 #if DBG 03924 DbgPrint("ERROR!! ThreadPool: could not create wait thread\n"); 03925 #endif 03926 03927 RELEASE_GLOBAL_WAIT_LOCK() ; 03928 03929 return Status ; 03930 03931 } else { 03932 03933 // Close Thread handle, we dont need it. 03934 03935 NtClose (ThreadHandle) ; 03936 } 03937 03938 // Loop back now that we have created another thread 03939 03940 } while (TRUE) ; // Loop back to top and put new wait request in the newly created thread 03941 03942 RELEASE_GLOBAL_WAIT_LOCK() ; 03943 03944 return Status ; 03945 } 03946 03947 03948 03949 // Timer Functions 03950 03951 03952 03953 VOID 03954 RtlpAddTimer ( 03955 PRTLP_TIMER Timer 03956 ) 03957 /*++ 03958 03959 Routine Description: 03960 03961 This routine runs as an APC into the Timer thread. It adds a new timer to the 03962 specified queue. 03963 03964 Arguments: 03965 03966 Timer - Pointer to the timer to add 03967 03968 Return Value: 03969 03970 03971 --*/ 03972 { 03973 PRTLP_TIMER_QUEUE Queue ; 03974 ULONG TimeRemaining, QueueRelTimeRemaining ; 03975 ULONG NewFiringTime ; 03976 03977 RtlpResync64BitTickCount() ; 03978 03979 03980 // the timer was set to be deleted in a callback function. 03981 03982 if (Timer->State & STATE_DELETE ) { 03983 03984 RtlpDeleteTimer( Timer ) ; 03985 return ; 03986 } 03987 03988 03989 Queue = Timer->Queue ; 03990 03991 03992 // TimeRemaining is the time left in the current timer + the relative time of 03993 // the queue it is being inserted into 03994 03995 TimeRemaining = RtlpGetTimeRemaining (TimerHandle) ; 03996 QueueRelTimeRemaining = TimeRemaining + RtlpGetQueueRelativeTime (Queue) ; 03997 03998 03999 if (RtlpInsertInDeltaList (&Queue->TimerList, Timer, QueueRelTimeRemaining, 04000 &NewFiringTime)) 04001 { 04002 04003 // If the Queue is not attached to TimerQueues since it had no timers 04004 // previously then insert the queue into the TimerQueues list, else just 04005 // reorder its existing position. 04006 04007 if (IsListEmpty (&Queue->List)) { 04008 04009 Queue->DeltaFiringTime = NewFiringTime ; 04010 04011 if (RtlpInsertInDeltaList (&TimerQueues, Queue, TimeRemaining, 04012 &NewFiringTime)) 04013 { 04014 04015 // There is a new element at the head of the queue we need to reset the NT 04016 // timer to fire sooner. 04017 04018 RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ; 04019 } 04020 04021 } else { 04022 04023 // If we insert at the head of the timer delta list we will need to 04024 // make sure the queue delta list is readjusted 04025 04026 if (RtlpReOrderDeltaList(&TimerQueues, Queue, TimeRemaining, &NewFiringTime, NewFiringTime)){ 04027 04028 // There is a new element at the head of the queue we need to reset the NT 04029 // timer to fire sooner. 04030 04031 RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ; 04032 04033 } 04034 } 04035 04036 } 04037 04038 Timer->State |= ( STATE_REGISTERED | STATE_ACTIVE ) ; 04039 } 04040 04041 04042 04043 VOID 04044 RtlpUpdateTimer ( 04045 PRTLP_TIMER Timer, 04046 PRTLP_TIMER UpdatedTimer 04047 ) 04048 /*++ 04049 04050 Routine Description: 04051 04052 This routine executes in an APC and updates the specified timer if it exists 04053 04054 Arguments: 04055 04056 Timer - Timer that is actually updated 04057 UpdatedTimer - Specifies pointer to a timer structure that contains Queue and 04058 Timer information 04059 04060 Return Value: 04061 04062 04063 --*/ 04064 { 04065 PRTLP_TIMER_QUEUE Queue ; 04066 ULONG TimeRemaining, QueueRelTimeRemaining ; 04067 ULONG NewFiringTime ; 04068 04069 04070 RtlpResync64BitTickCount( ) ; 04071 04072 CHECK_SIGNATURE(Timer) ; 04073 04074 Queue = Timer->Queue ; 04075 04076 // Update the periodic time on the timer 04077 04078 Timer->Period = UpdatedTimer->Period ; 04079 04080 04081 // if timer is not in active state, then dont update it 04082 04083 if ( ! ( Timer->State & STATE_ACTIVE ) ) { 04084 04085 return ; 04086 } 04087 04088 // Get the time remaining on the NT timer 04089 04090 TimeRemaining = RtlpGetTimeRemaining (TimerHandle) ; 04091 QueueRelTimeRemaining = TimeRemaining + RtlpGetQueueRelativeTime (Queue) ; 04092 04093 04094 // Update the timer based on the due time 04095 04096 if (RtlpReOrderDeltaList (&Queue->TimerList, Timer, QueueRelTimeRemaining, 04097 &NewFiringTime, 04098 UpdatedTimer->DeltaFiringTime)) 04099 { 04100 04101 // If this update caused the timer at the head of the queue to change, then reinsert 04102 // this queue in the list of queues. 04103 04104 if (RtlpReOrderDeltaList (&TimerQueues, Queue, TimeRemaining, &NewFiringTime, NewFiringTime)) { 04105 04106 // NT timer needs to be updated since the change caused the queue at the head of 04107 // the TimerQueues to change. 04108 04109 RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ; 04110 04111 } 04112 04113 } 04114 04115 RtlpFreeTPHeap( UpdatedTimer ) ; 04116 } 04117 04118 04119 VOID 04120 RtlpCancelTimer ( 04121 PRTLP_TIMER Timer 04122 ) 04123 /*++ 04124 04125 Routine Description: 04126 04127 This routine executes in an APC and cancels the specified timer if it exists 04128 04129 Arguments: 04130 04131 Timer - Specifies pointer to a timer structure that contains Queue and Timer information 04132 04133 Return Value: 04134 04135 04136 --*/ 04137 { 04138 RtlpCancelTimerEx( Timer, FALSE ) ; // queue not being deleted 04139 } 04140 04141 VOID 04142 RtlpCancelTimerEx ( 04143 PRTLP_TIMER Timer, 04144 BOOLEAN DeletingQueue 04145 ) 04146 /*++ 04147 04148 Routine Description: 04149 04150 This routine cancels the specified timer. 04151 04152 Arguments: 04153 04154 Timer - Specifies pointer to a timer structure that contains Queue and Timer information 04155 DeletingQueue - FALSE: routine executing in an APC. Delete timer only. 04156 TRUE : routine called by timer queue which is being deleted. So dont 04157 reset the queue's position 04158 Return Value: 04159 04160 04161 --*/ 04162 { 04163 PRTLP_TIMER_QUEUE Queue ; 04164 04165 RtlpResync64BitTickCount() ; 04166 CHECK_SIGNATURE( Timer ) ; 04167 04168 Queue = Timer->Queue ; 04169 04170 04171 if ( Timer->State & STATE_ACTIVE ) { 04172 04173 // if queue is being deleted, then the timer should not be reset 04174 04175 if ( ! DeletingQueue ) 04176 RtlpDeactivateTimer( Queue, Timer ) ; 04177 04178 } else { 04179 04180 // remove one shot Inactive timer from Queue->UncancelledTimerList 04181 // called only when the time queue is being deleted 04182 04183 RemoveEntryList( &Timer->List ) ; 04184 04185 } 04186 04187 04188 // Set the State to deleted 04189 04190 Timer->State |= STATE_DELETE ; 04191 04192 04193 // delete timer if refcount == 0 04194 04195 if ( InterlockedDecrement( &Timer->RefCount ) == 0 ) { 04196 04197 RtlpDeleteTimer( Timer ) ; 04198 } 04199 } 04200 04201 VOID 04202 RtlpDeactivateTimer ( 04203 PRTLP_TIMER_QUEUE Queue, 04204 PRTLP_TIMER Timer 04205 ) 04206 /*++ 04207 04208 Routine Description: 04209 04210 This routine executes in an APC and cancels the specified timer if it exists 04211 04212 Arguments: 04213 04214 Timer - Specifies pointer to a timer structure that contains Queue and Timer information 04215 04216 Return Value: 04217 04218 04219 --*/ 04220 { 04221 ULONG TimeRemaining, QueueRelTimeRemaining ; 04222 ULONG NewFiringTime ; 04223 04224 04225 // Remove the timer from the appropriate queue 04226 04227 TimeRemaining = RtlpGetTimeRemaining (TimerHandle) ; 04228 QueueRelTimeRemaining = TimeRemaining + RtlpGetQueueRelativeTime (Queue) ; 04229 04230 if (RtlpRemoveFromDeltaList (&Queue->TimerList, Timer, QueueRelTimeRemaining, &NewFiringTime)) { 04231 04232 // If we removed the last timer from the queue then we should remove the queue 04233 // from TimerQueues, else we should readjust its position based on the delta time change 04234 04235 if (IsListEmpty (&Queue->TimerList)) { 04236 04237 // Remove the queue from TimerQueues 04238 04239 if (RtlpRemoveFromDeltaList (&TimerQueues, Queue, TimeRemaining, &NewFiringTime)) { 04240 04241 // There is a new element at the head of the queue we need to reset the NT 04242 // timer to fire later 04243 04244 RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ; 04245 04246 } 04247 04248 InitializeListHead (&Queue->List) ; 04249 04250 } else { 04251 04252 // If we remove from the head of the timer delta list we will need to 04253 // make sure the queue delta list is readjusted 04254 04255 if (RtlpReOrderDeltaList (&TimerQueues, Queue, TimeRemaining, &NewFiringTime, NewFiringTime)) { 04256 04257 // There is a new element at the head of the queue we need to reset the NT 04258 // timer to fire later 04259 04260 RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ; 04261 04262 } 04263 04264 } 04265 04266 } 04267 } 04268 04269 04270 VOID 04271 RtlpDeleteTimer ( 04272 PRTLP_TIMER Timer 04273 ) 04274 /*++ 04275 04276 Routine Description: 04277 04278 This routine executes in worker or timer thread and deletes the timer 04279 whose RefCount == 0. The function can be called outside timer thread, 04280 so no structure outside Timer can be touched (no list etc). 04281 04282 Arguments: 04283 04284 Timer - Specifies pointer to a timer structure that contains Queue and Timer information 04285 04286 Return Value: 04287 04288 04289 --*/ 04290 { 04291 PRTLP_TIMER_QUEUE Queue = Timer->Queue ; 04292 04293 CHECK_SIGNATURE( Timer ) ; 04294 CLEAR_SIGNATURE( Timer ) ; 04295 04296 #if DBG1 04297 if (DPRN1) 04298 DbgPrint("<%d> Timer: %x: deleted\n\n", Timer->Queue->DbgId, 04299 (ULONG_PTR)Timer) ; 04300 #endif 04301 04302 // safe to call this. Either the timer is in the TimersToFireList and 04303 // the function is being called in time context or else it is not in the 04304 // list 04305 04306 RemoveEntryList( &Timer->TimersToFireList ) ; 04307 04308 if ( Timer->CompletionEvent ) 04309 NtSetEvent( Timer->CompletionEvent, NULL ) ; 04310 04311 04312 // decrement the total number of timers in the queue 04313 04314 if ( InterlockedDecrement( &Queue->RefCount ) == 0 ) 04315 04316 RtlpDeleteTimerQueueComplete( Queue ) ; 04317 04318 04319 RtlpFreeTPHeap( Timer ) ; 04320 04321 } 04322 04323 04324 04325 ULONG 04326 RtlpGetQueueRelativeTime ( 04327 PRTLP_TIMER_QUEUE Queue 04328 ) 04329 /*++ 04330 04331 Routine Description: 04332 04333 Walks the list of queues and returns the relative firing time by adding all the 04334 DeltaFiringTimes for all queues before it. 04335 04336 Arguments: 04337 04338 Queue - Queue for which to find the relative firing time 04339 04340 Return Value: 04341 04342 Time in milliseconds 04343 04344 --*/ 04345 { 04346 PLIST_ENTRY Node ; 04347 ULONG RelativeTime ; 04348 PRTLP_TIMER_QUEUE CurrentQueue ; 04349 04350 RelativeTime = 0 ; 04351 04352 // It the Queue is not attached to TimerQueues List because it has no timer 04353 // associated with it simply returns 0 as the relative time. Else run thru 04354 // all queues before it in the list and compute the relative firing time 04355 04356 if (!IsListEmpty (&Queue->List)) { 04357 04358 for (Node = TimerQueues.Flink; Node != &Queue->List; Node=Node->Flink) { 04359 04360 CurrentQueue = CONTAINING_RECORD (Node, RTLP_TIMER_QUEUE, List) ; 04361 04362 RelativeTime += CurrentQueue->DeltaFiringTime ; 04363 04364 } 04365 04366 // Add the queue's delta firing time as well 04367 04368 RelativeTime += Queue->DeltaFiringTime ; 04369 04370 } 04371 04372 return RelativeTime ; 04373 04374 } 04375 04376 04377 ULONG 04378 RtlpGetTimeRemaining ( 04379 HANDLE TimerHandle 04380 ) 04381 /*++ 04382 04383 Routine Description: 04384 04385 Gets the time remaining on the specified NT timer 04386 04387 Arguments: 04388 04389 TimerHandle - Handle to the NT timer 04390 04391 Return Value: 04392 04393 Time remaining on the timer 04394 04395 --*/ 04396 { 04397 ULONG InfoLen ; 04398 TIMER_BASIC_INFORMATION Info ; 04399 04400 NTSTATUS Status ; 04401 04402 Status = NtQueryTimer (TimerHandle, TimerBasicInformation, &Info, sizeof(Info), &InfoLen) ; 04403 04404 ASSERT (Status == STATUS_SUCCESS) ; 04405 04406 // Return 0 if 04407 // 04408 // - the timer has already fired then return 04409 // OR 04410 // - the timer is has more than 0x7f0000000 in its high part 04411 // (which indicates that the timer was (probably) programmed for -1) 04412 04413 04414 if (Info.TimerState || ((ULONG)Info.RemainingTime.HighPart > 0x7f000000) ) { 04415 04416 return 0 ; 04417 04418 } else { 04419 04420 return (ULONG) (Info.RemainingTime.QuadPart / 10000) ; 04421 04422 } 04423 04424 } 04425 04426 04427 04428 VOID 04429 RtlpResetTimer ( 04430 HANDLE TimerHandle, 04431 ULONG DueTime, 04432 PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB 04433 ) 04434 /*++ 04435 04436 Routine Description: 04437 04438 This routine resets the timer object with the new due time. 04439 04440 Arguments: 04441 04442 TimerHandle - Handle to the timer object 04443 04444 DueTime - Relative timer due time in Milliseconds 04445 04446 Return Value: 04447 04448 --*/ 04449 { 04450 LARGE_INTEGER LongDueTime ; 04451 04452 NtCancelTimer (TimerHandle, NULL) ; 04453 04454 // If the DueTime is INFINITE_TIME then set the timer to the largest integer possible 04455 04456 if (DueTime == INFINITE_TIME) { 04457 04458 LongDueTime.LowPart = 0x0 ; 04459 04460 LongDueTime.HighPart = 0x80000000 ; 04461 04462 } else { 04463 04464 // 04465 // set the absolute time when timer is to be fired 04466 // 04467 04468 if (ThreadCB) { 04469 04470 ThreadCB->Firing64BitTickCount = DueTime 04471 + RtlpGet64BitTickCount(&ThreadCB->Current64BitTickCount) ; 04472 04473 } else { 04474 // 04475 // adjust for drift only if it is a global timer 04476 // 04477 04478 ULONG Drift ; 04479 LONGLONG llCurrentTick ; 04480 04481 llCurrentTick = RtlpGet64BitTickCount(&Last64BitTickCount) ; 04482 04483 Drift = (ULONG) (llCurrentTick - RtlpGetResync64BitTickCount()) ; 04484 DueTime = (DueTime > Drift) ? DueTime-Drift : 1 ; 04485 RtlpSetFiring64BitTickCount(llCurrentTick + DueTime) ; 04486 } 04487 04488 04489 LongDueTime.QuadPart = Int32x32To64( DueTime, -10000 ); 04490 04491 } 04492 04493 04494 NtSetTimer ( 04495 TimerHandle, 04496 &LongDueTime, 04497 ThreadCB ? NULL : RtlpServiceTimer, 04498 NULL, 04499 FALSE, 04500 0, 04501 NULL 04502 ) ; 04503 } 04504 04505 04506 BOOLEAN 04507 RtlpInsertInDeltaList ( 04508 PLIST_ENTRY DeltaList, 04509 PRTLP_GENERIC_TIMER NewTimer, 04510 ULONG TimeRemaining, 04511 ULONG *NewFiringTime 04512 ) 04513 /*++ 04514 04515 Routine Description: 04516 04517 Inserts the timer element in the appropriate place in the delta list. 04518 04519 Arguments: 04520 04521 DeltaList - Delta list to insert into 04522 04523 NewTimer - Timer element to insert into list 04524 04525 TimeRemaining - This time must be added to the head of the list to get "real" 04526 relative time. 04527 04528 NewFiringTime - If the new element was inserted at the head of the list - this 04529 will contain the new firing time in milliseconds. The caller 04530 can use this time to re-program the NT timer. This MUST NOT be 04531 changed if the function returns FALSE. 04532 04533 Return Value: 04534 04535 TRUE - If the timer was inserted at head of delta list 04536 04537 FALSE - otherwise 04538 04539 --*/ 04540 { 04541 PLIST_ENTRY Node ; 04542 PRTLP_GENERIC_TIMER Temp ; 04543 PRTLP_GENERIC_TIMER Head ; 04544 04545 if (IsListEmpty (DeltaList)) { 04546 04547 InsertHeadList (DeltaList, &NewTimer->List) ; 04548 04549 *NewFiringTime = NewTimer->DeltaFiringTime ; 04550 04551 NewTimer->DeltaFiringTime = 0 ; 04552 04553 return TRUE ; 04554 04555 } 04556 04557 // Adjust the head of the list to reflect the time remaining on the NT timer 04558 04559 Head = CONTAINING_RECORD (DeltaList->Flink, RTLP_GENERIC_TIMER, List) ; 04560 04561 Head->DeltaFiringTime += TimeRemaining ; 04562 04563 04564 // Find the appropriate location to insert this element in 04565 04566 for (Node = DeltaList->Flink ; Node != DeltaList ; Node = Node->Flink) { 04567 04568 Temp = CONTAINING_RECORD (Node, RTLP_GENERIC_TIMER, List) ; 04569 04570 04571 if (Temp->DeltaFiringTime <= NewTimer->DeltaFiringTime) { 04572 04573 NewTimer->DeltaFiringTime -= Temp->DeltaFiringTime ; 04574 04575 } else { 04576 04577 // found appropriate place to insert this timer 04578 04579 break ; 04580 04581 } 04582 04583 } 04584 04585 // Either we have found the appopriate node to insert before in terms of deltas. 04586 // OR we have come to the end of the list. Insert this timer here. 04587 04588 InsertHeadList (Node->Blink, &NewTimer->List) ; 04589 04590 04591 // If this isnt the last element in the list - adjust the delta of the 04592 // next element 04593 04594 if (Node != DeltaList) { 04595 04596 Temp->DeltaFiringTime -= NewTimer->DeltaFiringTime ; 04597 04598 } 04599 04600 04601 // Check if element was inserted at head of list 04602 04603 if (DeltaList->Flink == &NewTimer->List) { 04604 04605 // Set NewFiringTime to the time in milliseconds when the new head of list 04606 // should be serviced. 04607 04608 *NewFiringTime = NewTimer->DeltaFiringTime ; 04609 04610 // This means the timer must be programmed to service this request 04611 04612 NewTimer->DeltaFiringTime = 0 ; 04613 04614 return TRUE ; 04615 04616 } else { 04617 04618 // No change to the head of the list, set the delta time back 04619 04620 Head->DeltaFiringTime -= TimeRemaining ; 04621 04622 return FALSE ; 04623 04624 } 04625 04626 } 04627 04628 04629 04630 BOOLEAN 04631 RtlpRemoveFromDeltaList ( 04632 PLIST_ENTRY DeltaList, 04633 PRTLP_GENERIC_TIMER Timer, 04634 ULONG TimeRemaining, 04635 ULONG* NewFiringTime 04636 ) 04637 /*++ 04638 04639 Routine Description: 04640 04641 Removes the specified timer from the delta list 04642 04643 Arguments: 04644 04645 DeltaList - Delta list to insert into 04646 04647 Timer - Timer element to insert into list 04648 04649 TimerHandle - Handle of the NT Timer object 04650 04651 TimeRemaining - This time must be added to the head of the list to get "real" 04652 relative time. 04653 04654 Return Value: 04655 04656 TRUE if the timer was removed from head of timer list 04657 FALSE otherwise 04658 04659 --*/ 04660 { 04661 PLIST_ENTRY Next ; 04662 PRTLP_GENERIC_TIMER Temp ; 04663 04664 Next = Timer->List.Flink ; 04665 04666 RemoveEntryList (&Timer->List) ; 04667 04668 if (IsListEmpty (DeltaList)) { 04669 04670 *NewFiringTime = INFINITE_TIME ; 04671 04672 return TRUE ; 04673 04674 } 04675 04676 if (Next == DeltaList) { 04677 04678 // If we removed the last element in the list nothing to do either 04679 04680 return FALSE ; 04681 04682 } else { 04683 04684 Temp = CONTAINING_RECORD ( Next, RTLP_GENERIC_TIMER, List) ; 04685 04686 Temp->DeltaFiringTime += Timer->DeltaFiringTime ; 04687 04688 // Check if element was removed from head of list 04689 04690 if (DeltaList->Flink == Next) { 04691 04692 *NewFiringTime = Temp->DeltaFiringTime + TimeRemaining ; 04693 04694 Temp->DeltaFiringTime = 0 ; 04695 04696 return TRUE ; 04697 04698 } else { 04699 04700 return FALSE ; 04701 04702 } 04703 04704 } 04705 04706 } 04707 04708 04709 04710 BOOLEAN 04711 RtlpReOrderDeltaList ( 04712 PLIST_ENTRY DeltaList, 04713 PRTLP_GENERIC_TIMER Timer, 04714 ULONG TimeRemaining, 04715 ULONG *NewFiringTime, 04716 ULONG ChangedFiringTime 04717 ) 04718 /*++ 04719 04720 Routine Description: 04721 04722 Called when a timer in the delta list needs to be re-inserted because the firing time 04723 has changed. 04724 04725 Arguments: 04726 04727 DeltaList - List in which to re-insert 04728 04729 Timer - Timer for which the firing time has changed 04730 04731 TimeRemaining - Time before the head of the delta list is fired 04732 04733 NewFiringTime - If the new element was inserted at the head of the list - this 04734 will contain the new firing time in milliseconds. The caller 04735 can use this time to re-program the NT timer. 04736 04737 ChangedFiringTime - Changed Time for the specified timer. 04738 04739 Return Value: 04740 04741 TRUE if the timer was removed from head of timer list 04742 FALSE otherwise 04743 04744 --*/ 04745 { 04746 ULONG NewTimeRemaining ; 04747 PRTLP_GENERIC_TIMER Temp ; 04748 04749 // Remove the timer from the list 04750 04751 if (RtlpRemoveFromDeltaList (DeltaList, Timer, TimeRemaining, NewFiringTime)) { 04752 04753 // If element was removed from the head of the list we should record that 04754 04755 NewTimeRemaining = *NewFiringTime ; 04756 04757 04758 } else { 04759 04760 // Element was not removed from head of delta list, the current TimeRemaining is valid 04761 04762 NewTimeRemaining = TimeRemaining ; 04763 04764 } 04765 04766 // Before inserting Timer, set its delta time to the ChangedFiringTime 04767 04768 Timer->DeltaFiringTime = ChangedFiringTime ; 04769 04770 // Reinsert this element back in the list 04771 04772 if (!RtlpInsertInDeltaList (DeltaList, Timer, NewTimeRemaining, NewFiringTime)) { 04773 04774 // If we did not add at the head of the list, then we should return TRUE if 04775 // RtlpRemoveFromDeltaList() had returned TRUE. We also update the NewFiringTime to 04776 // the reflect the new firing time returned by RtlpRemoveFromDeltaList() 04777 04778 *NewFiringTime = NewTimeRemaining ; 04779 04780 return (NewTimeRemaining != TimeRemaining) ; 04781 04782 } else { 04783 04784 // NewFiringTime contains the time the NT timer must be programmed for 04785 04786 return TRUE ; 04787 04788 } 04789 04790 } 04791 04792 04793 VOID 04794 RtlpAddTimerQueue ( 04795 PVOID Queue 04796 ) 04797 /*++ 04798 04799 Routine Description: 04800 04801 This routine runs as an APC into the Timer thread. It does whatever necessary to 04802 create a new timer queue 04803 04804 Arguments: 04805 04806 Queue - Pointer to the queue to add 04807 04808 Return Value: 04809 04810 04811 --*/ 04812 { 04813 04814 // We do nothing here. The newly created queue is free floating until a timer is 04815 // queued onto it. 04816 04817 } 04818 04819 04820 VOID 04821 RtlpServiceTimer ( 04822 PVOID NotUsedArg, 04823 ULONG NotUsedLowTimer, 04824 LONG NotUsedHighTimer 04825 ) 04826 /*++ 04827 04828 Routine Description: 04829 04830 Services the timer. Runs in an APC. 04831 04832 Arguments: 04833 04834 NotUsedArg - Argument is not used in this function. 04835 04836 NotUsedLowTimer - Argument is not used in this function. 04837 04838 NotUsedHighTimer - Argument is not used in this function. 04839 04840 Return Value: 04841 04842 Remarks: 04843 This APC is called only for timeouts of timer threads. 04844 04845 --*/ 04846 { 04847 PRTLP_TIMER Timer ; 04848 PRTLP_TIMER_QUEUE Queue ; 04849 PLIST_ENTRY TNode ; 04850 PLIST_ENTRY QNode ; 04851 PLIST_ENTRY Temp ; 04852 ULONG NewFiringTime ; 04853 LIST_ENTRY ReinsertTimerQueueList ; 04854 LIST_ENTRY TimersToFireList ; 04855 04856 RtlpResync64BitTickCount() ; 04857 04858 if (DPRN2) { 04859 DbgPrint("Before service timer ThreadId<%x:%x>\n", 04860 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 04861 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)); 04862 RtlDebugPrintTimes (); 04863 } 04864 04865 ACQUIRE_GLOBAL_TIMER_LOCK(); 04866 04867 // fire it if it even 200ms ahead. else reset the timer 04868 04869 if (Firing64BitTickCount.QuadPart > RtlpGet64BitTickCount(&Last64BitTickCount) + 200) { 04870 04871 RtlpResetTimer (TimerHandle, RtlpGetTimeRemaining (TimerHandle), NULL) ; 04872 04873 RELEASE_GLOBAL_TIMER_LOCK() ; 04874 return ; 04875 } 04876 04877 InitializeListHead (&ReinsertTimerQueueList) ; 04878 04879 InitializeListHead (&TimersToFireList) ; 04880 04881 04882 // We run thru all queues with DeltaFiringTime == 0 and fire all timers that 04883 // have DeltaFiringTime == 0. We remove the fired timers and either free them 04884 // (for one shot timers) or put them in aside list (for periodic timers). 04885 // After we have finished firing all timers in a queue we reinsert the timers 04886 // in the aside list back into the queue based on their new firing times. 04887 // 04888 // Similarly, we remove each fired Queue and put it in a aside list. After firing 04889 // all queues with DeltaFiringTime == 0, we reinsert the Queues in the aside list 04890 // and reprogram the NT timer to be the firing time of the first queue in the list 04891 04892 04893 for (QNode = TimerQueues.Flink ; QNode != &TimerQueues ; QNode = QNode->Flink) { 04894 04895 Queue = CONTAINING_RECORD (QNode, RTLP_TIMER_QUEUE, List) ; 04896 04897 // If the delta time in the timer queue is 0 - then this queue 04898 // has timers that are ready to fire. Walk the list and fire all timers with 04899 // Delta time of 0 04900 04901 if (Queue->DeltaFiringTime == 0) { 04902 04903 // Walk all timers with DeltaFiringTime == 0 and fire them. After that 04904 // reinsert the periodic timers in the appropriate place. 04905 04906 RtlpFireTimersAndReorder (Queue, &NewFiringTime, &TimersToFireList) ; 04907 04908 // detach this Queue from the list 04909 04910 QNode = QNode->Blink ; 04911 04912 RemoveEntryList (QNode->Flink) ; 04913 04914 // If there are timers in the queue then prepare to reinsert the queue in 04915 // TimerQueues. 04916 04917 if (NewFiringTime != INFINITE_TIME) { 04918 04919 Queue->DeltaFiringTime = NewFiringTime ; 04920 04921 // put the timer in list that we will process after we have 04922 // fired all elements in this queue 04923 04924 InsertHeadList (&ReinsertTimerQueueList, &Queue->List) ; 04925 04926 } else { 04927 04928 // Queue has no more timers in it. Let the Queue float. 04929 04930 InitializeListHead (&Queue->List) ; 04931 04932 } 04933 04934 04935 } else { 04936 04937 // No more Queues with DeltaFiringTime == 0 04938 04939 break ; 04940 04941 } 04942 04943 } 04944 04945 // At this point we have fired all the ready timers. We have two lists that need to be 04946 // merged - TimerQueues and ReinsertTimerQueueList. The following steps do this - at the 04947 // end of this we will reprogram the NT Timer. 04948 04949 if (!IsListEmpty(&TimerQueues)) { 04950 04951 Queue = CONTAINING_RECORD (TimerQueues.Flink, RTLP_TIMER_QUEUE, List) ; 04952 04953 NewFiringTime = Queue->DeltaFiringTime ; 04954 04955 Queue->DeltaFiringTime = 0 ; 04956 04957 if (!IsListEmpty (&ReinsertTimerQueueList)) { 04958 04959 // TimerQueues and ReinsertTimerQueueList are both non-empty. Merge them. 04960 04961 RtlpInsertTimersIntoDeltaList (&ReinsertTimerQueueList, &TimerQueues, 04962 NewFiringTime, &NewFiringTime) ; 04963 04964 } 04965 04966 // NewFiringTime contains the time the NT Timer should be programmed to. 04967 04968 } else { 04969 04970 if (!IsListEmpty (&ReinsertTimerQueueList)) { 04971 04972 // TimerQueues is empty. ReinsertTimerQueueList is not. 04973 04974 RtlpInsertTimersIntoDeltaList (&ReinsertTimerQueueList, &TimerQueues, 0, 04975 &NewFiringTime) ; 04976 04977 } else { 04978 04979 NewFiringTime = INFINITE_TIME ; 04980 04981 } 04982 04983 // NewFiringTime contains the time the NT Timer should be programmed to. 04984 04985 } 04986 04987 04988 // Reset the timer to reflect the Delta time associated with the first Queue 04989 04990 RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ; 04991 04992 if (DPRN3) { 04993 DbgPrint("After service timer:ThreadId<%x:%x>\n", 04994 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread), 04995 HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)); 04996 RtlDebugPrintTimes (); 04997 } 04998 04999 05000 // finally fire all the timers 05001 05002 RtlpFireTimers( &TimersToFireList ) ; 05003 05004 RELEASE_GLOBAL_TIMER_LOCK(); 05005 05006 } 05007 05008 05009 VOID 05010 RtlpFireTimersAndReorder ( 05011 PRTLP_TIMER_QUEUE Queue, 05012 ULONG *NewFiringTime, 05013 PLIST_ENTRY TimersToFireList 05014 ) 05015 /*++ 05016 05017 Routine Description: 05018 05019 Fires all timers in TimerList that have DeltaFiringTime == 0. After firing the timers 05020 it reorders the timers based on their periodic times OR frees the fired one shot timers. 05021 05022 Arguments: 05023 05024 TimerList - Timer list to work thru. 05025 05026 NewFiringTime - Location where the new firing time for the first timer in the delta list 05027 is returned. 05028 05029 05030 Return Value: 05031 05032 05033 --*/ 05034 { 05035 PLIST_ENTRY TNode ; 05036 PRTLP_TIMER Timer ; 05037 LIST_ENTRY ReinsertTimerList ; 05038 PLIST_ENTRY TimerList = &Queue->TimerList ; 05039 05040 InitializeListHead (&ReinsertTimerList) ; 05041 *NewFiringTime = 0 ; 05042 05043 05044 for (TNode = TimerList->Flink ; (TNode != TimerList) && (*NewFiringTime == 0); 05045 TNode = TimerList->Flink) 05046 { 05047 05048 Timer = CONTAINING_RECORD (TNode, RTLP_TIMER, List) ; 05049 05050 // Fire all timers with delta time of 0 05051 05052 if (Timer->DeltaFiringTime == 0) { 05053 05054 // detach this timer from the list 05055 05056 RemoveEntryList (TNode) ; 05057 05058 // get next firing time 05059 05060 if (!IsListEmpty(TimerList)) { 05061 05062 PRTLP_TIMER TmpTimer ; 05063 05064 TmpTimer = CONTAINING_RECORD (TimerList->Flink, RTLP_TIMER, List) ; 05065 05066 *NewFiringTime = TmpTimer->DeltaFiringTime ; 05067 05068 TmpTimer->DeltaFiringTime = 0 ; 05069 05070 } else { 05071 05072 *NewFiringTime = INFINITE_TIME ; 05073 } 05074 05075 05076 // if timer is not periodic then remove active state. Timer will be deleted 05077 // when cancel timer is called. 05078 05079 if (Timer->Period == 0) { 05080 05081 InsertHeadList( &Queue->UncancelledTimerList, &Timer->List ) ; 05082 05083 // if one shot wait was timed out then, deactivate the wait 05084 05085 if ( Timer->Wait ) { 05086 05087 // 05088 // though timer is being "freed" in this call, it can still be 05089 // used after this call 05090 // 05091 05092 RtlpDeactivateWait( Timer->Wait ) ; 05093 } 05094 05095 else { 05096 // should be set at the end 05097 05098 Timer->State &= ~STATE_ACTIVE ; 05099 } 05100 05101 Timer->State |= STATE_ONE_SHOT_FIRED ; 05102 05103 } else { 05104 05105 // Set the DeltaFiringTime to be the next period 05106 05107 Timer->DeltaFiringTime = Timer->Period ; 05108 05109 // reinsert the timer in the list. 05110 05111 RtlpInsertInDeltaList (TimerList, Timer, *NewFiringTime, NewFiringTime) ; 05112 } 05113 05114 05115 // Call the function associated with this timer. call it in the end 05116 // so that RtlTimer calls can be made in the timer function 05117 05118 if ( (Timer->State & STATE_DONTFIRE) 05119 || (Timer->Queue->State & STATE_DONTFIRE) ) 05120 { 05121 ; 05122 05123 } else { 05124 05125 InsertTailList( TimersToFireList, &Timer->TimersToFireList ) ; 05126 05127 } 05128 05129 } else { 05130 05131 // No more Timers with DeltaFiringTime == 0 05132 05133 break ; 05134 05135 } 05136 } 05137 05138 05139 if ( *NewFiringTime == 0 ) { 05140 *NewFiringTime = INFINITE_TIME ; 05141 } 05142 } 05143 05144 05145 VOID 05146 RtlpInsertTimersIntoDeltaList ( 05147 IN PLIST_ENTRY NewTimerList, 05148 IN PLIST_ENTRY DeltaTimerList, 05149 IN ULONG TimeRemaining, 05150 OUT ULONG *NewFiringTime 05151 ) 05152 /*++ 05153 05154 Routine Description: 05155 05156 This routine walks thru a list of timers in NewTimerList and inserts them into a delta 05157 timers list pointed to by DeltaTimerList. The timeout associated with the first element 05158 in the new list is returned in NewFiringTime. 05159 05160 Arguments: 05161 05162 NewTimerList - List of timers that need to be inserted into the DeltaTimerList 05163 05164 DeltaTimerList - Existing delta list of zero or more timers. 05165 05166 TimeRemaining - Firing time of the first element in the DeltaTimerList 05167 05168 NewFiringTime - Location where the new firing time will be returned 05169 05170 Return Value: 05171 05172 05173 --*/ 05174 { 05175 PRTLP_GENERIC_TIMER Timer ; 05176 PLIST_ENTRY TNode ; 05177 PLIST_ENTRY Temp ; 05178 05179 for (TNode = NewTimerList->Flink ; TNode != NewTimerList ; TNode = TNode->Flink) { 05180 05181 Temp = TNode->Blink ; 05182 05183 RemoveEntryList (Temp->Flink) ; 05184 05185 Timer = CONTAINING_RECORD (TNode, RTLP_GENERIC_TIMER, List) ; 05186 05187 if (RtlpInsertInDeltaList (DeltaTimerList, Timer, TimeRemaining, NewFiringTime)) { 05188 05189 TimeRemaining = *NewFiringTime ; 05190 05191 } 05192 05193 TNode = Temp ; 05194 05195 } 05196 05197 } 05198 05199 05200 LONG 05201 RtlpTimerThread ( 05202 PVOID Initialized 05203 ) 05204 /*++ 05205 05206 Routine Description: 05207 05208 All the timer activity takes place in APCs. 05209 05210 Arguments: 05211 05212 Initialized - Used to notify the starter of the thread that thread initialization 05213 has completed 05214 05215 Return Value: 05216 05217 --*/ 05218 { 05219 LARGE_INTEGER TimeOut ; 05220 05221 // no structure initializations should be done here as new timer thread 05222 // may be created after threadPoolCleanup 05223 05224 05225 TimerThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ; 05226 05227 05228 // Reset the NT Timer to never fire initially 05229 05230 RtlpResetTimer (TimerHandle, -1, NULL) ; 05231 05232 // Notify starter of this thread that it has initialized 05233 05234 InterlockedExchange ((ULONG *) Initialized, 1) ; 05235 05236 // Sleep alertably so that all the activity can take place 05237 // in APCs 05238 05239 for ( ; ; ) { 05240 05241 // Set timeout for the largest timeout possible 05242 05243 TimeOut.LowPart = 0 ; 05244 TimeOut.HighPart = 0x80000000 ; 05245 05246 NtDelayExecution (TRUE, &TimeOut) ; 05247 05248 } 05249 05250 return 0 ; // Keep compiler happy 05251 05252 } 05253 05254 05255 NTSTATUS 05256 RtlpInitializeTimerThreadPool ( 05257 ) 05258 /*++ 05259 05260 Routine Description: 05261 05262 This routine is used to initialize structures used for Timer Thread 05263 05264 Arguments: 05265 05266 05267 Return Value: 05268 05269 --*/ 05270 { 05271 NTSTATUS Status = STATUS_SUCCESS; 05272 LARGE_INTEGER TimeOut ; 05273 05274 // In order to avoid an explicit RtlInitialize() function to initialize the wait thread pool 05275 // we use StartedTimerInitialization and CompletedTimerInitialization to provide us the 05276 // necessary synchronization to avoid multiple threads from initializing the thread pool. 05277 // This scheme does not work if RtlInitializeCriticalSection() or NtCreateEvent fails - but in this case the 05278 // caller has not choices left. 05279 05280 if (!InterlockedExchange(&StartedTimerInitialization, 1L)) { 05281 05282 if (CompletedTimerInitialization) 05283 InterlockedExchange(&CompletedTimerInitialization, 0 ) ; 05284 05285 do { 05286 05287 // Initialize global timer lock 05288 05289 Status = RtlInitializeCriticalSection( &TimerCriticalSection ) ; 05290 if (! NT_SUCCESS( Status )) { 05291 break ; 05292 } 05293 05294 Status = NtCreateTimer( 05295 &TimerHandle, 05296 TIMER_ALL_ACCESS, 05297 NULL, 05298 NotificationTimer 05299 ) ; 05300 05301 if (!NT_SUCCESS(Status) ) 05302 break ; 05303 05304 InitializeListHead (&TimerQueues) ; // Initialize Timer Queue Structures 05305 05306 05307 // initialize tick count 05308 05309 Resync64BitTickCount.QuadPart = NtGetTickCount() ; 05310 Firing64BitTickCount.QuadPart = 0 ; 05311 05312 05313 Status = RtlpStartThreadFunc (RtlpTimerThread, &TimerThreadHandle) ; 05314 05315 if (!NT_SUCCESS(Status) ) 05316 break ; 05317 05318 05319 } while(FALSE ) ; 05320 05321 if (!NT_SUCCESS(Status) ) { 05322 05323 ASSERT (Status == STATUS_SUCCESS) ; 05324 05325 StartedTimerInitialization = 0 ; 05326 InterlockedExchange (&CompletedTimerInitialization, ~0) ; 05327 05328 return Status ; 05329 } 05330 05331 InterlockedExchange (&CompletedTimerInitialization, 1L) ; 05332 05333 } else { 05334 05335 // Sleep 1 ms and see if the other thread has completed initialization 05336 05337 ONE_MILLISECOND_TIMEOUT(TimeOut) ; 05338 05339 while (!(volatile ULONG) CompletedTimerInitialization) { 05340 05341 NtDelayExecution (FALSE, &TimeOut) ; 05342 05343 } 05344 05345 if (CompletedTimerInitialization != 1) 05346 Status = STATUS_NO_MEMORY ; 05347 } 05348 05349 return NT_SUCCESS(Status) ? STATUS_SUCCESS : Status ; 05350 } 05351 05352 05353 NTSTATUS 05354 RtlpDeleteTimerQueue ( 05355 PRTLP_TIMER_QUEUE Queue 05356 ) 05357 /*++ 05358 05359 Routine Description: 05360 05361 This routine deletes the queue specified in the Request and frees all timers 05362 05363 Arguments: 05364 05365 Queue - queue to delete 05366 05367 Event - Event Handle used for signalling completion of request 05368 05369 Return Value: 05370 05371 --*/ 05372 { 05373 ULONG TimeRemaining ; 05374 ULONG NewFiringTime ; 05375 PLIST_ENTRY Node ; 05376 PRTLP_TIMER Timer ; 05377 05378 RtlpResync64BitTickCount() ; 05379 05380 SET_DEL_TIMERQ_SIGNATURE( Queue ) ; 05381 05382 05383 // If there are no timers in the queue then it is not attached to TimerQueues 05384 // In this case simply free the memory and return. Otherwise we have to first 05385 // remove the queue from the TimerQueues List, update the firing time if this 05386 // was the first queue in the list and then walk all the timers and free them 05387 // before freeing the Timer Queue. 05388 05389 if (!IsListEmpty (&Queue->List)) { 05390 05391 TimeRemaining = RtlpGetTimeRemaining (TimerHandle) 05392 + RtlpGetQueueRelativeTime (Queue) ; 05393 05394 if (RtlpRemoveFromDeltaList (&TimerQueues, Queue, TimeRemaining, 05395 &NewFiringTime)) 05396 { 05397 05398 // If removed from head of queue list, reset the timer 05399 05400 RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ; 05401 } 05402 05403 05404 // Free all the timers associated with this queue 05405 05406 for (Node = Queue->TimerList.Flink ; Node != &Queue->TimerList ; ) { 05407 05408 Timer = CONTAINING_RECORD (Node, RTLP_TIMER, List) ; 05409 05410 Node = Node->Flink ; 05411 05412 RtlpCancelTimerEx( Timer ,TRUE ) ; // Queue being deleted 05413 } 05414 } 05415 05416 05417 // Free all the uncancelled one shot timers in this queue 05418 05419 for (Node = Queue->UncancelledTimerList.Flink ; Node != &Queue->UncancelledTimerList ; ) { 05420 05421 Timer = CONTAINING_RECORD (Node, RTLP_TIMER, List) ; 05422 05423 Node = Node->Flink ; 05424 05425 RtlpCancelTimerEx( Timer ,TRUE ) ; // Queue being deleted 05426 } 05427 05428 05429 // delete the queue completely if the RefCount is 0 05430 05431 if ( InterlockedDecrement( &Queue->RefCount ) == 0 ) { 05432 05433 RtlpDeleteTimerQueueComplete( Queue ) ; 05434 05435 return STATUS_SUCCESS ; 05436 05437 } else { 05438 05439 return STATUS_PENDING ; 05440 } 05441 05442 } 05443 05444 05445 05446 /*++ 05447 05448 Routine Description: 05449 05450 This routine frees the queue and sets the event. 05451 05452 Arguments: 05453 05454 Queue - queue to delete 05455 05456 Event - Event Handle used for signalling completion of request 05457 05458 Return Value: 05459 05460 --*/ 05461 VOID 05462 RtlpDeleteTimerQueueComplete ( 05463 PRTLP_TIMER_QUEUE Queue 05464 ) 05465 { 05466 #if DBG1 05467 if (DPRN1) 05468 DbgPrint("<%d> Queue: %x: deleted\n\n", Queue->DbgId, 05469 (ULONG_PTR)Queue) ; 05470 #endif 05471 05472 InterlockedDecrement( &NumTimerQueues ) ; 05473 05474 // Notify the thread issuing the cancel that the request is completed 05475 05476 if ( Queue->CompletionEvent ) 05477 NtSetEvent (Queue->CompletionEvent, NULL) ; 05478 05479 RtlpFreeTPHeap( Queue ) ; 05480 } 05481 05482 05483 VOID 05484 RtlpThreadCleanup ( 05485 ) 05486 /*++ 05487 05488 Routine Description: 05489 05490 This routine is used for exiting timer, wait and IOworker threads. 05491 05492 Arguments: 05493 05494 Return Value: 05495 05496 --*/ 05497 { 05498 NtTerminateThread( NtCurrentThread(), 0) ; 05499 } 05500 05501 05502 NTSTATUS 05503 RtlpWaitForEvent ( 05504 HANDLE Event, 05505 HANDLE ThreadHandle 05506 ) 05507 /*++ 05508 05509 Routine Description: 05510 05511 Waits for the event to be signalled. If the event is not signalled within 05512 one second, then checks to see that the thread is alive 05513 05514 Arguments: 05515 05516 Event : Event handle used for signalling completion of request 05517 05518 ThreadHandle: Thread to check whether still alive 05519 05520 Return Value: 05521 05522 STATUS_SUCCESS if event was signalled 05523 else return NTSTATUS 05524 05525 --*/ 05526 { 05527 NTSTATUS Status ; 05528 LARGE_INTEGER TimeOut ; 05529 05530 // Timeout of 1 second for request to complete 05531 ONE_SECOND_TIMEOUT(TimeOut) ; 05532 05533 Wait: 05534 05535 Status = NtWaitForSingleObject (Event, FALSE, &TimeOut) ; 05536 05537 if (Status == STATUS_TIMEOUT) { 05538 05539 // The wait timed out. Check to see if the wait thread is still alive. 05540 // This is done by trying to queue a dummy APC to it. There is no better 05541 // way known to determine if the thread has died unexpectedly. 05542 // If so then go back to waiting. 05543 05544 Status = NtQueueApcThread( 05545 ThreadHandle, 05546 (PPS_APC_ROUTINE)RtlpDoNothing, 05547 NULL, 05548 NULL, 05549 NULL 05550 ); 05551 05552 if (NT_SUCCESS(Status) ) { 05553 05554 // Wait thread is still alive. Go back to waiting. 05555 05556 goto Wait ; 05557 05558 } else { 05559 05560 // The wait thread died between the time the APC was queued and the 05561 // the time we started waiting on NtQueryInformationThread() 05562 05563 05564 DbgPrint ("Thread died before event could be signalled") ; 05565 05566 } 05567 05568 } 05569 05570 return NT_SUCCESS(Status) ? STATUS_SUCCESS : Status ; 05571 } 05572 05573 05574 PRTLP_EVENT 05575 RtlpGetWaitEvent ( 05576 VOID 05577 ) 05578 /*++ 05579 05580 Routine Description: 05581 05582 Returns an event from the event cache. 05583 05584 Arguments: 05585 05586 None 05587 05588 Return Value: 05589 05590 Pointer to event structure 05591 05592 --*/ 05593 { 05594 NTSTATUS Status; 05595 PRTLP_EVENT Event ; 05596 05597 if (!CompletedEventCacheInitialization) { 05598 05599 RtlpInitializeEventCache () ; 05600 05601 } 05602 05603 RtlEnterCriticalSection (&EventCacheCriticalSection) ; 05604 05605 if (!IsListEmpty (&EventCache)) { 05606 05607 Event = (PRTLP_EVENT) RemoveHeadList (&EventCache) ; 05608 05609 } else { 05610 05611 Event = RtlpForceAllocateTPHeap( sizeof( RTLP_EVENT ), 0 ); 05612 05613 if (!Event) { 05614 05615 RtlLeaveCriticalSection (&EventCacheCriticalSection) ; 05616 05617 return NULL ; 05618 05619 } 05620 05621 Status = NtCreateEvent( 05622 &Event->Handle, 05623 EVENT_ALL_ACCESS, 05624 NULL, 05625 SynchronizationEvent, 05626 FALSE 05627 ); 05628 05629 if (!NT_SUCCESS(Status) ) { 05630 05631 RtlpFreeTPHeap( Event ) ; 05632 05633 RtlLeaveCriticalSection (&EventCacheCriticalSection) ; 05634 05635 return NULL ; 05636 05637 } 05638 05639 } 05640 05641 RtlLeaveCriticalSection (&EventCacheCriticalSection) ; 05642 05643 return Event ; 05644 } 05645 05646 05647 VOID 05648 RtlpFreeWaitEvent ( 05649 PRTLP_EVENT Event 05650 ) 05651 /*++ 05652 05653 Routine Description: 05654 05655 Frees the event to the event cache 05656 05657 Arguments: 05658 05659 Event - the event struct to put back into the cache 05660 05661 Return Value: 05662 05663 Nothing 05664 05665 --*/ 05666 { 05667 05668 if ( Event == NULL ) 05669 return ; 05670 05671 InitializeListHead (&Event->List) ; 05672 05673 RtlEnterCriticalSection (&EventCacheCriticalSection) ; 05674 05675 if ( NumUnusedEvents > MAX_UNUSED_EVENTS ) { 05676 05677 NtClose( Event->Handle ) ; 05678 05679 RtlpFreeTPHeap( Event ) ; 05680 05681 } else { 05682 05683 InsertHeadList (&EventCache, &Event->List) ; 05684 NumUnusedEvents++ ; 05685 } 05686 05687 05688 RtlLeaveCriticalSection (&EventCacheCriticalSection) ; 05689 } 05690 05691 05692 05693 VOID 05694 RtlpInitializeEventCache ( 05695 VOID 05696 ) 05697 /*++ 05698 05699 Routine Description: 05700 05701 Initializes the event cache 05702 05703 Arguments: 05704 05705 None 05706 05707 Return Value: 05708 05709 Nothing 05710 05711 --*/ 05712 { 05713 NTSTATUS Status; 05714 LARGE_INTEGER TimeOut ; 05715 05716 if (!InterlockedExchange(&StartedEventCacheInitialization, 1L)) { 05717 05718 InitializeListHead (&EventCache) ; 05719 05720 Status = RtlInitializeCriticalSection(&EventCacheCriticalSection) ; 05721 05722 ASSERT (Status == STATUS_SUCCESS) ; 05723 05724 NumUnusedEvents = 0 ; 05725 05726 InterlockedExchange (&CompletedEventCacheInitialization, 1L) ; 05727 05728 } else { 05729 05730 // sleep for 1 milliseconds and see if the initialization is complete 05731 05732 ONE_MILLISECOND_TIMEOUT(TimeOut) ; 05733 05734 while (!(volatile ULONG) CompletedEventCacheInitialization) { 05735 05736 NtDelayExecution (FALSE, &TimeOut) ; 05737 05738 } 05739 05740 } 05741 } 05742 05743 05744 VOID 05745 PrintTimerQueue(PLIST_ENTRY QNode, ULONG Delta, ULONG Count 05746 ) 05747 { 05748 PLIST_ENTRY Tnode ; 05749 PRTLP_TIMER Timer ; 05750 PRTLP_TIMER_QUEUE Queue ; 05751 05752 Queue = CONTAINING_RECORD (QNode, RTLP_TIMER_QUEUE, List) ; 05753 DbgPrint("<%1d> Queue: %x FiringTime:%d\n", Count, (ULONG_PTR)Queue, 05754 Queue->DeltaFiringTime); 05755 for (Tnode=Queue->TimerList.Flink; Tnode!=&Queue->TimerList; 05756 Tnode=Tnode->Flink) 05757 { 05758 Timer = CONTAINING_RECORD (Tnode, RTLP_TIMER, List) ; 05759 Delta += Timer->DeltaFiringTime ; 05760 DbgPrint(" Timer: %x Delta:%d Period:%d\n",(ULONG_PTR)Timer, 05761 Delta, Timer->Period); 05762 } 05763 05764 } 05765 05766 VOID 05767 RtlDebugPrintTimes ( 05768 ) 05769 { 05770 PLIST_ENTRY QNode ; 05771 ULONG Count = 0 ; 05772 ULONG Delta = RtlpGetTimeRemaining (TimerHandle) ; 05773 ULONG CurrentThreadId = 05774 HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ; 05775 05776 RtlpResync64BitTickCount(); 05777 05778 if (CompletedTimerInitialization != 1) { 05779 05780 DbgPrint("===========RtlTimerThread not yet initialized==========\n"); 05781 return ; 05782 } 05783 05784 if (CurrentThreadId == TimerThreadId) 05785 { 05786 PRTLP_TIMER_QUEUE Queue ; 05787 05788 DbgPrint("================Printing timerqueues====================\n"); 05789 DbgPrint("TimeRemaining: %d\n", Delta); 05790 for (QNode = TimerQueues.Flink; QNode != &TimerQueues; 05791 QNode = QNode->Flink) 05792 { 05793 Queue = CONTAINING_RECORD (QNode, RTLP_TIMER_QUEUE, List) ; 05794 Delta += Queue->DeltaFiringTime ; 05795 05796 PrintTimerQueue(QNode, Delta, ++Count); 05797 05798 } 05799 DbgPrint("================Printed ================================\n"); 05800 } 05801 05802 else 05803 { 05804 NtQueueApcThread( 05805 TimerThreadHandle, 05806 (PPS_APC_ROUTINE)RtlDebugPrintTimes, 05807 NULL, 05808 NULL, 05809 NULL 05810 ); 05811 } 05812 } 05813 05814 05815 /*DO NOT USE THIS FUNCTION: REPLACED BY RTLCREATETIMER*/ 05816 05817 NTSTATUS 05818 RtlSetTimer( 05819 IN HANDLE TimerQueueHandle, 05820 OUT HANDLE *Handle, 05821 IN WAITORTIMERCALLBACKFUNC Function, 05822 IN PVOID Context, 05823 IN ULONG DueTime, 05824 IN ULONG Period, 05825 IN ULONG Flags 05826 ) 05827 { 05828 static ULONG Count = 0; 05829 if (Count++ ==0) { 05830 DbgPrint("Using obsolete function call: RtlSetTimer\n"); 05831 DbgBreakPoint(); 05832 DbgPrint("Using obsolete function call: RtlSetTimer\n"); 05833 } 05834 05835 return RtlCreateTimer(TimerQueueHandle, 05836 Handle, 05837 Function, 05838 Context, 05839 DueTime, 05840 Period, 05841 Flags 05842 ) ; 05843 } 05844 05845 05846 PVOID 05847 RtlpForceAllocateTPHeap( 05848 ULONG dwSize, 05849 ULONG dwFlags 05850 ) 05851 /*++ 05852 Routine Description: 05853 05854 This routine goes into an infinite loop trying to allocate the memory. 05855 05856 Arguments: 05857 05858 dwSize - size of memory to be allocated 05859 05860 dwFlags - Flags for memory allocation 05861 05862 Return Value: 05863 05864 ptr to memory 05865 05866 --*/ 05867 { 05868 PVOID ptr; 05869 ptr = RtlpAllocateTPHeap(dwSize, dwFlags); 05870 if (ptr) 05871 return ptr; 05872 05873 { 05874 LARGE_INTEGER TimeOut ; 05875 do { 05876 05877 ONE_SECOND_TIMEOUT(TimeOut) ; 05878 05879 NtDelayExecution (FALSE, &TimeOut) ; 05880 05881 ptr = RtlpAllocateTPHeap(dwSize, dwFlags); 05882 if (ptr) 05883 break; 05884 05885 } while (TRUE) ; 05886 } 05887 return ptr; 05888 } 05889 05890 05891 05892 /*DO NOT USE THIS FUNCTION: REPLACED BY RTLDeleteTimer*/ 05893 05894 NTSTATUS 05895 RtlCancelTimer( 05896 IN HANDLE TimerQueueHandle, 05897 IN HANDLE TimerToCancel 05898 ) 05899 /*++ 05900 05901 Routine Description: 05902 05903 This routine cancels the timer. This call is non-blocking. The timer Callback 05904 will not be executed after this call returns. 05905 05906 Arguments: 05907 05908 TimerQueueHandle - Handle identifying the queue from which to delete timer 05909 05910 TimerToCancel - Handle identifying the timer to cancel 05911 05912 Return Value: 05913 05914 NTSTATUS - Result code from call. The following are returned 05915 05916 STATUS_SUCCESS - Timer cancelled. All callbacks completed. 05917 STATUS_PENDING - Timer cancelled. Some callbacks still not completed. 05918 05919 --*/ 05920 { 05921 static ULONG Count = 0; 05922 if (Count++ ==0) { 05923 DbgPrint("Using obsolete function call: RtlCancelTimer\n"); 05924 DbgBreakPoint(); 05925 DbgPrint("Using obsolete function call: RtlCancelTimer\n"); 05926 } 05927 05928 return RtlDeleteTimer( TimerQueueHandle, TimerToCancel, NULL ) ; 05929 } 05930

Generated on Sat May 15 19:41:58 2004 for test by doxygen 1.3.7