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");