1 /* $Id: timer.c,v 1.74 2004/06/23 22:31:51 ion 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 /* GLOBALS ****************************************************************/
36 LARGE_INTEGER SystemBootTime
= (LARGE_INTEGER
)0LL;
38 LARGE_INTEGER SystemBootTime
= { 0 };
41 CHAR KiTimerSystemAuditing
= 0;
42 volatile ULONG KiKernelTime
;
43 volatile ULONG KiUserTime
;
44 volatile ULONG KiDpcTime
;
47 * Number of timer interrupts since initialisation
49 volatile ULONGLONG KeTickCount
= 0;
50 volatile ULONG KiRawTicks
= 0;
53 * The increment in the system clock every timer tick (in system time units)
59 #define CLOCK_INCREMENT (100000)
62 * PURPOSE: List of timers
64 static LIST_ENTRY AbsoluteTimerListHead
;
65 static LIST_ENTRY RelativeTimerListHead
;
66 static KSPIN_LOCK TimerListLock
;
67 static KSPIN_LOCK TimerValueLock
;
68 static KSPIN_LOCK TimeLock
;
69 static KDPC ExpireTimerDpc
;
71 /* must raise IRQL to PROFILE_LEVEL and grab spin lock there, to sync with ISR */
73 extern ULONG PiNrRunnableThreads
;
74 extern HANDLE PsIdleThreadHandle
;
77 #define MICROSECONDS_PER_TICK (10000)
78 #define TICKS_TO_CALIBRATE (1)
79 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
80 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
82 static BOOLEAN TimerInitDone
= FALSE
;
84 /* FUNCTIONS **************************************************************/
88 NtQueryTimerResolution(OUT PULONG MinimumResolution
,
89 OUT PULONG MaximumResolution
,
90 OUT PULONG ActualResolution
)
93 return STATUS_NOT_IMPLEMENTED
;
98 NtSetTimerResolution(IN ULONG RequestedResolution
,
100 OUT PULONG ActualResolution
)
103 return STATUS_NOT_IMPLEMENTED
;
108 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter
,
109 IN PLARGE_INTEGER Frequency
)
111 LARGE_INTEGER PerfCounter
;
112 LARGE_INTEGER PerfFrequency
;
115 PerfCounter
= KeQueryPerformanceCounter(&PerfFrequency
);
119 Status
= MmCopyToCaller(&Counter
->QuadPart
, &PerfCounter
.QuadPart
, sizeof(PerfCounter
.QuadPart
));
120 if (!NT_SUCCESS(Status
))
126 if (Frequency
!= NULL
)
128 Status
= MmCopyToCaller(&Frequency
->QuadPart
, &PerfFrequency
.QuadPart
, sizeof(PerfFrequency
.QuadPart
));
129 if (!NT_SUCCESS(Status
))
135 return(STATUS_SUCCESS
);
140 NtDelayExecution(IN ULONG Alertable
,
144 LARGE_INTEGER Timeout
;
146 Status
= MmCopyFromCaller(&Timeout
, Interval
, sizeof(Timeout
));
147 if (!NT_SUCCESS(Status
))
152 Timeout
= *((PLARGE_INTEGER
)Interval
);
153 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
154 Alertable
, Internal
, Timeout
);
156 DPRINT("Execution delay is %d/%d\n",
157 Timeout
.u
.HighPart
, Timeout
.u
.LowPart
);
158 Status
= KeDelayExecutionThread(UserMode
, (BOOLEAN
)Alertable
, &Timeout
);
167 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode
,
169 PLARGE_INTEGER Interval
)
171 * FUNCTION: Puts the current thread into an alertable or nonalertable
172 * wait state for a given internal
174 * WaitMode = Processor mode in which the caller is waiting
175 * Altertable = Specifies if the wait is alertable
176 * Interval = Specifies the interval to wait
180 PKTHREAD Thread
= KeGetCurrentThread();
182 KeSetTimer(&Thread
->Timer
, *Interval
, NULL
);
183 return (KeWaitForSingleObject(&Thread
->Timer
,
184 (WaitMode
== KernelMode
) ? Executive
: UserRequest
, /* TMN: Was unconditionally Executive */
185 WaitMode
, /* TMN: Was UserMode */
195 KeQueryTimeIncrement(VOID
)
197 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
198 * the system clock every time the clock interrupts
199 * RETURNS: The increment
202 return(CLOCK_INCREMENT
);
210 KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
212 * FUNCTION: Gets the current system time
214 * CurrentTime (OUT) = The routine stores the current time here
215 * NOTE: The time is the number of 100-nanosecond intervals since the
216 * 1st of January, 1601.
221 CurrentTime
->u
.HighPart
= SharedUserData
->SystemTime
.High1Part
;
222 CurrentTime
->u
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
224 while (CurrentTime
->u
.HighPart
!= SharedUserData
->SystemTime
.High2Part
);
228 KeQueryInterruptTime(VOID
)
230 LARGE_INTEGER CurrentTime
;
234 CurrentTime
.u
.HighPart
= SharedUserData
->InterruptTime
.High1Part
;
235 CurrentTime
.u
.LowPart
= SharedUserData
->InterruptTime
.LowPart
;
237 while (CurrentTime
.u
.HighPart
!= SharedUserData
->InterruptTime
.High2Part
);
239 return CurrentTime
.QuadPart
;
249 LARGE_INTEGER TickCount
;
250 KeQueryTickCount(&TickCount
);
251 return TickCount
.u
.LowPart
;
258 KeSetTimer (PKTIMER Timer
,
259 LARGE_INTEGER DueTime
,
262 * FUNCTION: Sets the absolute or relative interval at which a timer object
263 * is to be set to the signaled state and optionally supplies a
264 * CustomTimerDpc to be executed when the timer expires.
266 * Timer = Points to a previously initialized timer object
267 * DueTimer = If positive then absolute time to expire at
268 * If negative then the relative time to expire at
269 * Dpc = If non-NULL then a dpc to be called when the timer expires
270 * RETURNS: True if the timer was already in the system timer queue
274 return(KeSetTimerEx(Timer
, DueTime
, 0, Dpc
));
281 KeSetTimerEx (PKTIMER Timer
,
282 LARGE_INTEGER DueTime
,
286 * FUNCTION: Sets the absolute or relative interval at which a timer object
287 * is to be set to the signaled state and optionally supplies a
288 * CustomTimerDpc to be executed when the timer expires.
290 * Timer = Points to a previously initialized timer object
291 * DueTimer = If positive then absolute time to expire at
292 * If negative then the relative time to expire at
293 * Dpc = If non-NULL then a dpc to be called when the timer expires
294 * RETURNS: True if the timer was already in the system timer queue
300 BOOLEAN AlreadyInList
;
302 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer
);
304 assert(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
306 KeAcquireSpinLock(&TimerListLock
, &oldlvl
);
309 if (DueTime
.QuadPart
< 0)
311 Timer
->Header
.Absolute
= 0;
312 Timer
->DueTime
.QuadPart
= KeQueryInterruptTime() - DueTime
.QuadPart
;
316 KeQuerySystemTime(&Time
);
317 Timer
->Header
.Absolute
= 1;
318 if (DueTime
.QuadPart
>= Time
.QuadPart
)
320 Timer
->DueTime
.QuadPart
= DueTime
.QuadPart
;
324 Timer
->DueTime
.QuadPart
= Time
.QuadPart
;
327 Timer
->Period
= Period
;
328 Timer
->Header
.SignalState
= FALSE
;
329 AlreadyInList
= (Timer
->TimerListEntry
.Flink
== NULL
) ? FALSE
: TRUE
;
330 assert((Timer
->TimerListEntry
.Flink
== NULL
&& Timer
->TimerListEntry
.Blink
== NULL
) ||
331 (Timer
->TimerListEntry
.Flink
!= NULL
&& Timer
->TimerListEntry
.Blink
!= NULL
));
334 RemoveEntryList(&Timer
->TimerListEntry
);
336 if (Timer
->Header
.Absolute
)
338 InsertAscendingList(&AbsoluteTimerListHead
,
347 InsertAscendingList(&RelativeTimerListHead
,
355 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
357 return AlreadyInList
;
364 KeCancelTimer (PKTIMER Timer
)
366 * FUNCTION: Removes a timer from the system timer list
368 * Timer = timer to cancel
369 * RETURNS: True if the timer was running
375 DPRINT("KeCancelTimer(Timer %x)\n",Timer
);
377 KeAcquireSpinLock(&TimerListLock
, &oldlvl
);
379 if (Timer
->TimerListEntry
.Flink
== NULL
)
381 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
384 if (Timer
->Header
.Absolute
)
386 assert(&Timer
->TimerListEntry
!= &AbsoluteTimerListHead
);
390 assert(&Timer
->TimerListEntry
!= &RelativeTimerListHead
);
392 assert(Timer
->TimerListEntry
.Flink
!= &Timer
->TimerListEntry
);
393 RemoveEntryList(&Timer
->TimerListEntry
);
394 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
395 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
404 KeReadStateTimer (PKTIMER Timer
)
406 return (BOOLEAN
)(Timer
->Header
.SignalState
);
413 KeInitializeTimer (PKTIMER Timer
)
415 * FUNCTION: Initalizes a kernel timer object
417 * Timer = caller supplied storage for the timer
418 * NOTE: This function initializes a notification timer
421 KeInitializeTimerEx(Timer
, NotificationTimer
);
428 KeInitializeTimerEx (PKTIMER Timer
,
431 * FUNCTION: Initializes a kernel timer object
433 * Timer = caller supplied storage for the timer
434 * Type = the type of timer (notification or synchronization)
435 * NOTE: When a notification type expires all waiting threads are released
436 * and the timer remains signalled until it is explicitly reset. When a
437 * syncrhonization timer expires its state is set to signalled until a
438 * single waiting thread is released and then the timer is reset.
443 if (Type
== NotificationTimer
)
445 IType
= InternalNotificationTimer
;
447 else if (Type
== SynchronizationTimer
)
449 IType
= InternalSynchronizationTimer
;
457 KeInitializeDispatcherHeader(&Timer
->Header
,
459 sizeof(KTIMER
) / sizeof(ULONG
),
461 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
468 KeQueryTickCount(PLARGE_INTEGER TickCount
)
470 * FUNCTION: Returns the number of ticks since the system was booted
472 * TickCount (OUT) = Points to storage for the number of ticks
475 TickCount
->QuadPart
= KeTickCount
;
483 KeQueryRuntimeThread(
498 IN ULONG MaxIncrement
,
499 IN ULONG MinIncrement
506 * We enter this function at IRQL DISPATCH_LEVEL, and with the
507 * TimerListLock held.
510 HandleExpiredTimer(PKTIMER Timer
)
512 DPRINT("HandleExpiredTime(Timer %x)\n", Timer
);
513 if (Timer
->Dpc
!= NULL
)
515 DPRINT("Timer->Dpc %x Timer->Dpc->DeferredRoutine %x\n",
516 Timer
->Dpc
, Timer
->Dpc
->DeferredRoutine
);
517 KeInsertQueueDpc(Timer
->Dpc
,
520 DPRINT("Finished dpc routine\n");
523 assert(KeGetCurrentIrql() == DISPATCH_LEVEL
);
525 KeAcquireDispatcherDatabaseLockAtDpcLevel();
526 Timer
->Header
.SignalState
= TRUE
;
527 KeDispatcherObjectWake(&Timer
->Header
);
528 KeReleaseDispatcherDatabaseLockFromDpcLevel();
530 if (Timer
->Period
!= 0)
532 Timer
->DueTime
.QuadPart
+=
533 Timer
->Period
* SYSTEM_TIME_UNITS_PER_MSEC
;
534 if (Timer
->Header
.Absolute
)
536 InsertAscendingList(&AbsoluteTimerListHead
,
544 InsertAscendingList(&RelativeTimerListHead
,
554 KeExpireTimers(PKDPC Dpc
,
559 PLIST_ENTRY current_entry
= NULL
;
560 PKTIMER current
= NULL
;
561 ULONG Eip
= (ULONG
)Arg1
;
562 LARGE_INTEGER InterruptTime
;
563 LARGE_INTEGER SystemTime
;
564 LIST_ENTRY TimerList
;
566 DPRINT("KeExpireTimers()\n");
568 assert(KeGetCurrentIrql() == DISPATCH_LEVEL
);
570 InitializeListHead(&TimerList
);
572 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
574 InterruptTime
.QuadPart
= KeQueryInterruptTime();
575 KeQuerySystemTime(&SystemTime
);
577 current_entry
= RelativeTimerListHead
.Flink
;
578 assert(current_entry
);
579 while (current_entry
!= &RelativeTimerListHead
)
581 current
= CONTAINING_RECORD(current_entry
, KTIMER
, TimerListEntry
);
583 assert(current_entry
!= &RelativeTimerListHead
);
584 assert(current_entry
->Flink
!= current_entry
);
585 if ((ULONGLONG
)InterruptTime
.QuadPart
< current
->DueTime
.QuadPart
)
589 current_entry
= current_entry
->Flink
;
590 RemoveEntryList(¤t
->TimerListEntry
);
591 InsertTailList(&TimerList
, ¤t
->TimerListEntry
);
594 current_entry
= AbsoluteTimerListHead
.Flink
;
595 assert(current_entry
);
596 while (current_entry
!= &AbsoluteTimerListHead
)
598 current
= CONTAINING_RECORD(current_entry
, KTIMER
, TimerListEntry
);
600 assert(current_entry
!= &AbsoluteTimerListHead
);
601 assert(current_entry
->Flink
!= current_entry
);
602 if ((ULONGLONG
)SystemTime
.QuadPart
< current
->DueTime
.QuadPart
)
606 current_entry
= current_entry
->Flink
;
607 RemoveEntryList(¤t
->TimerListEntry
);
608 InsertTailList(&TimerList
, ¤t
->TimerListEntry
);
611 while (!IsListEmpty(&TimerList
))
613 current_entry
= RemoveHeadList(&TimerList
);
614 current
= CONTAINING_RECORD(current_entry
, KTIMER
, TimerListEntry
);
615 current
->TimerListEntry
.Flink
= current
->TimerListEntry
.Blink
= NULL
;
616 HandleExpiredTimer(current
);
619 KiAddProfileEvent(ProfileTime
, Eip
);
621 KeReleaseSpinLockFromDpcLevel(&TimerListLock
);
626 KiUpdateSystemTime(KIRQL oldIrql
,
629 * FUNCTION: Handles a timer interrupt
634 assert(KeGetCurrentIrql() == PROFILE_LEVEL
);
638 if (TimerInitDone
== FALSE
)
643 * Increment the number of timers ticks
646 SharedUserData
->TickCountLow
++;
648 KiAcquireSpinLock(&TimerValueLock
);
650 Time
.u
.LowPart
= SharedUserData
->InterruptTime
.LowPart
;
651 Time
.u
.HighPart
= SharedUserData
->InterruptTime
.High1Part
;
652 Time
.QuadPart
+= CLOCK_INCREMENT
;
653 SharedUserData
->InterruptTime
.High2Part
= Time
.u
.HighPart
;
654 SharedUserData
->InterruptTime
.LowPart
= Time
.u
.LowPart
;
655 SharedUserData
->InterruptTime
.High1Part
= Time
.u
.HighPart
;
657 Time
.u
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
658 Time
.u
.HighPart
= SharedUserData
->SystemTime
.High1Part
;
659 Time
.QuadPart
+= CLOCK_INCREMENT
;
660 SharedUserData
->SystemTime
.High2Part
= Time
.u
.HighPart
;
661 SharedUserData
->SystemTime
.LowPart
= Time
.u
.LowPart
;
662 SharedUserData
->SystemTime
.High1Part
= Time
.u
.HighPart
;
664 KiReleaseSpinLock(&TimerValueLock
);
667 * Queue a DPC that will expire timers
669 KeInsertQueueDpc(&ExpireTimerDpc
, (PVOID
)Eip
, 0);
674 KeInitializeTimerImpl(VOID
)
676 * FUNCTION: Initializes timer irq handling
677 * NOTE: This is only called once from main()
680 TIME_FIELDS TimeFields
;
682 DPRINT("KeInitializeTimerImpl()\n");
683 InitializeListHead(&AbsoluteTimerListHead
);
684 InitializeListHead(&RelativeTimerListHead
);
685 KeInitializeSpinLock(&TimerListLock
);
686 KeInitializeSpinLock(&TimerValueLock
);
687 KeInitializeSpinLock(&TimeLock
);
688 KeInitializeDpc(&ExpireTimerDpc
, KeExpireTimers
, 0);
690 * Calculate the starting time for the system clock
692 HalQueryRealTimeClock(&TimeFields
);
693 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
695 SharedUserData
->TickCountLow
= 0;
696 SharedUserData
->TickCountMultiplier
= 167783691; // 2^24 * 1193182 / 119310
697 SharedUserData
->InterruptTime
.High2Part
= 0;
698 SharedUserData
->InterruptTime
.LowPart
= 0;
699 SharedUserData
->InterruptTime
.High1Part
= 0;
700 SharedUserData
->SystemTime
.High2Part
= SystemBootTime
.u
.HighPart
;
701 SharedUserData
->SystemTime
.LowPart
= SystemBootTime
.u
.LowPart
;
702 SharedUserData
->SystemTime
.High1Part
= SystemBootTime
.u
.HighPart
;
704 TimerInitDone
= TRUE
;
705 DPRINT("Finished KeInitializeTimerImpl()\n");
710 KiUpdateProcessThreadTime(VOID
)
712 PKTHREAD CurrentThread
;
713 PKPROCESS CurrentProcess
;
715 assert(KeGetCurrentIrql() == PASSIVE_LEVEL
);
717 * Make sure no counting can take place until Processes and Threads are
720 if ((PsInitialSystemProcess
== NULL
) ||
721 (PsIdleThreadHandle
== NULL
) || (KiTimerSystemAuditing
== 0))
726 CurrentProcess
= KeGetCurrentProcess();
727 CurrentThread
= KeGetCurrentThread();
729 DPRINT("KiKernelTime %u, KiUserTime %u \n", KiKernelTime
, KiUserTime
);
731 KiAcquireSpinLock(&TimeLock
);
733 if (CurrentThread
->PreviousMode
== UserMode
)
735 ++CurrentThread
->UserTime
;
736 ++CurrentProcess
->UserTime
;
739 if (CurrentThread
->PreviousMode
== KernelMode
)
741 ++CurrentProcess
->KernelTime
;
742 ++CurrentThread
->KernelTime
;
746 KiReleaseSpinLock(&TimeLock
);
754 KeSetTimeUpdateNotifyRoutine(
755 IN PTIME_UPDATE_NOTIFY_ROUTINE NotifyRoutine
768 IN PKTRAP_FRAME TrapFrame
780 IN PKTRAP_FRAME TrapFrame
,