1 /* $Id: timer.c,v 1.55 2003/01/02 23:37:28 chorns 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 HIGH_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
)
105 NtDelayExecution(IN ULONG Alertable
,
109 LARGE_INTEGER Timeout
;
111 Timeout
= *((PLARGE_INTEGER
)Interval
);
112 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
113 Alertable
, Internal
, Timeout
);
115 DPRINT("Execution delay is %d/%d\n",
116 Timeout
.u
.HighPart
, Timeout
.u
.LowPart
);
117 Status
= KeDelayExecutionThread(UserMode
, Alertable
, &Timeout
);
123 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode
,
125 PLARGE_INTEGER Interval
)
127 * FUNCTION: Puts the current thread into an alertable or nonalertable
128 * wait state for a given internal
130 * WaitMode = Processor mode in which the caller is waiting
131 * Altertable = Specifies if the wait is alertable
132 * Interval = Specifies the interval to wait
136 PKTHREAD Thread
= KeGetCurrentThread();
138 KeInitializeTimer(&Thread
->Timer
);
139 KeSetTimer(&Thread
->Timer
, *Interval
, NULL
);
140 return (KeWaitForSingleObject(&Thread
->Timer
,
149 KeQueryTimeIncrement(VOID
)
151 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
152 * the system clock every time the clock interrupts
153 * RETURNS: The increment
156 return(CLOCK_INCREMENT
);
161 KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
163 * FUNCTION: Gets the current system time
165 * CurrentTime (OUT) = The routine stores the current time here
166 * NOTE: The time is the number of 100-nanosecond intervals since the
167 * 1st of January, 1601.
172 KeRaiseIrql(PROFILE_LEVEL
, &oldIrql
);
173 KeAcquireSpinLockAtDpcLevel(&TimerValueLock
);
174 *CurrentTime
= system_time
;
175 KeReleaseSpinLock(&TimerValueLock
, oldIrql
);
180 NtGetTickCount (PULONG UpTime
)
182 LARGE_INTEGER TickCount
;
184 return(STATUS_INVALID_PARAMETER
);
185 KeQueryTickCount(&TickCount
);
186 *UpTime
= TickCount
.u
.LowPart
;
187 return (STATUS_SUCCESS
);
192 KeSetTimer (PKTIMER Timer
,
193 LARGE_INTEGER DueTime
,
196 * FUNCTION: Sets the absolute or relative interval at which a timer object
197 * is to be set to the signaled state and optionally supplies a
198 * CustomTimerDpc to be executed when the timer expires.
200 * Timer = Points to a previously initialized timer object
201 * DueTimer = If positive then absolute time to expire at
202 * If negative then the relative time to expire at
203 * Dpc = If non-NULL then a dpc to be called when the timer expires
204 * RETURNS: True if the timer was already in the system timer queue
208 return(KeSetTimerEx(Timer
, DueTime
, 0, Dpc
));
212 KeSetTimerEx (PKTIMER Timer
,
213 LARGE_INTEGER DueTime
,
217 * FUNCTION: Sets the absolute or relative interval at which a timer object
218 * is to be set to the signaled state and optionally supplies a
219 * CustomTimerDpc to be executed when the timer expires.
221 * Timer = Points to a previously initialized timer object
222 * DueTimer = If positive then absolute time to expire at
223 * If negative then the relative time to expire at
224 * Dpc = If non-NULL then a dpc to be called when the timer expires
225 * RETURNS: True if the timer was already in the system timer queue
230 LARGE_INTEGER SystemTime
;
232 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer
);
234 KeRaiseIrql(PROFILE_LEVEL
, &oldlvl
);
235 KeAcquireSpinLockAtDpcLevel(&TimerValueLock
);
237 SystemTime
= system_time
;
239 KeReleaseSpinLock(&TimerValueLock
, DISPATCH_LEVEL
);
240 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
243 if (DueTime
.QuadPart
< 0)
246 Timer
->DueTime
.QuadPart
= SystemTime
.QuadPart
- DueTime
.QuadPart
;
250 Timer
->DueTime
.QuadPart
= DueTime
.QuadPart
;
252 Timer
->Period
= Period
;
253 Timer
->Header
.SignalState
= FALSE
;
254 if (Timer
->TimerListEntry
.Flink
!= NULL
)
256 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
259 InsertTailList(&TimerListHead
,&Timer
->TimerListEntry
);
260 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
266 KeCancelTimer (PKTIMER Timer
)
268 * FUNCTION: Removes a timer from the system timer list
270 * Timer = timer to cancel
271 * RETURNS: True if the timer was running
277 DPRINT("KeCancelTimer(Timer %x)\n",Timer
);
279 KeAcquireSpinLock(&TimerListLock
, &oldlvl
);
281 if (Timer
->TimerListEntry
.Flink
== NULL
)
283 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
286 RemoveEntryList(&Timer
->TimerListEntry
);
287 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
288 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
294 KeReadStateTimer (PKTIMER Timer
)
296 return(Timer
->Header
.SignalState
);
300 KeInitializeTimer (PKTIMER Timer
)
302 * FUNCTION: Initalizes a kernel timer object
304 * Timer = caller supplied storage for the timer
305 * NOTE: This function initializes a notification timer
308 KeInitializeTimerEx(Timer
, NotificationTimer
);
312 KeInitializeTimerEx (PKTIMER Timer
,
315 * FUNCTION: Initializes a kernel timer object
317 * Timer = caller supplied storage for the timer
318 * Type = the type of timer (notification or synchronization)
319 * NOTE: When a notification type expires all waiting threads are released
320 * and the timer remains signalled until it is explicitly reset. When a
321 * syncrhonization timer expires its state is set to signalled until a
322 * single waiting thread is released and then the timer is reset.
327 if (Type
== NotificationTimer
)
329 IType
= InternalNotificationTimer
;
331 else if (Type
== SynchronizationTimer
)
333 IType
= InternalSynchronizationTimer
;
341 KeInitializeDispatcherHeader(&Timer
->Header
,
343 sizeof(KTIMER
) / sizeof(ULONG
),
345 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
349 KeQueryTickCount(PLARGE_INTEGER TickCount
)
351 * FUNCTION: Returns the number of ticks since the system was booted
353 * TickCount (OUT) = Points to storage for the number of ticks
356 TickCount
->QuadPart
= KeTickCount
;
360 HandleExpiredTimer(PKTIMER current
)
362 DPRINT("HandleExpiredTime(current %x)\n",current
);
363 if (current
->Dpc
!= NULL
)
365 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
366 current
->Dpc
, current
->Dpc
->DeferredRoutine
);
367 KeInsertQueueDpc(current
->Dpc
,
370 DPRINT("Finished dpc routine\n");
372 KeAcquireDispatcherDatabaseLock(FALSE
);
373 current
->Header
.SignalState
= TRUE
;
374 KeDispatcherObjectWake(¤t
->Header
);
375 KeReleaseDispatcherDatabaseLock(FALSE
);
376 if (current
->Period
!= 0)
378 current
->DueTime
.QuadPart
+=
379 current
->Period
* SYSTEM_TIME_UNITS_PER_MSEC
;
383 RemoveEntryList(¤t
->TimerListEntry
);
384 current
->TimerListEntry
.Flink
= current
->TimerListEntry
.Blink
= NULL
;
389 KeExpireTimers(PKDPC Dpc
,
394 PLIST_ENTRY current_entry
= NULL
;
395 PKTIMER current
= NULL
;
396 ULONG Eip
= (ULONG
)Arg1
;
398 LARGE_INTEGER SystemTime
;
400 DPRINT("KeExpireTimers()\n");
402 KeRaiseIrql(PROFILE_LEVEL
, &oldIrql
);
403 KeAcquireSpinLockAtDpcLevel(&TimerValueLock
);
405 SystemTime
= system_time
;
407 KeReleaseSpinLock(&TimerValueLock
, oldIrql
);
408 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
410 if (KeGetCurrentIrql() > DISPATCH_LEVEL
)
412 DPRINT1("-----------------------------\n");
417 current_entry
= TimerListHead
.Flink
;
419 while (current_entry
!= &TimerListHead
)
421 current
= CONTAINING_RECORD(current_entry
, KTIMER
, TimerListEntry
);
423 current_entry
= current_entry
->Flink
;
424 if ((ULONGLONG
) SystemTime
.QuadPart
>= current
->DueTime
.QuadPart
)
426 HandleExpiredTimer(current
);
430 KiAddProfileEvent(ProfileTime
, Eip
);
432 KeReleaseSpinLockFromDpcLevel(&TimerListLock
);
437 KiUpdateSystemTime(KIRQL oldIrql
,
440 * FUNCTION: Handles a timer interrupt
444 assert(KeGetCurrentIrql() == PROFILE_LEVEL
);
448 if (TimerInitDone
== FALSE
)
453 * Increment the number of timers ticks
456 SharedUserData
->TickCountLow
++;
458 KeAcquireSpinLockAtDpcLevel(&TimerValueLock
);
459 system_time
.QuadPart
+= CLOCK_INCREMENT
;
460 KeReleaseSpinLockFromDpcLevel(&TimerValueLock
);
463 * Queue a DPC that will expire timers
465 KeInsertQueueDpc(&ExpireTimerDpc
, (PVOID
)Eip
, 0);
470 KeInitializeTimerImpl(VOID
)
472 * FUNCTION: Initializes timer irq handling
473 * NOTE: This is only called once from main()
476 TIME_FIELDS TimeFields
;
477 LARGE_INTEGER SystemBootTime
;
479 DPRINT("KeInitializeTimerImpl()\n");
480 InitializeListHead(&TimerListHead
);
481 KeInitializeSpinLock(&TimerListLock
);
482 KeInitializeSpinLock(&TimerValueLock
);
483 KeInitializeDpc(&ExpireTimerDpc
, KeExpireTimers
, 0);
484 TimerInitDone
= TRUE
;
486 * Calculate the starting time for the system clock
488 HalQueryRealTimeClock(&TimeFields
);
489 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
490 boot_time
=SystemBootTime
;
491 system_time
=boot_time
;
493 DPRINT("Finished KeInitializeTimerImpl()\n");