1 /* $Id: timer.c,v 1.61 2003/07/21 21:53:51 royce Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/timer.c
6 * PURPOSE: Handle timers
7 * PROGRAMMER: David Welch (welch@mcmail.com)
10 * 12/3/99: Phillip Susi: enabled the timers, fixed spin lock
13 /* NOTES ******************************************************************/
15 * System time units are 100-nanosecond intervals
18 /* INCLUDES ***************************************************************/
21 #include <ddk/ntddk.h>
22 #include <internal/ke.h>
23 #include <internal/id.h>
24 #include <internal/ps.h>
27 #include <internal/debug.h>
29 /* TYPES *****************************************************************/
33 /* GLOBALS ****************************************************************/
38 static LARGE_INTEGER boot_time
= (LARGE_INTEGER
)0LL;
39 static LARGE_INTEGER system_time
= (LARGE_INTEGER
)0LL;
42 * Number of timer interrupts since initialisation
44 volatile ULONGLONG KeTickCount
= 0;
45 volatile ULONG KiRawTicks
= 0;
48 * The increment in the system clock every timer tick (in system time units)
54 #define CLOCK_INCREMENT (100000)
57 * PURPOSE: List of timers
59 static LIST_ENTRY TimerListHead
;
60 static KSPIN_LOCK TimerListLock
;
61 static KSPIN_LOCK TimerValueLock
;
62 static KDPC ExpireTimerDpc
;
64 /* must raise IRQL to PROFILE_LEVEL and grab spin lock there, to sync with ISR */
66 extern ULONG PiNrRunnableThreads
;
68 #define MICROSECONDS_PER_TICK (10000)
69 #define TICKS_TO_CALIBRATE (1)
70 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
71 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
73 static BOOLEAN TimerInitDone
= FALSE
;
75 /* FUNCTIONS **************************************************************/
79 NtQueryTimerResolution(OUT PULONG MinimumResolution
,
80 OUT PULONG MaximumResolution
,
81 OUT PULONG ActualResolution
)
88 NtSetTimerResolution(IN ULONG RequestedResolution
,
90 OUT PULONG ActualResolution
)
97 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter
,
98 IN PLARGE_INTEGER Frequency
)
100 LARGE_INTEGER PerfCounter
;
101 LARGE_INTEGER PerfFrequency
;
103 PerfCounter
= KeQueryPerformanceCounter(&PerfFrequency
);
106 Counter
->QuadPart
= PerfCounter
.QuadPart
;
108 if (Frequency
!= NULL
)
109 Frequency
->QuadPart
= PerfFrequency
.QuadPart
;
111 return(STATUS_SUCCESS
);
116 NtDelayExecution(IN ULONG Alertable
,
120 LARGE_INTEGER Timeout
;
122 Timeout
= *((PLARGE_INTEGER
)Interval
);
123 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
124 Alertable
, Internal
, Timeout
);
126 DPRINT("Execution delay is %d/%d\n",
127 Timeout
.u
.HighPart
, Timeout
.u
.LowPart
);
128 Status
= KeDelayExecutionThread(UserMode
, Alertable
, &Timeout
);
137 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode
,
139 PLARGE_INTEGER Interval
)
141 * FUNCTION: Puts the current thread into an alertable or nonalertable
142 * wait state for a given internal
144 * WaitMode = Processor mode in which the caller is waiting
145 * Altertable = Specifies if the wait is alertable
146 * Interval = Specifies the interval to wait
150 PKTHREAD Thread
= KeGetCurrentThread();
152 KeInitializeTimer(&Thread
->Timer
);
153 KeSetTimer(&Thread
->Timer
, *Interval
, NULL
);
154 return (KeWaitForSingleObject(&Thread
->Timer
,
166 KeQueryTimeIncrement(VOID
)
168 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
169 * the system clock every time the clock interrupts
170 * RETURNS: The increment
173 return(CLOCK_INCREMENT
);
181 KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
183 * FUNCTION: Gets the current system time
185 * CurrentTime (OUT) = The routine stores the current time here
186 * NOTE: The time is the number of 100-nanosecond intervals since the
187 * 1st of January, 1601.
192 KeRaiseIrql(PROFILE_LEVEL
, &oldIrql
);
193 KeAcquireSpinLockAtDpcLevel(&TimerValueLock
);
194 *CurrentTime
= system_time
;
195 KeReleaseSpinLock(&TimerValueLock
, oldIrql
);
200 NtGetTickCount (PULONG UpTime
)
202 LARGE_INTEGER TickCount
;
204 return(STATUS_INVALID_PARAMETER
);
205 KeQueryTickCount(&TickCount
);
206 *UpTime
= TickCount
.u
.LowPart
;
207 return (STATUS_SUCCESS
);
215 KeSetTimer (PKTIMER Timer
,
216 LARGE_INTEGER DueTime
,
219 * FUNCTION: Sets the absolute or relative interval at which a timer object
220 * is to be set to the signaled state and optionally supplies a
221 * CustomTimerDpc to be executed when the timer expires.
223 * Timer = Points to a previously initialized timer object
224 * DueTimer = If positive then absolute time to expire at
225 * If negative then the relative time to expire at
226 * Dpc = If non-NULL then a dpc to be called when the timer expires
227 * RETURNS: True if the timer was already in the system timer queue
231 return(KeSetTimerEx(Timer
, DueTime
, 0, Dpc
));
238 KeSetTimerEx (PKTIMER Timer
,
239 LARGE_INTEGER DueTime
,
243 * FUNCTION: Sets the absolute or relative interval at which a timer object
244 * is to be set to the signaled state and optionally supplies a
245 * CustomTimerDpc to be executed when the timer expires.
247 * Timer = Points to a previously initialized timer object
248 * DueTimer = If positive then absolute time to expire at
249 * If negative then the relative time to expire at
250 * Dpc = If non-NULL then a dpc to be called when the timer expires
251 * RETURNS: True if the timer was already in the system timer queue
256 LARGE_INTEGER SystemTime
;
258 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer
);
260 KeRaiseIrql(PROFILE_LEVEL
, &oldlvl
);
261 KeAcquireSpinLockAtDpcLevel(&TimerValueLock
);
263 SystemTime
= system_time
;
265 KeReleaseSpinLock(&TimerValueLock
, DISPATCH_LEVEL
);
266 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
269 if (DueTime
.QuadPart
< 0)
272 Timer
->DueTime
.QuadPart
= SystemTime
.QuadPart
- DueTime
.QuadPart
;
276 Timer
->DueTime
.QuadPart
= DueTime
.QuadPart
;
278 Timer
->Period
= Period
;
279 Timer
->Header
.SignalState
= FALSE
;
280 if (Timer
->TimerListEntry
.Flink
!= NULL
)
282 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
285 InsertTailList(&TimerListHead
,&Timer
->TimerListEntry
);
286 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
295 KeCancelTimer (PKTIMER Timer
)
297 * FUNCTION: Removes a timer from the system timer list
299 * Timer = timer to cancel
300 * RETURNS: True if the timer was running
306 DPRINT("KeCancelTimer(Timer %x)\n",Timer
);
308 KeAcquireSpinLock(&TimerListLock
, &oldlvl
);
310 if (Timer
->TimerListEntry
.Flink
== NULL
)
312 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
315 RemoveEntryList(&Timer
->TimerListEntry
);
316 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
317 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
326 KeReadStateTimer (PKTIMER Timer
)
328 return(Timer
->Header
.SignalState
);
335 KeInitializeTimer (PKTIMER Timer
)
337 * FUNCTION: Initalizes a kernel timer object
339 * Timer = caller supplied storage for the timer
340 * NOTE: This function initializes a notification timer
343 KeInitializeTimerEx(Timer
, NotificationTimer
);
350 KeInitializeTimerEx (PKTIMER Timer
,
353 * FUNCTION: Initializes a kernel timer object
355 * Timer = caller supplied storage for the timer
356 * Type = the type of timer (notification or synchronization)
357 * NOTE: When a notification type expires all waiting threads are released
358 * and the timer remains signalled until it is explicitly reset. When a
359 * syncrhonization timer expires its state is set to signalled until a
360 * single waiting thread is released and then the timer is reset.
365 if (Type
== NotificationTimer
)
367 IType
= InternalNotificationTimer
;
369 else if (Type
== SynchronizationTimer
)
371 IType
= InternalSynchronizationTimer
;
379 KeInitializeDispatcherHeader(&Timer
->Header
,
381 sizeof(KTIMER
) / sizeof(ULONG
),
383 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
390 KeQueryTickCount(PLARGE_INTEGER TickCount
)
392 * FUNCTION: Returns the number of ticks since the system was booted
394 * TickCount (OUT) = Points to storage for the number of ticks
397 TickCount
->QuadPart
= KeTickCount
;
401 HandleExpiredTimer(PKTIMER current
)
403 DPRINT("HandleExpiredTime(current %x)\n",current
);
404 if (current
->Dpc
!= NULL
)
406 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
407 current
->Dpc
, current
->Dpc
->DeferredRoutine
);
408 KeInsertQueueDpc(current
->Dpc
,
411 DPRINT("Finished dpc routine\n");
413 KeAcquireDispatcherDatabaseLock(FALSE
);
414 current
->Header
.SignalState
= TRUE
;
415 KeDispatcherObjectWake(¤t
->Header
);
416 KeReleaseDispatcherDatabaseLock(FALSE
);
417 if (current
->Period
!= 0)
419 current
->DueTime
.QuadPart
+=
420 current
->Period
* SYSTEM_TIME_UNITS_PER_MSEC
;
424 RemoveEntryList(¤t
->TimerListEntry
);
425 current
->TimerListEntry
.Flink
= current
->TimerListEntry
.Blink
= NULL
;
430 KeExpireTimers(PKDPC Dpc
,
435 PLIST_ENTRY current_entry
= NULL
;
436 PKTIMER current
= NULL
;
437 ULONG Eip
= (ULONG
)Arg1
;
439 LARGE_INTEGER SystemTime
;
441 DPRINT("KeExpireTimers()\n");
443 KeRaiseIrql(PROFILE_LEVEL
, &oldIrql
);
444 KeAcquireSpinLockAtDpcLevel(&TimerValueLock
);
446 SystemTime
= system_time
;
448 KeReleaseSpinLock(&TimerValueLock
, oldIrql
);
449 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
451 if (KeGetCurrentIrql() > DISPATCH_LEVEL
)
453 DPRINT1("-----------------------------\n");
458 current_entry
= TimerListHead
.Flink
;
460 while (current_entry
!= &TimerListHead
)
462 current
= CONTAINING_RECORD(current_entry
, KTIMER
, TimerListEntry
);
464 current_entry
= current_entry
->Flink
;
465 if ((ULONGLONG
) SystemTime
.QuadPart
>= current
->DueTime
.QuadPart
)
467 HandleExpiredTimer(current
);
471 KiAddProfileEvent(ProfileTime
, Eip
);
473 KeReleaseSpinLockFromDpcLevel(&TimerListLock
);
478 KiUpdateSystemTime(KIRQL oldIrql
,
481 * FUNCTION: Handles a timer interrupt
485 assert(KeGetCurrentIrql() == PROFILE_LEVEL
);
489 if (TimerInitDone
== FALSE
)
494 * Increment the number of timers ticks
497 SharedUserData
->TickCountLow
++;
499 KeAcquireSpinLockAtDpcLevel(&TimerValueLock
);
500 system_time
.QuadPart
+= CLOCK_INCREMENT
;
501 KeReleaseSpinLockFromDpcLevel(&TimerValueLock
);
504 * Queue a DPC that will expire timers
506 KeInsertQueueDpc(&ExpireTimerDpc
, (PVOID
)Eip
, 0);
511 KeInitializeTimerImpl(VOID
)
513 * FUNCTION: Initializes timer irq handling
514 * NOTE: This is only called once from main()
517 TIME_FIELDS TimeFields
;
518 LARGE_INTEGER SystemBootTime
;
520 DPRINT("KeInitializeTimerImpl()\n");
521 InitializeListHead(&TimerListHead
);
522 KeInitializeSpinLock(&TimerListLock
);
523 KeInitializeSpinLock(&TimerValueLock
);
524 KeInitializeDpc(&ExpireTimerDpc
, KeExpireTimers
, 0);
525 TimerInitDone
= TRUE
;
527 * Calculate the starting time for the system clock
529 HalQueryRealTimeClock(&TimeFields
);
530 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
531 boot_time
=SystemBootTime
;
532 system_time
=boot_time
;
534 SharedUserData
->TickCountLow
= 0;
535 SharedUserData
->TickCountMultiplier
= 167783691; // 2^24 * 1193182 / 119310
537 DPRINT("Finished KeInitializeTimerImpl()\n");