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

timer.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1989 Microsoft Corporation 00004 00005 Module Name: 00006 00007 timer.c 00008 00009 Abstract: 00010 00011 This module implements the executive timer object. Functions are provided 00012 to create, open, cancel, set, and query timer objects. 00013 00014 Author: 00015 00016 David N. Cutler (davec) 12-May-1989 00017 00018 Environment: 00019 00020 Kernel mode only. 00021 00022 Revision History: 00023 00024 --*/ 00025 00026 #include "exp.h" 00027 00028 // 00029 // Executive timer object structure definition. 00030 // 00031 00032 typedef struct _ETIMER { 00033 KTIMER KeTimer; 00034 KAPC TimerApc; 00035 KDPC TimerDpc; 00036 LIST_ENTRY ActiveTimerListEntry; 00037 KSPIN_LOCK Lock; 00038 LONG Period; 00039 BOOLEAN ApcAssociated; 00040 BOOLEAN WakeTimer; 00041 LIST_ENTRY WakeTimerListEntry; 00042 } ETIMER, *PETIMER; 00043 00044 // 00045 // List of all timers which are set to wake 00046 // 00047 00048 KSPIN_LOCK ExpWakeTimerListLock; 00049 LIST_ENTRY ExpWakeTimerList; 00050 00051 // 00052 // Address of timer object type descriptor. 00053 // 00054 00055 POBJECT_TYPE ExTimerObjectType; 00056 00057 // 00058 // Structure that describes the mapping of generic access rights to object 00059 // specific access rights for timer objects. 00060 // 00061 00062 GENERIC_MAPPING ExpTimerMapping = { 00063 STANDARD_RIGHTS_READ | 00064 TIMER_QUERY_STATE, 00065 STANDARD_RIGHTS_WRITE | 00066 TIMER_MODIFY_STATE, 00067 STANDARD_RIGHTS_EXECUTE | 00068 SYNCHRONIZE, 00069 TIMER_ALL_ACCESS 00070 }; 00071 00072 #ifdef ALLOC_PRAGMA 00073 #pragma alloc_text(INIT, ExpTimerInitialization) 00074 #pragma alloc_text(PAGE, NtCreateTimer) 00075 #pragma alloc_text(PAGE, NtOpenTimer) 00076 #pragma alloc_text(PAGE, NtQueryTimer) 00077 #pragma alloc_text(PAGELK, ExGetNextWakeTime) 00078 #endif 00079 00080 VOID 00081 ExpTimerApcRoutine ( 00082 IN PKAPC Apc, 00083 IN PKNORMAL_ROUTINE *NormalRoutine, 00084 IN PVOID *NormalContext, 00085 IN PVOID *SystemArgument1, 00086 IN PVOID *SystemArgument2 00087 ) 00088 00089 /*++ 00090 00091 Routine Description: 00092 00093 This function is the special APC routine that is called to remove 00094 a timer from the current thread's active timer list. 00095 00096 Arguments: 00097 00098 Apc - Supplies a pointer to the APC object used to invoke this routine. 00099 00100 NormalRoutine - Supplies a pointer to a pointer to the normal routine 00101 function that was specified when the APC was initialized. 00102 00103 NormalContext - Supplies a pointer to a pointer to an arbitrary data 00104 structure that was specified when the APC was initialized. 00105 00106 SystemArgument1, SystemArgument2 - Supplies a set of two pointers to 00107 two arguments that contain untyped data. 00108 00109 Return Value: 00110 00111 None. 00112 00113 --*/ 00114 00115 { 00116 00117 BOOLEAN Dereference; 00118 PETHREAD ExThread; 00119 PETIMER ExTimer; 00120 KIRQL OldIrql1; 00121 00122 // 00123 // Get address of executive timer object and the current thread object. 00124 // 00125 00126 ExThread = PsGetCurrentThread(); 00127 ExTimer = CONTAINING_RECORD(Apc, ETIMER, TimerApc); 00128 00129 // 00130 // If the timer is still in the current thread's active timer list, then 00131 // remove it if it is not a periodic timer and set APC associated FALSE. 00132 // It is possible for the timer not to be in the current thread's active 00133 // timer list since the APC could have been delivered, and then another 00134 // thread could have set the timer again with another APC. This would 00135 // have caused the timer to be removed from the current thread's active 00136 // timer list. 00137 // 00138 // N. B. The spin locks for the timer and the active timer list must be 00139 // acquired in the order: 1) timer lock, 2) thread list lock. 00140 // 00141 00142 Dereference = FALSE; 00143 ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1); 00144 ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock); 00145 if ((ExTimer->ApcAssociated) && (&ExThread->Tcb == ExTimer->TimerApc.Thread)) { 00146 if (ExTimer->Period == 0) { 00147 RemoveEntryList(&ExTimer->ActiveTimerListEntry); 00148 ExTimer->ApcAssociated = FALSE; 00149 Dereference = TRUE; 00150 } 00151 00152 } else { 00153 *NormalRoutine = (PKNORMAL_ROUTINE)NULL; 00154 } 00155 00156 ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock); 00157 ExReleaseSpinLock(&ExTimer->Lock, OldIrql1); 00158 if (Dereference) { 00159 ObDereferenceObject((PVOID)ExTimer); 00160 } 00161 00162 return; 00163 } 00164 00165 VOID 00166 ExpTimerDpcRoutine ( 00167 IN PKDPC Dpc, 00168 IN PVOID DeferredContext, 00169 IN PVOID SystemArgument1, 00170 IN PVOID SystemArgument2 00171 ) 00172 00173 /*++ 00174 00175 Routine Description: 00176 00177 This function is the DPC routine that is called when a timer expires that 00178 has an associated APC routine. Its function is to insert the associated 00179 APC into the target thread's APC queue. 00180 00181 Arguments: 00182 00183 Dpc - Supplies a pointer to a control object of type DPC. 00184 00185 DeferredContext - Supplies a pointer to the executive timer that contains 00186 the DPC that caused this routine to be executed. 00187 00188 SystemArgument1, SystemArgument2 - Supplies values that are not used by 00189 this routine. 00190 00191 Return Value: 00192 00193 None. 00194 00195 --*/ 00196 00197 { 00198 00199 PETIMER ExTimer; 00200 PKTIMER KeTimer; 00201 KIRQL OldIrql; 00202 00203 // 00204 // Get address of executive and kernel timer objects. 00205 // 00206 00207 ExTimer = (PETIMER)DeferredContext; 00208 KeTimer = &ExTimer->KeTimer; 00209 00210 // 00211 // If there is still an APC associated with the timer, then insert the APC 00212 // in target thread's APC queue. It is possible that the timer does not 00213 // have an associated APC. This can happen when the timer is set to expire 00214 // by a thread running on another processor just after the DPC has been 00215 // removed from the DPC queue, but before it has acquired the timer related 00216 // spin lock. 00217 // 00218 00219 ExAcquireSpinLock(&ExTimer->Lock, &OldIrql); 00220 if (ExTimer->ApcAssociated) { 00221 KeInsertQueueApc(&ExTimer->TimerApc, 00222 SystemArgument1, 00223 SystemArgument2, 00224 TIMER_APC_INCREMENT); 00225 } 00226 00227 ExReleaseSpinLock(&ExTimer->Lock, OldIrql); 00228 return; 00229 } 00230 00231 static VOID 00232 ExpDeleteTimer ( 00233 IN PVOID Object 00234 ) 00235 00236 /*++ 00237 00238 Routine Description: 00239 00240 This function is the delete routine for timer objects. Its function is 00241 to cancel the timer and free the spin lock associated with a timer. 00242 00243 Arguments: 00244 00245 Object - Supplies a pointer to an executive timer object. 00246 00247 Return Value: 00248 00249 None. 00250 00251 --*/ 00252 00253 { 00254 PETIMER ExTimer; 00255 KIRQL OldIrql; 00256 00257 ExTimer = (PETIMER) Object; 00258 00259 // 00260 // Remove from wake list 00261 // 00262 00263 if (ExTimer->WakeTimerListEntry.Flink) { 00264 ExAcquireSpinLock(&ExpWakeTimerListLock, &OldIrql); 00265 if (ExTimer->WakeTimerListEntry.Flink) { 00266 RemoveEntryList(&ExTimer->WakeTimerListEntry); 00267 ExTimer->WakeTimerListEntry.Flink = NULL; 00268 } 00269 ExReleaseSpinLock(&ExpWakeTimerListLock, OldIrql); 00270 } 00271 00272 // 00273 // Cancel the timer and free the spin lock associated with the timer. 00274 // 00275 00276 KeCancelTimer(&ExTimer->KeTimer); 00277 return; 00278 } 00279 00280 BOOLEAN 00281 ExpTimerInitialization ( 00282 ) 00283 00284 /*++ 00285 00286 Routine Description: 00287 00288 This function creates the timer object type descriptor at system 00289 initialization and stores the address of the object type descriptor 00290 in local static storage. 00291 00292 Arguments: 00293 00294 None. 00295 00296 Return Value: 00297 00298 A value of TRUE is returned if the timer object type descriptor is 00299 successfully initialized. Otherwise a value of FALSE is returned. 00300 00301 --*/ 00302 00303 { 00304 00305 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; 00306 NTSTATUS Status; 00307 UNICODE_STRING TypeName; 00308 00309 KeInitializeSpinLock (&ExpWakeTimerListLock); 00310 InitializeListHead (&ExpWakeTimerList); 00311 00312 // 00313 // Initialize string descriptor. 00314 // 00315 00316 RtlInitUnicodeString(&TypeName, L"Timer"); 00317 00318 // 00319 // Create timer object type descriptor. 00320 // 00321 00322 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); 00323 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); 00324 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; 00325 ObjectTypeInitializer.GenericMapping = ExpTimerMapping; 00326 ObjectTypeInitializer.PoolType = NonPagedPool; 00327 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(ETIMER); 00328 ObjectTypeInitializer.ValidAccessMask = TIMER_ALL_ACCESS; 00329 ObjectTypeInitializer.DeleteProcedure = ExpDeleteTimer; 00330 Status = ObCreateObjectType(&TypeName, 00331 &ObjectTypeInitializer, 00332 (PSECURITY_DESCRIPTOR)NULL, 00333 &ExTimerObjectType); 00334 00335 00336 00337 // 00338 // If the time object type descriptor was successfully created, then 00339 // return a value of TRUE. Otherwise return a value of FALSE. 00340 // 00341 00342 return (BOOLEAN)(NT_SUCCESS(Status)); 00343 } 00344 00345 VOID 00346 ExTimerRundown ( 00347 ) 00348 00349 /*++ 00350 00351 Routine Description: 00352 00353 This function is called when a thread is about to be terminated to 00354 process the active timer list. It is assumed that APC's have been 00355 disabled for the subject thread, thus this code cannot be interrupted 00356 to execute an APC for the current thread. 00357 00358 Arguments: 00359 00360 None. 00361 00362 Return Value: 00363 00364 None. 00365 00366 --*/ 00367 00368 { 00369 00370 BOOLEAN Dereference; 00371 PETHREAD ExThread; 00372 PETIMER ExTimer; 00373 PLIST_ENTRY NextEntry; 00374 KIRQL OldIrql1; 00375 00376 // 00377 // Process each entry in the active timer list. 00378 // 00379 00380 ExThread = PsGetCurrentThread(); 00381 ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql1); 00382 NextEntry = ExThread->ActiveTimerListHead.Flink; 00383 while (NextEntry != &ExThread->ActiveTimerListHead) { 00384 ExTimer = CONTAINING_RECORD(NextEntry, ETIMER, ActiveTimerListEntry); 00385 00386 // 00387 // Increment the reference count on the object so that it cannot be 00388 // deleted, and then drop the active timer list lock. 00389 // 00390 // N. B. The object reference cannot fail and will acquire no mutexes. 00391 // 00392 00393 ObReferenceObject(ExTimer); 00394 ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql1); 00395 00396 // 00397 // Acquire the timer spin lock and reacquire the active time list spin 00398 // lock. If the timer is still in the current thread's active timer 00399 // list, then cancel the timer, remove the timer's DPC from the DPC 00400 // queue, remove the timer's APC from the APC queue, remove the timer 00401 // from the thread's active timer list, and set the associate APC 00402 // flag FALSE. 00403 // 00404 // N. B. The spin locks for the timer and the active timer list must be 00405 // acquired in the order: 1) timer lock, 2) thread list lock. 00406 // 00407 00408 ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1); 00409 ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock); 00410 if ((ExTimer->ApcAssociated) && (&ExThread->Tcb == ExTimer->TimerApc.Thread)) { 00411 RemoveEntryList(&ExTimer->ActiveTimerListEntry); 00412 ExTimer->ApcAssociated = FALSE; 00413 KeCancelTimer(&ExTimer->KeTimer); 00414 KeRemoveQueueDpc(&ExTimer->TimerDpc); 00415 KeRemoveQueueApc(&ExTimer->TimerApc); 00416 Dereference = TRUE; 00417 00418 } else { 00419 Dereference = FALSE; 00420 } 00421 00422 ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock); 00423 ExReleaseSpinLock(&ExTimer->Lock, OldIrql1); 00424 if (Dereference) { 00425 ObDereferenceObject((PVOID)ExTimer); 00426 } 00427 00428 ObDereferenceObject((PVOID)ExTimer); 00429 00430 // 00431 // Raise IRQL to DISPATCH_LEVEL and reacquire active timer list 00432 // spin lock. 00433 // 00434 00435 ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql1); 00436 NextEntry = ExThread->ActiveTimerListHead.Flink; 00437 } 00438 00439 ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql1); 00440 return; 00441 } 00442 00443 NTSTATUS 00444 NtCreateTimer ( 00445 OUT PHANDLE TimerHandle, 00446 IN ACCESS_MASK DesiredAccess, 00447 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, 00448 IN TIMER_TYPE TimerType 00449 ) 00450 00451 /*++ 00452 00453 Routine Description: 00454 00455 This function creates an timer object and opens a handle to the object with 00456 the specified desired access. 00457 00458 Arguments: 00459 00460 TimerHandle - Supplies a pointer to a variable that will receive the 00461 timer object handle. 00462 00463 DesiredAccess - Supplies the desired types of access for the timer object. 00464 00465 ObjectAttributes - Supplies a pointer to an object attributes structure. 00466 00467 TimerType - Supplies the type of the timer (autoclearing or notification). 00468 00469 Return Value: 00470 00471 TBS 00472 00473 --*/ 00474 00475 { 00476 00477 PETIMER ExTimer; 00478 HANDLE Handle; 00479 KPROCESSOR_MODE PreviousMode; 00480 NTSTATUS Status; 00481 00482 // 00483 // Establish an exception handler, probe the output handle address, and 00484 // attempt to create a timer object. If the probe fails, then return the 00485 // exception code as the service status. Otherwise return the status value 00486 // returned by the object insertion routine. 00487 // 00488 00489 try { 00490 00491 // 00492 // Get previous processor mode and probe output handle address if 00493 // necessary. 00494 // 00495 00496 PreviousMode = KeGetPreviousMode(); 00497 if (PreviousMode != KernelMode) { 00498 ProbeForWriteHandle(TimerHandle); 00499 } 00500 00501 // 00502 // Check argument validity. 00503 // 00504 00505 if ((TimerType != NotificationTimer) && 00506 (TimerType != SynchronizationTimer)) { 00507 return STATUS_INVALID_PARAMETER_4; 00508 } 00509 00510 // 00511 // Allocate timer object. 00512 // 00513 00514 Status = ObCreateObject(PreviousMode, 00515 ExTimerObjectType, 00516 ObjectAttributes, 00517 PreviousMode, 00518 NULL, 00519 sizeof(ETIMER), 00520 0, 00521 0, 00522 (PVOID *)&ExTimer); 00523 00524 // 00525 // If the timer object was successfully allocated, then initialize the 00526 // timer object and attempt to insert the time object in the current 00527 // process' handle table. 00528 // 00529 00530 if (NT_SUCCESS(Status)) { 00531 KeInitializeDpc(&ExTimer->TimerDpc, 00532 ExpTimerDpcRoutine, 00533 (PVOID)ExTimer); 00534 00535 KeInitializeTimerEx(&ExTimer->KeTimer, TimerType); 00536 KeInitializeSpinLock(&ExTimer->Lock); 00537 ExTimer->ApcAssociated = FALSE; 00538 ExTimer->WakeTimer = FALSE; 00539 ExTimer->WakeTimerListEntry.Flink = NULL; 00540 Status = ObInsertObject((PVOID)ExTimer, 00541 NULL, 00542 DesiredAccess, 00543 0, 00544 (PVOID *)NULL, 00545 &Handle); 00546 00547 // 00548 // If the timer object was successfully inserted in the current 00549 // process' handle table, then attempt to write the timer object 00550 // handle value. If the write attempt fails, then do not report 00551 // an error. When the caller attempts to access the handle value, 00552 // an access violation will occur. 00553 // 00554 00555 if (NT_SUCCESS(Status)) { 00556 try { 00557 *TimerHandle = Handle; 00558 } except(ExSystemExceptionFilter()) { 00559 } 00560 } 00561 } 00562 00563 // 00564 // If an exception occurs during the probe of the output handle address, 00565 // then always handle the exception and return the exception code as the 00566 // status value. 00567 // 00568 00569 } except(ExSystemExceptionFilter()) { 00570 return GetExceptionCode(); 00571 } 00572 00573 // 00574 // Return service status. 00575 // 00576 00577 return Status; 00578 } 00579 00580 NTSTATUS 00581 NtOpenTimer ( 00582 OUT PHANDLE TimerHandle, 00583 IN ACCESS_MASK DesiredAccess, 00584 IN POBJECT_ATTRIBUTES ObjectAttributes 00585 ) 00586 00587 /*++ 00588 00589 Routine Description: 00590 00591 This function opens a handle to an timer object with the specified 00592 desired access. 00593 00594 Arguments: 00595 00596 TimerHandle - Supplies a pointer to a variable that will receive the 00597 timer object handle. 00598 00599 DesiredAccess - Supplies the desired types of access for the timer object. 00600 00601 ObjectAttributes - Supplies a pointer to an object attributes structure. 00602 00603 Return Value: 00604 00605 TBS 00606 00607 --*/ 00608 00609 { 00610 00611 HANDLE Handle; 00612 KPROCESSOR_MODE PreviousMode; 00613 NTSTATUS Status; 00614 00615 // 00616 // Establish an exception handler, probe the output handle address, and 00617 // attempt to open a timer object. If the probe fails, then return the 00618 // exception code as the service status. Otherwise return the status value 00619 // returned by the open object routine. 00620 // 00621 00622 try { 00623 00624 // 00625 // Get previous processor mode and probe output handle address if 00626 // necessary. 00627 // 00628 00629 PreviousMode = KeGetPreviousMode(); 00630 if (PreviousMode != KernelMode) { 00631 ProbeForWriteHandle(TimerHandle); 00632 } 00633 00634 // 00635 // Open handle to the timer object with the specified desired access. 00636 // 00637 00638 Status = ObOpenObjectByName(ObjectAttributes, 00639 ExTimerObjectType, 00640 PreviousMode, 00641 NULL, 00642 DesiredAccess, 00643 NULL, 00644 &Handle); 00645 00646 // 00647 // If the open was successful, then attempt to write the timer object 00648 // handle value. If the write attempt fails, then do not report an 00649 // error. When the caller attempts to access the handle value, an 00650 // access violation will occur. 00651 // 00652 00653 if (NT_SUCCESS(Status)) { 00654 try { 00655 *TimerHandle = Handle; 00656 00657 } except(ExSystemExceptionFilter()) { 00658 } 00659 } 00660 00661 // 00662 // If an exception occurs during the probe of the output handle address, 00663 // then always handle the exception and return the exception code as the 00664 // status value. 00665 // 00666 00667 } except(ExSystemExceptionFilter()) { 00668 return GetExceptionCode(); 00669 } 00670 00671 // 00672 // Return service status. 00673 // 00674 00675 return Status; 00676 } 00677 00678 NTSTATUS 00679 NtCancelTimer ( 00680 IN HANDLE TimerHandle, 00681 OUT PBOOLEAN CurrentState OPTIONAL 00682 ) 00683 00684 /*++ 00685 00686 Routine Description: 00687 00688 This function cancels a timer object. 00689 00690 Arguments: 00691 00692 TimerHandle - Supplies a handle to an timer object. 00693 00694 CurrentState - Supplies an optional pointer to a variable that will 00695 receive the current state of the timer object. 00696 00697 Return Value: 00698 00699 TBS 00700 00701 --*/ 00702 00703 { 00704 00705 BOOLEAN Dereference; 00706 PETHREAD ExThread; 00707 PETIMER ExTimer; 00708 KIRQL OldIrql1; 00709 KPROCESSOR_MODE PreviousMode; 00710 BOOLEAN State; 00711 NTSTATUS Status; 00712 00713 // 00714 // Establish an exception handler, probe the current state address if 00715 // specified, reference the timer object, and cancel the timer object. 00716 // If the probe fails, then return the exception code as the service 00717 // status. Otherwise return the status value returned by the reference 00718 // object by handle routine. 00719 // 00720 00721 try { 00722 00723 // 00724 // Get previous processor mode and probe current state address if 00725 // necessary. 00726 // 00727 00728 PreviousMode = KeGetPreviousMode(); 00729 if ((PreviousMode != KernelMode) && (ARGUMENT_PRESENT(CurrentState))) { 00730 ProbeForWriteBoolean(CurrentState); 00731 } 00732 00733 // 00734 // Reference timer object by handle. 00735 // 00736 00737 Status = ObReferenceObjectByHandle(TimerHandle, 00738 TIMER_MODIFY_STATE, 00739 ExTimerObjectType, 00740 PreviousMode, 00741 (PVOID *)&ExTimer, 00742 NULL); 00743 00744 // 00745 // If the reference was successful, then cancel the timer object, 00746 // dereference the timer object, and write the current state value 00747 // if specified. If the write attempt fails, then do not report an 00748 // error. When the caller attempts to access the current state value, 00749 // an access violation will occur. 00750 // 00751 00752 if (NT_SUCCESS(Status)) { 00753 ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1); 00754 if (ExTimer->ApcAssociated) { 00755 ExThread = CONTAINING_RECORD(ExTimer->TimerApc.Thread, ETHREAD, Tcb); 00756 ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock); 00757 RemoveEntryList(&ExTimer->ActiveTimerListEntry); 00758 ExTimer->ApcAssociated = FALSE; 00759 ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock); 00760 KeCancelTimer(&ExTimer->KeTimer); 00761 KeRemoveQueueDpc(&ExTimer->TimerDpc); 00762 KeRemoveQueueApc(&ExTimer->TimerApc); 00763 Dereference = TRUE; 00764 00765 } else { 00766 KeCancelTimer(&ExTimer->KeTimer); 00767 Dereference = FALSE; 00768 } 00769 00770 if (ExTimer->WakeTimerListEntry.Flink) { 00771 ExAcquireSpinLockAtDpcLevel(&ExpWakeTimerListLock); 00772 00773 // 00774 // Check again as ExGetNextWakeTime might have removed it. 00775 // 00776 if (ExTimer->WakeTimerListEntry.Flink) { 00777 RemoveEntryList(&ExTimer->WakeTimerListEntry); 00778 ExTimer->WakeTimerListEntry.Flink = NULL; 00779 } 00780 ExReleaseSpinLockFromDpcLevel(&ExpWakeTimerListLock); 00781 } 00782 00783 ExReleaseSpinLock(&ExTimer->Lock, OldIrql1); 00784 if (Dereference) { 00785 ObDereferenceObject((PVOID)ExTimer); 00786 } 00787 00788 // 00789 // Read current state of timer, dereference timer object, and set 00790 // current state. 00791 // 00792 00793 State = KeReadStateTimer(&ExTimer->KeTimer); 00794 ObDereferenceObject(ExTimer); 00795 if (ARGUMENT_PRESENT(CurrentState)) { 00796 try { 00797 *CurrentState = State; 00798 00799 } except(ExSystemExceptionFilter()) { 00800 } 00801 } 00802 } 00803 00804 // 00805 // If an exception occurs during the probe of the current state address, 00806 // then always handle the exception and return the exception code as the 00807 // status value. 00808 // 00809 00810 } except(ExSystemExceptionFilter()) { 00811 return GetExceptionCode(); 00812 } 00813 00814 // 00815 // Return service status. 00816 // 00817 00818 return Status; 00819 } 00820 00821 NTSTATUS 00822 NtQueryTimer ( 00823 IN HANDLE TimerHandle, 00824 IN TIMER_INFORMATION_CLASS TimerInformationClass, 00825 OUT PVOID TimerInformation, 00826 IN ULONG TimerInformationLength, 00827 OUT PULONG ReturnLength OPTIONAL 00828 ) 00829 00830 /*++ 00831 00832 Routine Description: 00833 00834 This function queries the state of an timer object and returns the 00835 requested information in the specified record structure. 00836 00837 Arguments: 00838 00839 TimerHandle - Supplies a handle to an timer object. 00840 00841 TimerInformationClass - Supplies the class of information being requested. 00842 00843 TimerInformation - Supplies a pointer to a record that is to receive the 00844 requested information. 00845 00846 TimerInformationLength - Supplies the length of the record that is to 00847 receive the requested information. 00848 00849 ReturnLength - Supplies an optional pointer to a variable that is to 00850 receive the actual length of information that is returned. 00851 00852 Return Value: 00853 00854 TBS 00855 00856 --*/ 00857 00858 { 00859 00860 PETIMER ExTimer; 00861 PKTIMER KeTimer; 00862 KPROCESSOR_MODE PreviousMode; 00863 BOOLEAN State; 00864 NTSTATUS Status; 00865 LARGE_INTEGER TimeToGo; 00866 00867 // 00868 // Establish an exception handler, probe the output arguments, reference 00869 // the timer object, and return the specified information. If the probe 00870 // fails, then return the exception code as the service status. Otherwise 00871 // return the status value returned by the reference object by handle 00872 // routine. 00873 // 00874 00875 try { 00876 00877 // 00878 // Get previous processor mode and probe output arguments if necessary. 00879 // 00880 00881 PreviousMode = KeGetPreviousMode(); 00882 if (PreviousMode != KernelMode) { 00883 ProbeForWrite(TimerInformation, 00884 sizeof(TIMER_BASIC_INFORMATION), 00885 sizeof(ULONG)); 00886 00887 if (ARGUMENT_PRESENT(ReturnLength)) { 00888 ProbeForWriteUlong(ReturnLength); 00889 } 00890 } 00891 00892 // 00893 // Check argument validity. 00894 // 00895 00896 if (TimerInformationClass != TimerBasicInformation) { 00897 return STATUS_INVALID_INFO_CLASS; 00898 } 00899 00900 if (TimerInformationLength != sizeof(TIMER_BASIC_INFORMATION)) { 00901 return STATUS_INFO_LENGTH_MISMATCH; 00902 } 00903 00904 // 00905 // Reference timer object by handle. 00906 // 00907 00908 Status = ObReferenceObjectByHandle(TimerHandle, 00909 TIMER_QUERY_STATE, 00910 ExTimerObjectType, 00911 PreviousMode, 00912 (PVOID *)&ExTimer, 00913 NULL); 00914 00915 // 00916 // If the reference was successful, then read the current state, 00917 // compute the time remaining, dereference the timer object, fill in 00918 // the information structure, and return the length of the information 00919 // structure if specified. If the write of the time information or the 00920 // return length fails, then do not report an error. When the caller 00921 // accesses the information structure or the length, an violation will 00922 // occur. 00923 // 00924 00925 if (NT_SUCCESS(Status)) { 00926 KeTimer = &ExTimer->KeTimer; 00927 State = KeReadStateTimer(KeTimer); 00928 KiQueryInterruptTime(&TimeToGo); 00929 TimeToGo.QuadPart = KeTimer->DueTime.QuadPart - TimeToGo.QuadPart; 00930 ObDereferenceObject(ExTimer); 00931 try { 00932 ((PTIMER_BASIC_INFORMATION)TimerInformation)->TimerState = State; 00933 ((PTIMER_BASIC_INFORMATION)TimerInformation)->RemainingTime = TimeToGo; 00934 if (ARGUMENT_PRESENT(ReturnLength)) { 00935 *ReturnLength = sizeof(TIMER_BASIC_INFORMATION); 00936 } 00937 00938 } except(ExSystemExceptionFilter()) { 00939 } 00940 } 00941 00942 // 00943 // If an exception occurs during the probe of the current state address, 00944 // then always handle the exception and return the exception code as the 00945 // status value. 00946 // 00947 00948 } except(ExSystemExceptionFilter()) { 00949 return GetExceptionCode(); 00950 } 00951 00952 // 00953 // Return service status. 00954 // 00955 00956 return Status; 00957 } 00958 00959 NTSTATUS 00960 NtSetTimer ( 00961 IN HANDLE TimerHandle, 00962 IN PLARGE_INTEGER DueTime, 00963 IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL, 00964 IN PVOID TimerContext OPTIONAL, 00965 IN BOOLEAN WakeTimer, 00966 IN LONG Period OPTIONAL, 00967 OUT PBOOLEAN PreviousState OPTIONAL 00968 ) 00969 00970 /*++ 00971 00972 Routine Description: 00973 00974 This function sets an timer object to a Not-Signaled state and sets the timer 00975 to expire at the specified time. 00976 00977 Arguments: 00978 00979 TimerHandle - Supplies a handle to an timer object. 00980 00981 DueTime - Supplies a pointer to absolute of relative time at which the 00982 timer is to expire. 00983 00984 TimerApcRoutine - Supplies an optional pointer to a function which is to 00985 be executed when the timer expires. If this parameter is not specified, 00986 then the TimerContext parameter is ignored. 00987 00988 TimerContext - Supplies an optional pointer to an arbitrary data structure 00989 that will be passed to the function specified by the TimerApcRoutine 00990 parameter. This parameter is ignored if the TimerApcRoutine parameter 00991 is not specified. 00992 00993 WakeTimer - Supplies a boolean value that specifies whether the timer 00994 wakes computer operation if sleeping 00995 00996 Period - Supplies an optional repetitive period for the timer. 00997 00998 PreviousState - Supplies an optional pointer to a variable that will 00999 receive the previous state of the timer object. 01000 01001 Return Value: 01002 01003 TBS 01004 01005 --*/ 01006 01007 { 01008 01009 BOOLEAN AssociatedApc; 01010 BOOLEAN Dereference; 01011 PETHREAD ExThread; 01012 PETIMER ExTimer; 01013 LARGE_INTEGER ExpirationTime; 01014 KIRQL OldIrql1; 01015 KPROCESSOR_MODE PreviousMode; 01016 BOOLEAN State; 01017 NTSTATUS Status; 01018 01019 // 01020 // Establish an exception handler, probe the due time and previous state 01021 // address if specified, reference the timer object, and set the timer 01022 // object. If the probe fails, then return the exception code as the 01023 // service status. Otherwise return the status value returned by the 01024 // reference object by handle routine. 01025 // 01026 01027 try { 01028 01029 // 01030 // Get previous processor mode and probe previous state address 01031 // if necessary. 01032 // 01033 01034 PreviousMode = KeGetPreviousMode(); 01035 if (PreviousMode != KernelMode) { 01036 if (ARGUMENT_PRESENT(PreviousState)) { 01037 ProbeForWriteBoolean(PreviousState); 01038 } 01039 01040 ProbeForRead(DueTime, sizeof(LARGE_INTEGER), sizeof(ULONG)); 01041 } 01042 01043 // 01044 // Check argument validity. 01045 // 01046 01047 if (Period < 0) { 01048 return STATUS_INVALID_PARAMETER_6; 01049 } 01050 01051 // 01052 // Capture the expiration time. 01053 // 01054 01055 ExpirationTime = *DueTime; 01056 01057 // 01058 // Reference timer object by handle. 01059 // 01060 01061 Status = ObReferenceObjectByHandle(TimerHandle, 01062 TIMER_MODIFY_STATE, 01063 ExTimerObjectType, 01064 PreviousMode, 01065 (PVOID *)&ExTimer, 01066 NULL); 01067 01068 // 01069 // If this WakeTimer flag is set, return the appropiate informational 01070 // success status code. 01071 // 01072 01073 if (NT_SUCCESS(Status) && WakeTimer && !PoWakeTimerSupported()) { 01074 Status = STATUS_TIMER_RESUME_IGNORED; 01075 } 01076 01077 // 01078 // If the reference was successful, then cancel the timer object, set 01079 // the timer object, dereference time object, and write the previous 01080 // state value if specified. If the write of the previous state value 01081 // fails, then do not report an error. When the caller attempts to 01082 // access the previous state value, an access violation will occur. 01083 // 01084 01085 if (NT_SUCCESS(Status)) { 01086 ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1); 01087 01088 if (ExTimer->ApcAssociated) { 01089 ExThread = CONTAINING_RECORD(ExTimer->TimerApc.Thread, ETHREAD, Tcb); 01090 ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock); 01091 RemoveEntryList(&ExTimer->ActiveTimerListEntry); 01092 ExTimer->ApcAssociated = FALSE; 01093 ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock); 01094 KeCancelTimer(&ExTimer->KeTimer); 01095 KeRemoveQueueDpc(&ExTimer->TimerDpc); 01096 KeRemoveQueueApc(&ExTimer->TimerApc); 01097 Dereference = TRUE; 01098 01099 } else { 01100 KeCancelTimer(&ExTimer->KeTimer); 01101 Dereference = FALSE; 01102 } 01103 01104 // 01105 // Read the current state of the timer. 01106 // 01107 01108 State = KeReadStateTimer(&ExTimer->KeTimer); 01109 01110 // 01111 // If this is a wake timer ensure it's on the wake timer list 01112 // 01113 01114 ExTimer->WakeTimer = WakeTimer; 01115 ExAcquireSpinLockAtDpcLevel(&ExpWakeTimerListLock); 01116 if (WakeTimer) { 01117 if (!ExTimer->WakeTimerListEntry.Flink) { 01118 InsertTailList(&ExpWakeTimerList, &ExTimer->WakeTimerListEntry); 01119 } 01120 } else { 01121 if (ExTimer->WakeTimerListEntry.Flink) { 01122 RemoveEntryList(&ExTimer->WakeTimerListEntry); 01123 ExTimer->WakeTimerListEntry.Flink = NULL; 01124 } 01125 } 01126 ExReleaseSpinLockFromDpcLevel(&ExpWakeTimerListLock); 01127 01128 // 01129 // If an APC routine is specified, then initialize the APC, acquire the 01130 // thread's active time list lock, insert the timer in the thread's 01131 // active timer list, set the timer with an associated DPC, and set the 01132 // associated APC flag TRUE. Otherwise set the timer without an associated 01133 // DPC, and set the associated APC flag FALSE. 01134 // 01135 01136 ExTimer->Period = Period; 01137 if (ARGUMENT_PRESENT(TimerApcRoutine)) { 01138 ExThread = PsGetCurrentThread(); 01139 KeInitializeApc(&ExTimer->TimerApc, 01140 &ExThread->Tcb, 01141 CurrentApcEnvironment, 01142 ExpTimerApcRoutine, 01143 (PKRUNDOWN_ROUTINE)NULL, 01144 (PKNORMAL_ROUTINE)TimerApcRoutine, 01145 PreviousMode, 01146 TimerContext); 01147 01148 ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock); 01149 InsertTailList(&ExThread->ActiveTimerListHead, 01150 &ExTimer->ActiveTimerListEntry); 01151 01152 ExTimer->ApcAssociated = TRUE; 01153 ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock); 01154 KeSetTimerEx(&ExTimer->KeTimer, 01155 ExpirationTime, 01156 Period, 01157 &ExTimer->TimerDpc); 01158 01159 AssociatedApc = TRUE; 01160 01161 } else { 01162 KeSetTimerEx(&ExTimer->KeTimer, 01163 ExpirationTime, 01164 Period, 01165 NULL); 01166 01167 AssociatedApc = FALSE; 01168 } 01169 01170 ExReleaseSpinLock(&ExTimer->Lock, OldIrql1); 01171 01172 // 01173 // Dereference the object as appropriate. 01174 // 01175 01176 if (Dereference) { 01177 ObDereferenceObject((PVOID)ExTimer); 01178 } 01179 01180 if (AssociatedApc == FALSE) { 01181 ObDereferenceObject((PVOID)ExTimer); 01182 } 01183 01184 if (ARGUMENT_PRESENT(PreviousState)) { 01185 try { 01186 *PreviousState = State; 01187 01188 } except(ExSystemExceptionFilter()) { 01189 } 01190 } 01191 } 01192 01193 // 01194 // If an exception occurs during the probe of the current state address, 01195 // then always handle the exception and return the exception code as the 01196 // status value. 01197 // 01198 01199 } except(ExSystemExceptionFilter()) { 01200 return GetExceptionCode(); 01201 } 01202 01203 // 01204 // Return service status. 01205 // 01206 01207 return Status; 01208 } 01209 01210 01211 VOID 01212 ExGetNextWakeTime ( 01213 OUT PULONGLONG DueTime, 01214 OUT PTIME_FIELDS TimeFields, 01215 OUT PVOID *TimerObject 01216 ) 01217 { 01218 PLIST_ENTRY Link; 01219 PETIMER ExTimer; 01220 PETIMER BestTimer; 01221 KIRQL OldIrql; 01222 ULONGLONG TimerDueTime; 01223 ULONGLONG BestDueTime; 01224 ULONGLONG InterruptTime; 01225 LARGE_INTEGER SystemTime; 01226 LARGE_INTEGER CmosTime; 01227 01228 ExAcquireSpinLock(&ExpWakeTimerListLock, &OldIrql); 01229 BestDueTime = 0; 01230 BestTimer = NULL; 01231 Link = ExpWakeTimerList.Flink; 01232 while (Link != &ExpWakeTimerList) { 01233 ExTimer = CONTAINING_RECORD(Link, ETIMER, WakeTimerListEntry); 01234 Link = Link->Flink; 01235 01236 if (ExTimer->WakeTimer) { 01237 01238 TimerDueTime = KeQueryTimerDueTime(&ExTimer->KeTimer); 01239 TimerDueTime = 0 - TimerDueTime; 01240 01241 // 01242 // Is this timers due time closer? 01243 // 01244 01245 if (TimerDueTime > BestDueTime) { 01246 BestDueTime = TimerDueTime; 01247 BestTimer = ExTimer; 01248 } 01249 01250 } else { 01251 01252 // 01253 // Timer is not an active wake timer, remove it 01254 // 01255 01256 RemoveEntryList(&ExTimer->WakeTimerListEntry); 01257 ExTimer->WakeTimerListEntry.Flink = NULL; 01258 } 01259 } 01260 01261 ExReleaseSpinLock(&ExpWakeTimerListLock, OldIrql); 01262 01263 if (BestDueTime) { 01264 // 01265 // Convert time to timefields 01266 // 01267 01268 KeQuerySystemTime (&SystemTime); 01269 InterruptTime = KeQueryInterruptTime (); 01270 BestDueTime = 0 - BestDueTime; 01271 01272 SystemTime.QuadPart += BestDueTime - InterruptTime; 01273 01274 // 01275 // Many system alarms are only good to 1 second resolution. 01276 // Add one sceond to the target time so that the timer is really 01277 // elasped if this is the wake event. 01278 // 01279 01280 SystemTime.QuadPart += 10000000; 01281 01282 ExSystemTimeToLocalTime(&SystemTime,&CmosTime); 01283 RtlTimeToTimeFields(&CmosTime, TimeFields); 01284 } 01285 01286 *DueTime = BestDueTime; 01287 *TimerObject = BestTimer; 01288 }

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