test: timerobj.c Source File

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

# timerobj.c

Go to the documentation of this file.

    00001 /*++
    00002
    00003 Copyright (c) 1989  Microsoft Corporation
    00004
    00005 Module Name:
    00006
    00007     timerobj.c
    00008
    00009 Abstract:
    00010
    00011     This module implements the kernel timer object. Functions are
    00012     provided to initialize, read, set, and cancel timer objects.
    00013
    00014 Author:
    00015
    00016     David N. Cutler (davec) 2-Mar-1989
    00017
    00018 Environment:
    00019
    00020     Kernel mode only.
    00021
    00022 Revision History:
    00023
    00024 --*/
    00025
    00026 #include "ki.h"
    00027
    00028 #ifdef ALLOC_PRAGMA
    00029 #pragma alloc_text(PAGELK, KeQueryTimerDueTime)
    00030 #endif
    00031
    00032 //
    00033 // The following assert macro is used to check that an input timer is
    00034 // really a ktimer and not something else, like deallocated pool.
    00035 //
    00036
    00037 #define ASSERT_TIMER(E) {                                     \
    00038     ASSERT(((E)->Header.Type == TimerNotificationObject) ||   \
    00039            ((E)->Header.Type == TimerSynchronizationObject)); \
    00040 }
    00041
    00042 VOID
    00043 KeInitializeTimer (
    00044     IN PKTIMER Timer
    00045     )
    00046
    00047 /*++
    00048
    00049 Routine Description:
    00050
    00051     This function initializes a kernel timer object.
    00052
    00053 Arguments:
    00054
    00055     Timer - Supplies a pointer to a dispatcher object of type timer.
    00056
    00057 Return Value:
    00058
    00059     None.
    00060
    00061 --*/
    00062
    00063 {
    00064
    00065     //
    00066     // Initialize extended timer object with a type of notification and a
    00067     // period of zero.
    00068     //
    00069
    00070     KeInitializeTimerEx(Timer, NotificationTimer);
    00071     return;
    00072 }
    00073
    00074 VOID
    00075 KeInitializeTimerEx (
    00076     IN PKTIMER Timer,
    00077     IN TIMER_TYPE Type
    00078     )
    00079
    00080 /*++
    00081
    00082 Routine Description:
    00083
    00084     This function initializes an extended kernel timer object.
    00085
    00086 Arguments:
    00087
    00088     Timer - Supplies a pointer to a dispatcher object of type timer.
    00089
    00090     Type - Supplies the type of timer object; NotificationTimer or
    00091         SynchronizationTimer;
    00092
    00093 Return Value:
    00094
    00095     None.
    00096
    00097 --*/
    00098
    00099 {
    00100     //
    00101     // Initialize standard dispatcher object header and set initial
    00102     // state of timer.
    00103     //
    00104
    00105     Timer->Header.Type = TimerNotificationObject + Type;
    00106     Timer->Header.Inserted = FALSE;
    00107     Timer->Header.Size = sizeof(KTIMER) / sizeof(LONG);
    00108     Timer->Header.SignalState = FALSE;
    00109
    00110 #if DBG
    00111
    00112     Timer->TimerListEntry.Flink = NULL;
    00113     Timer->TimerListEntry.Blink = NULL;
    00114
    00115 #endif
    00116
    00117     InitializeListHead(&Timer->Header.WaitListHead);
    00118     Timer->DueTime.QuadPart = 0;
    00119     Timer->Period = 0;
    00120     return;
    00121 }
    00122
    00123 VOID
    00124 KeClearTimer (
    00125     IN PKTIMER Timer
    00126     )
    00127
    00128 /*++
    00129
    00130 Routine Description:
    00131
    00132     This function clears the signal state of an timer object.
    00133
    00134 Arguments:
    00135
    00136     Event - Supplies a pointer to a dispatcher object of type timer.
    00137
    00138 Return Value:
    00139
    00140     None.
    00141
    00142 --*/
    00143
    00144 {
    00145
    00146     ASSERT_TIMER(Timer);
    00147
    00148     //
    00149     // Clear signal state of timer object.
    00150     //
    00151
    00152     Timer->Header.SignalState = 0;
    00153     return;
    00154 }
    00155
    00156 BOOLEAN
    00157 KeCancelTimer (
    00158     IN PKTIMER Timer
    00159     )
    00160
    00161 /*++
    00162
    00163 Routine Description:
    00164
    00165     This function cancels a timer that was previously set to expire at
    00166     a specified time. If the timer is not currently set, then no operation
    00167     is performed. Canceling a timer does not set the state of the timer to
    00168     Signaled.
    00169
    00170 Arguments:
    00171
    00172     Timer - Supplies a pointer to a dispatcher object of type timer.
    00173
    00174 Return Value:
    00175
    00176     A boolean value of TRUE is returned if the the specified timer was
    00177     currently set. Else a value of FALSE is returned.
    00178
    00179 --*/
    00180
    00181 {
    00182
    00183     BOOLEAN Inserted;
    00184     KIRQL OldIrql;
    00185
    00186     ASSERT_TIMER(Timer);
    00187     ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
    00188
    00189     //
    00190     // Raise IRQL to dispatcher level, lock the dispatcher database, and
    00191     // capture the timer inserted status. If the timer is currently set,
    00192     // then remove it from the timer list.
    00193     //
    00194
    00195     KiLockDispatcherDatabase(&OldIrql);
    00196     Inserted = Timer->Header.Inserted;
    00197     if (Inserted != FALSE) {
    00198         KiRemoveTreeTimer(Timer);
    00199     }
    00200
    00201     //
    00202     // Unlock the dispatcher database, lower IRQL to its previous value, and
    00203     // return boolean value that signifies whether the timer was set of not.
    00204     //
    00205
    00206     KiUnlockDispatcherDatabase(OldIrql);
    00207     return Inserted;
    00208 }
    00209
    00210 BOOLEAN
    00211 KeReadStateTimer (
    00212     IN PKTIMER Timer
    00213     )
    00214
    00215 /*++
    00216
    00217 Routine Description:
    00218
    00219     This function reads the current signal state of a timer object.
    00220
    00221 Arguments:
    00222
    00223     Timer - Supplies a pointer to a dispatcher object of type timer.
    00224
    00225 Return Value:
    00226
    00227     The current signal state of the timer object.
    00228
    00229 --*/
    00230
    00231 {
    00232
    00233     ASSERT_TIMER(Timer);
    00234
    00235     //
    00236     // Return current signal state of timer object.
    00237     //
    00238
    00239     return (BOOLEAN)Timer->Header.SignalState;
    00240 }
    00241
    00242 BOOLEAN
    00243 KeSetTimer (
    00244     IN PKTIMER Timer,
    00245     IN LARGE_INTEGER DueTime,
    00246     IN PKDPC Dpc OPTIONAL
    00247     )
    00248
    00249 /*++
    00250
    00251 Routine Description:
    00252
    00253     This function sets a timer to expire at a specified time. If the timer is
    00254     already set, then it is implicitly canceled before it is set to expire at
    00255     the specified time. Setting a timer causes its due time to be computed,
    00256     its state to be set to Not-Signaled, and the timer object itself to be
    00257     inserted in the timer list.
    00258
    00259 Arguments:
    00260
    00261     Timer - Supplies a pointer to a dispatcher object of type timer.
    00262
    00263     DueTime - Supplies an absolute or relative time at which the timer
    00264         is to expire.
    00265
    00266     Dpc - Supplies an optional pointer to a control object of type DPC.
    00267
    00268 Return Value:
    00269
    00270     A boolean value of TRUE is returned if the the specified timer was
    00271     currently set. Else a value of FALSE is returned.
    00272
    00273 --*/
    00274
    00275 {
    00276
    00277     //
    00278     // Set the timer with a period of zero.
    00279     //
    00280
    00281     return KeSetTimerEx(Timer, DueTime, 0, Dpc);
    00282 }
    00283
    00284 BOOLEAN
    00285 KeSetTimerEx (
    00286     IN PKTIMER Timer,
    00287     IN LARGE_INTEGER DueTime,
    00288     IN LONG Period OPTIONAL,
    00289     IN PKDPC Dpc OPTIONAL
    00290     )
    00291
    00292 /*++
    00293
    00294 Routine Description:
    00295
    00296     This function sets a timer to expire at a specified time. If the timer is
    00297     already set, then it is implicitly canceled before it is set to expire at
    00298     the specified time. Setting a timer causes its due time to be computed,
    00299     its state to be set to Not-Signaled, and the timer object itself to be
    00300     inserted in the timer list.
    00301
    00302 Arguments:
    00303
    00304     Timer - Supplies a pointer to a dispatcher object of type timer.
    00305
    00306     DueTime - Supplies an absolute or relative time at which the timer
    00307         is to expire.
    00308
    00309     Period - Supplies an optional period for the timer in milliseconds.
    00310
    00311     Dpc - Supplies an optional pointer to a control object of type DPC.
    00312
    00313 Return Value:
    00314
    00315     A boolean value of TRUE is returned if the the specified timer was
    00316     currently set. Else a value of FALSE is returned.
    00317
    00318 --*/
    00319
    00320 {
    00321
    00322     BOOLEAN Inserted;
    00323     LARGE_INTEGER Interval;
    00324     KIRQL OldIrql;
    00325     LARGE_INTEGER SystemTime;
    00326
    00327     ASSERT_TIMER(Timer);
    00328     ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
    00329
    00330     //
    00331     // Raise IRQL to dispatcher level and lock dispatcher database.
    00332     //
    00333
    00334     KiLockDispatcherDatabase(&OldIrql);
    00335
    00336     //
    00337     // Capture the timer inserted status and if the timer is currently
    00338     // set, then remove it from the timer list.
    00339     //
    00340
    00341     Inserted = Timer->Header.Inserted;
    00342     if (Inserted != FALSE) {
    00343         KiRemoveTreeTimer(Timer);
    00344     }
    00345
    00346     //
    00347     // Clear the signal state, set the period, set the DPC address, and
    00348     // insert the timer in the timer tree. If the timer is not inserted
    00349     // in the timer tree, then it has already expired and as many waiters
    00350     // as possible should be continued, and a DPC, if specified should be
    00351     // queued.
    00352     //
    00353     // N.B. The signal state must be cleared in case the period is not
    00354     //      zero.
    00355     //
    00356
    00357     Timer->Header.SignalState = FALSE;
    00358     Timer->Dpc = Dpc;
    00359     Timer->Period = Period;
    00360     if (KiInsertTreeTimer((PRKTIMER)Timer, DueTime) == FALSE) {
    00361         if (IsListEmpty(&Timer->Header.WaitListHead) == FALSE) {
    00362             KiWaitTest(Timer, TIMER_EXPIRE_INCREMENT);
    00363         }
    00364
    00365         //
    00366         // If a DPC is specfied, then call the DPC routine.
    00367         //
    00368
    00369         if (Dpc != NULL) {
    00370             KiQuerySystemTime(&SystemTime);
    00371             KeInsertQueueDpc(Timer->Dpc,
    00372                              ULongToPtr(SystemTime.LowPart),
    00373                              ULongToPtr(SystemTime.HighPart));
    00374         }
    00375
    00376         //
    00377         // If the timer is periodic, then compute the next interval time
    00378         // and reinsert the timer in the timer tree.
    00379         //
    00380         // N.B. Even though the timer insertion is relative, it can still
    00381         //      fail if the period of the timer elapses in between computing
    00382         //      the time and inserting the timer in the table. If this happens,
    00383         //      try again.
    00384         //
    00385
    00386         if (Period != 0) {
    00387             Interval.QuadPart = Int32x32To64(Timer->Period, - 10 * 1000);
    00388             while (!KiInsertTreeTimer(Timer, Interval)) {
    00389                 ;
    00390             }
    00391         }
    00392     }
    00393
    00394     //
    00395     // Unlock the dispatcher database and lower IRQL to its previous
    00396     // value.
    00397     //
    00398
    00399     KiUnlockDispatcherDatabase(OldIrql);
    00400
    00401     //
    00402     // Return boolean value that signifies whether the timer was set of
    00403     // not.
    00404     //
    00405
    00406     return Inserted;
    00407 }
    00408
    00409 ULONGLONG
    00410 KeQueryTimerDueTime (
    00411     IN PKTIMER Timer
    00412     )
    00413
    00414 /*++
    00415
    00416 Routine Description:
    00417
    00418     This function returns the InterruptTime at which the timer is
    00419     pending.   0 is returned if the timer is not pending.
    00420
    00421     N.B. This function may only be called by the system sleep code.
    00422
    00423 Arguments:
    00424
    00425     Timer - Supplies a pointer to a dispatcher object of type timer.
    00426
    00427 Return Value:
    00428
    00429     Returns the amount of time remaining on the timer, or 0 if the
    00430     timer is not pending.
    00431
    00432 --*/
    00433
    00434 {
    00435
    00436     KIRQL OldIrql;
    00437     LARGE_INTEGER InterruptTime;
    00438     ULONGLONG DueTime;
    00439
    00440     ASSERT_TIMER(Timer);
    00441
    00442     //
    00443     // Raise IRQL to dispatcher level and lock dispatcher database.
    00444     //
    00445
    00446     KiLockDispatcherDatabase(&OldIrql);
    00447
    00448     //
    00449     // If the timer is currently pending, compute its due time
    00450     //
    00451
    00452     DueTime = 0;
    00453     if (Timer->Header.Inserted) {
    00454         DueTime = Timer->DueTime.QuadPart;
    00455     }
    00456
    00457     //
    00458     // Unlock the dispatcher database and lower IRQL to its previous
    00459     // value, and return the due time
    00460     //
    00461
    00462     KiUnlockDispatcherDatabase(OldIrql);
    00463     return DueTime;
    00464 }
    00465
    00466 PVOID
    00467 KeCheckForTimer(
    00468     IN PVOID BlockStart,
    00469     IN ULONG BlockSize
    00470     )
    00471 /*++
    00472
    00473 Routine Description:
    00474
    00475     This function is used for debugging by checking all timers
    00476     to see if any is in the memory block passed.  If so, the
    00477     system stops at a debug breakpoint.
    00478
    00479 Arguments:
    00480
    00481     MemoryBlock - Base address to check for timer
    00482
    00483     BlockSize - Size (in bytes) to check in memory block
    00484
    00485 Return Value:
    00486
    00487     The address of the currently active timer.
    00488
    00489 --*/
    00490 {
    00491     ULONG Index;
    00492     PLIST_ENTRY ListHead;
    00493     PLIST_ENTRY NextEntry;
    00494     KIRQL OldIrql;
    00495     PKTIMER Timer;
    00496     PUCHAR Address;
    00497     PUCHAR Start;
    00498     PUCHAR End;
    00499
    00500     //
    00501     // Compute the ending memory location.
    00502     //
    00503
    00504     Start = (PUCHAR)BlockStart;
    00505     End = Start + BlockSize;
    00506
    00507     //
    00508     // Raise IRQL to dispatcher level and lock dispatcher database.
    00509     //
    00510
    00511     KiLockDispatcherDatabase(&OldIrql);
    00512
    00513     //
    00514     // Run the entire timer database and check for any timers in
    00515     // the memory block
    00516     //
    00517
    00518     Index = 0;
    00519     do {
    00520         ListHead = &KiTimerTableListHead[Index];
    00521         NextEntry = ListHead->Flink;
    00522         while (NextEntry != ListHead) {
    00523             Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
    00524             Address = (PUCHAR)Timer;
    00525             NextEntry = NextEntry->Flink;
    00526
    00527             //
    00528             // Check this timer object is not in the range.
    00529             // In each of the following, we check that the object
    00530             // does not overlap the range, for example, if the timer
    00531             // object (in this first check), starts one dword before
    00532             // the range being checked, we have an overlap and should
    00533             // stop.
    00534             //
    00535
    00536             if ((Address > (Start - sizeof(KTIMER))) &&
    00537                 (Address < End)) {
    00538                 KeBugCheckEx(TIMER_OR_DPC_INVALID,
    00539                              0x0,
    00540                              (ULONG_PTR)Address,
    00541                              (ULONG_PTR)Start,
    00542                              (ULONG_PTR)End);
    00543             }
    00544
    00545             if (Timer->Dpc) {
    00546
    00547                 //
    00548                 // Check the timer's DPC object isn't in the range.
    00549                 //
    00550
    00551                 Address = (PUCHAR)Timer->Dpc;
    00552                 if ((Address > (Start - sizeof(KDPC))) &&
    00553                     (Address < End)) {
    00554                     KeBugCheckEx(TIMER_OR_DPC_INVALID,
    00555                                  0x1,
    00556                                  (ULONG_PTR)Address,
    00557                                  (ULONG_PTR)Start,
    00558                                  (ULONG_PTR)End);
    00559                 }
    00560
    00561                 //
    00562                 // Check the timer's DPC routine is not in the range.
    00563                 //
    00564
    00565                 Address = (PUCHAR)Timer->Dpc->DeferredRoutine;
    00566                 if (Address >= Start && Address < End) {
    00567                     KeBugCheckEx(TIMER_OR_DPC_INVALID,
    00568                                  0x2,
    00569                                  (ULONG_PTR)Address,
    00570                                  (ULONG_PTR)Start,
    00571                                  (ULONG_PTR)End);
    00572                 }
    00573             }
    00574         }
    00575
    00576         Index += 1;
    00577     } while(Index < TIMER_TABLE_SIZE);
    00578
    00579
    00580     //
    00581     // Unlock the dispatcher database and lower IRQL to its previous value
    00582     //
    00583
    00584     KiUnlockDispatcherDatabase(OldIrql);
    00585     return NULL;
    00586 }