1 /* $Id: timer.c,v 1.64 2003/12/30 18:52:04 fireball 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>
25 #include <internal/safe.h>
28 #include <internal/debug.h>
30 /* TYPES *****************************************************************/
34 /* GLOBALS ****************************************************************/
39 static LONGLONG boot_time
= 0;
40 static LONGLONG system_time
= 0;
43 * Number of timer interrupts since initialisation
45 volatile ULONGLONG KeTickCount
= 0;
46 volatile ULONG KiRawTicks
= 0;
49 * The increment in the system clock every timer tick (in system time units)
55 #define CLOCK_INCREMENT (100000)
58 * PURPOSE: List of timers
60 static LIST_ENTRY TimerListHead
;
61 static KSPIN_LOCK TimerListLock
;
62 static KSPIN_LOCK TimerValueLock
;
63 static KDPC ExpireTimerDpc
;
65 /* must raise IRQL to PROFILE_LEVEL and grab spin lock there, to sync with ISR */
67 extern ULONG PiNrRunnableThreads
;
69 #define MICROSECONDS_PER_TICK (10000)
70 #define TICKS_TO_CALIBRATE (1)
71 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
72 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
74 static BOOLEAN TimerInitDone
= FALSE
;
76 /* FUNCTIONS **************************************************************/
80 NtQueryTimerResolution(OUT PULONG MinimumResolution
,
81 OUT PULONG MaximumResolution
,
82 OUT PULONG ActualResolution
)
89 NtSetTimerResolution(IN ULONG RequestedResolution
,
91 OUT PULONG ActualResolution
)
98 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter
,
99 IN PLARGE_INTEGER Frequency
)
101 LARGE_INTEGER PerfCounter
;
102 LARGE_INTEGER PerfFrequency
;
105 PerfCounter
= KeQueryPerformanceCounter(&PerfFrequency
);
109 Status
= MmCopyToCaller(&Counter
->QuadPart
, &PerfCounter
.QuadPart
, sizeof(PerfCounter
.QuadPart
));
110 if (!NT_SUCCESS(Status
))
116 if (Frequency
!= NULL
)
118 Status
= MmCopyToCaller(&Frequency
->QuadPart
, &PerfFrequency
.QuadPart
, sizeof(PerfFrequency
.QuadPart
));
119 if (!NT_SUCCESS(Status
))
125 return(STATUS_SUCCESS
);
130 NtDelayExecution(IN ULONG Alertable
,
134 LARGE_INTEGER Timeout
;
136 Status
= MmCopyFromCaller(&Timeout
, Interval
, sizeof(Timeout
));
137 if (!NT_SUCCESS(Status
))
142 Timeout
= *((PLARGE_INTEGER
)Interval
);
143 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
144 Alertable
, Internal
, Timeout
);
146 DPRINT("Execution delay is %d/%d\n",
147 Timeout
.u
.HighPart
, Timeout
.u
.LowPart
);
148 Status
= KeDelayExecutionThread(UserMode
, (BOOLEAN
)Alertable
, &Timeout
);
157 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode
,
159 PLARGE_INTEGER Interval
)
161 * FUNCTION: Puts the current thread into an alertable or nonalertable
162 * wait state for a given internal
164 * WaitMode = Processor mode in which the caller is waiting
165 * Altertable = Specifies if the wait is alertable
166 * Interval = Specifies the interval to wait
170 PKTHREAD Thread
= KeGetCurrentThread();
172 KeSetTimer(&Thread
->Timer
, *Interval
, NULL
);
173 return (KeWaitForSingleObject(&Thread
->Timer
,
174 (WaitMode
== KernelMode
) ? Executive
: UserRequest
, /* TMN: Was unconditionally Executive */
175 WaitMode
, /* TMN: Was UserMode */
185 KeQueryTimeIncrement(VOID
)
187 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
188 * the system clock every time the clock interrupts
189 * RETURNS: The increment
192 return(CLOCK_INCREMENT
);
200 KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
202 * FUNCTION: Gets the current system time
204 * CurrentTime (OUT) = The routine stores the current time here
205 * NOTE: The time is the number of 100-nanosecond intervals since the
206 * 1st of January, 1601.
211 KeRaiseIrql(PROFILE_LEVEL
, &oldIrql
);
212 KeAcquireSpinLockAtDpcLevel(&TimerValueLock
);
213 CurrentTime
->QuadPart
= system_time
;
214 KeReleaseSpinLockFromDpcLevel(&TimerValueLock
);
215 KeLowerIrql(oldIrql
);
220 NtGetTickCount (PULONG UpTime
)
222 LARGE_INTEGER TickCount
;
225 return(STATUS_INVALID_PARAMETER
);
228 KeQueryTickCount(&TickCount
);
229 return(MmCopyToCaller(UpTime
, &TickCount
.u
.LowPart
, sizeof(*UpTime
)));
237 KeSetTimer (PKTIMER Timer
,
238 LARGE_INTEGER DueTime
,
241 * FUNCTION: Sets the absolute or relative interval at which a timer object
242 * is to be set to the signaled state and optionally supplies a
243 * CustomTimerDpc to be executed when the timer expires.
245 * Timer = Points to a previously initialized timer object
246 * DueTimer = If positive then absolute time to expire at
247 * If negative then the relative time to expire at
248 * Dpc = If non-NULL then a dpc to be called when the timer expires
249 * RETURNS: True if the timer was already in the system timer queue
253 return(KeSetTimerEx(Timer
, DueTime
, 0, Dpc
));
260 KeSetTimerEx (PKTIMER Timer
,
261 LARGE_INTEGER DueTime
,
265 * FUNCTION: Sets the absolute or relative interval at which a timer object
266 * is to be set to the signaled state and optionally supplies a
267 * CustomTimerDpc to be executed when the timer expires.
269 * Timer = Points to a previously initialized timer object
270 * DueTimer = If positive then absolute time to expire at
271 * If negative then the relative time to expire at
272 * Dpc = If non-NULL then a dpc to be called when the timer expires
273 * RETURNS: True if the timer was already in the system timer queue
278 LARGE_INTEGER SystemTime
;
279 BOOLEAN AlreadyInList
;
281 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer
);
283 assert(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
285 KeRaiseIrql(DISPATCH_LEVEL
, &oldlvl
);
286 KeQuerySystemTime(&SystemTime
);
287 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
290 if (DueTime
.QuadPart
< 0)
292 Timer
->DueTime
.QuadPart
= SystemTime
.QuadPart
- DueTime
.QuadPart
;
296 Timer
->DueTime
.QuadPart
= DueTime
.QuadPart
;
298 Timer
->Period
= Period
;
299 Timer
->Header
.SignalState
= FALSE
;
300 AlreadyInList
= (Timer
->TimerListEntry
.Flink
== NULL
) ? FALSE
: TRUE
;
301 assert((Timer
->TimerListEntry
.Flink
== NULL
&& Timer
->TimerListEntry
.Blink
== NULL
) ||
302 (Timer
->TimerListEntry
.Flink
!= NULL
&& Timer
->TimerListEntry
.Blink
!= NULL
));
305 assert(&TimerListHead
!= &Timer
->TimerListEntry
);
306 InsertTailList(&TimerListHead
, &Timer
->TimerListEntry
);
307 assert(TimerListHead
.Flink
!= &TimerListHead
);
308 assert(Timer
->TimerListEntry
.Flink
!= &Timer
->TimerListEntry
);
311 * TODO: Perhaps verify that the timer really is in
312 * the TimerListHead list if AlreadyInList is TRUE?
314 KeReleaseSpinLockFromDpcLevel(&TimerListLock
);
317 return AlreadyInList
;
324 KeCancelTimer (PKTIMER Timer
)
326 * FUNCTION: Removes a timer from the system timer list
328 * Timer = timer to cancel
329 * RETURNS: True if the timer was running
335 DPRINT("KeCancelTimer(Timer %x)\n",Timer
);
337 KeAcquireSpinLock(&TimerListLock
, &oldlvl
);
339 if (Timer
->TimerListEntry
.Flink
== NULL
)
341 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
344 assert(&Timer
->TimerListEntry
!= &TimerListHead
);
345 assert(Timer
->TimerListEntry
.Flink
!= &Timer
->TimerListEntry
);
346 RemoveEntryList(&Timer
->TimerListEntry
);
347 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
348 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
357 KeReadStateTimer (PKTIMER Timer
)
359 return (BOOLEAN
)(Timer
->Header
.SignalState
);
366 KeInitializeTimer (PKTIMER Timer
)
368 * FUNCTION: Initalizes a kernel timer object
370 * Timer = caller supplied storage for the timer
371 * NOTE: This function initializes a notification timer
374 KeInitializeTimerEx(Timer
, NotificationTimer
);
381 KeInitializeTimerEx (PKTIMER Timer
,
384 * FUNCTION: Initializes a kernel timer object
386 * Timer = caller supplied storage for the timer
387 * Type = the type of timer (notification or synchronization)
388 * NOTE: When a notification type expires all waiting threads are released
389 * and the timer remains signalled until it is explicitly reset. When a
390 * syncrhonization timer expires its state is set to signalled until a
391 * single waiting thread is released and then the timer is reset.
396 if (Type
== NotificationTimer
)
398 IType
= InternalNotificationTimer
;
400 else if (Type
== SynchronizationTimer
)
402 IType
= InternalSynchronizationTimer
;
410 KeInitializeDispatcherHeader(&Timer
->Header
,
412 sizeof(KTIMER
) / sizeof(ULONG
),
414 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
421 KeQueryTickCount(PLARGE_INTEGER TickCount
)
423 * FUNCTION: Returns the number of ticks since the system was booted
425 * TickCount (OUT) = Points to storage for the number of ticks
428 TickCount
->QuadPart
= KeTickCount
;
432 * We enter this function at IRQL DISPATCH_LEVEL, and with the
433 * TimerListLock held.
436 HandleExpiredTimer(PKTIMER Timer
)
438 DPRINT("HandleExpiredTime(Timer %x) at IRQL: %d\n", Timer
, KeGetCurrentIrql());
439 if (Timer
->Dpc
!= NULL
)
441 DPRINT("Timer->Dpc %x Timer->Dpc->DeferredRoutine %x\n",
442 Timer
->Dpc
, Timer
->Dpc
->DeferredRoutine
);
443 KeInsertQueueDpc(Timer
->Dpc
,
446 DPRINT("Finished dpc routine\n");
449 assert(KeGetCurrentIrql() == DISPATCH_LEVEL
);
451 KeAcquireDispatcherDatabaseLockAtDpcLevel();
452 Timer
->Header
.SignalState
= TRUE
;
453 KeDispatcherObjectWake(&Timer
->Header
);
454 KeReleaseDispatcherDatabaseLockFromDpcLevel();
455 if (Timer
->Period
!= 0)
457 Timer
->DueTime
.QuadPart
+=
458 Timer
->Period
* SYSTEM_TIME_UNITS_PER_MSEC
;
462 assert(&Timer
->TimerListEntry
!= &TimerListHead
);
463 RemoveEntryList(&Timer
->TimerListEntry
);
464 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
469 KeExpireTimers(PKDPC Dpc
,
474 PLIST_ENTRY current_entry
= NULL
;
475 PKTIMER current
= NULL
;
476 ULONG Eip
= (ULONG
)Arg1
;
477 LARGE_INTEGER SystemTime
;
479 DPRINT("KeExpireTimers()\n");
481 assert(KeGetCurrentIrql() == DISPATCH_LEVEL
);
483 KeQuerySystemTime(&SystemTime
);
485 if (KeGetCurrentIrql() > DISPATCH_LEVEL
)
487 DPRINT1("-----------------------------\n");
491 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
493 current_entry
= TimerListHead
.Flink
;
495 assert(current_entry
);
497 while (current_entry
!= &TimerListHead
)
499 current
= CONTAINING_RECORD(current_entry
, KTIMER
, TimerListEntry
);
501 assert(current_entry
!= &TimerListHead
);
502 assert(current_entry
->Flink
!= current_entry
);
504 current_entry
= current_entry
->Flink
;
505 if ((ULONGLONG
) SystemTime
.QuadPart
>= current
->DueTime
.QuadPart
)
507 HandleExpiredTimer(current
);
511 KiAddProfileEvent(ProfileTime
, Eip
);
513 KeReleaseSpinLockFromDpcLevel(&TimerListLock
);
518 KiUpdateSystemTime(KIRQL oldIrql
,
521 * FUNCTION: Handles a timer interrupt
525 assert(KeGetCurrentIrql() == PROFILE_LEVEL
);
529 if (TimerInitDone
== FALSE
)
534 * Increment the number of timers ticks
537 SharedUserData
->TickCountLow
++;
539 KeAcquireSpinLockAtDpcLevel(&TimerValueLock
);
540 system_time
+= CLOCK_INCREMENT
;
541 KeReleaseSpinLockFromDpcLevel(&TimerValueLock
);
544 * Queue a DPC that will expire timers
546 KeInsertQueueDpc(&ExpireTimerDpc
, (PVOID
)Eip
, 0);
551 KeInitializeTimerImpl(VOID
)
553 * FUNCTION: Initializes timer irq handling
554 * NOTE: This is only called once from main()
557 TIME_FIELDS TimeFields
;
558 LARGE_INTEGER SystemBootTime
;
560 DPRINT("KeInitializeTimerImpl()\n");
561 InitializeListHead(&TimerListHead
);
562 KeInitializeSpinLock(&TimerListLock
);
563 KeInitializeSpinLock(&TimerValueLock
);
564 KeInitializeDpc(&ExpireTimerDpc
, KeExpireTimers
, 0);
565 TimerInitDone
= TRUE
;
567 * Calculate the starting time for the system clock
569 HalQueryRealTimeClock(&TimeFields
);
570 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
571 boot_time
=SystemBootTime
.QuadPart
;
572 system_time
=boot_time
;
574 SharedUserData
->TickCountLow
= 0;
575 SharedUserData
->TickCountMultiplier
= 167783691; // 2^24 * 1193182 / 119310
577 DPRINT("Finished KeInitializeTimerImpl()\n");