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 ****************************************************************/
40 static LARGE_INTEGER boot_time = (LARGE_INTEGER)0LL;
41 static LARGE_INTEGER system_time = (LARGE_INTEGER)0LL;
44 * Number of timer interrupts since initialisation
46 volatile ULONGLONG KeTickCount = 0;
47 volatile ULONG KiRawTicks = 0;
50 * The increment in the system clock every timer tick (in system time units)
56 #define CLOCK_INCREMENT (100000)
59 * PURPOSE: List of timers
61 static LIST_ENTRY TimerListHead;
62 #endif /* LIBCAPTIVE */
63 static KSPIN_LOCK TimerListLock;
65 static KSPIN_LOCK TimerValueLock;
66 static KDPC ExpireTimerDpc;
68 /* must raise IRQL to PROFILE_LEVEL and grab spin lock there, to sync with ISR */
70 extern ULONG PiNrRunnableThreads;
72 #define MICROSECONDS_PER_TICK (10000)
73 #define TICKS_TO_CALIBRATE (1)
74 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
75 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
77 static BOOLEAN TimerInitDone = FALSE;
79 #endif /* LIBCAPTIVE */
81 /* FUNCTIONS **************************************************************/
86 NtQueryTimerResolution(OUT PULONG MinimumResolution,
87 OUT PULONG MaximumResolution,
88 OUT PULONG ActualResolution)
95 NtSetTimerResolution(IN ULONG RequestedResolution,
97 OUT PULONG ActualResolution)
104 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
105 IN PLARGE_INTEGER Frequency)
107 LARGE_INTEGER PerfCounter;
108 LARGE_INTEGER PerfFrequency;
110 PerfCounter = KeQueryPerformanceCounter(&PerfFrequency);
113 Counter->QuadPart = PerfCounter.QuadPart;
115 if (Frequency != NULL)
116 Frequency->QuadPart = PerfFrequency.QuadPart;
118 return(STATUS_SUCCESS);
123 NtDelayExecution(IN ULONG Alertable,
127 LARGE_INTEGER Timeout;
129 Timeout = *((PLARGE_INTEGER)Interval);
130 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
131 Alertable, Internal, Timeout);
133 DPRINT("Execution delay is %d/%d\n",
134 Timeout.u.HighPart, Timeout.u.LowPart);
135 Status = KeDelayExecutionThread(UserMode, Alertable, &Timeout);
144 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
146 PLARGE_INTEGER Interval)
148 * FUNCTION: Puts the current thread into an alertable or nonalertable
149 * wait state for a given internal
151 * WaitMode = Processor mode in which the caller is waiting
152 * Altertable = Specifies if the wait is alertable
153 * Interval = Specifies the interval to wait
157 PKTHREAD Thread = KeGetCurrentThread();
159 KeInitializeTimer(&Thread->Timer);
160 KeSetTimer(&Thread->Timer, *Interval, NULL);
161 return (KeWaitForSingleObject(&Thread->Timer,
173 KeQueryTimeIncrement(VOID)
175 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
176 * the system clock every time the clock interrupts
177 * RETURNS: The increment
180 return(CLOCK_INCREMENT);
188 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
190 * FUNCTION: Gets the current system time
192 * CurrentTime (OUT) = The routine stores the current time here
193 * NOTE: The time is the number of 100-nanosecond intervals since the
194 * 1st of January, 1601.
199 KeRaiseIrql(PROFILE_LEVEL, &oldIrql);
200 KeAcquireSpinLockAtDpcLevel(&TimerValueLock);
201 *CurrentTime = system_time;
202 KeReleaseSpinLock(&TimerValueLock, oldIrql);
207 NtGetTickCount (PULONG UpTime)
209 LARGE_INTEGER TickCount;
211 return(STATUS_INVALID_PARAMETER);
212 KeQueryTickCount(&TickCount);
213 *UpTime = TickCount.u.LowPart;
214 return (STATUS_SUCCESS);
222 KeSetTimer (PKTIMER Timer,
223 LARGE_INTEGER DueTime,
226 * FUNCTION: Sets the absolute or relative interval at which a timer object
227 * is to be set to the signaled state and optionally supplies a
228 * CustomTimerDpc to be executed when the timer expires.
230 * Timer = Points to a previously initialized timer object
231 * DueTimer = If positive then absolute time to expire at
232 * If negative then the relative time to expire at
233 * Dpc = If non-NULL then a dpc to be called when the timer expires
234 * RETURNS: True if the timer was already in the system timer queue
238 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
245 KeSetTimerEx (PKTIMER Timer,
246 LARGE_INTEGER DueTime,
250 * FUNCTION: Sets the absolute or relative interval at which a timer object
251 * is to be set to the signaled state and optionally supplies a
252 * CustomTimerDpc to be executed when the timer expires.
254 * Timer = Points to a previously initialized timer object
255 * DueTimer = If positive then absolute time to expire at
256 * If negative then the relative time to expire at
257 * Dpc = If non-NULL then a dpc to be called when the timer expires
258 * RETURNS: True if the timer was already in the system timer queue
263 LARGE_INTEGER SystemTime;
265 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer);
267 KeRaiseIrql(PROFILE_LEVEL, &oldlvl);
268 KeAcquireSpinLockAtDpcLevel(&TimerValueLock);
270 SystemTime = system_time;
272 KeReleaseSpinLock(&TimerValueLock, DISPATCH_LEVEL);
273 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
276 if (DueTime.QuadPart < 0)
279 Timer->DueTime.QuadPart = SystemTime.QuadPart - DueTime.QuadPart;
283 Timer->DueTime.QuadPart = DueTime.QuadPart;
285 Timer->Period = Period;
286 Timer->Header.SignalState = FALSE;
287 if (Timer->TimerListEntry.Flink != NULL)
289 KeReleaseSpinLock(&TimerListLock, oldlvl);
292 InsertTailList(&TimerListHead,&Timer->TimerListEntry);
293 KeReleaseSpinLock(&TimerListLock, oldlvl);
298 #endif /* LIBCAPTIVE */
304 KeCancelTimer (PKTIMER Timer)
306 * FUNCTION: Removes a timer from the system timer list
308 * Timer = timer to cancel
309 * RETURNS: True if the timer was running
315 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
317 KeAcquireSpinLock(&TimerListLock, &oldlvl);
319 if (Timer->TimerListEntry.Flink == NULL)
321 KeReleaseSpinLock(&TimerListLock, oldlvl);
324 RemoveEntryList(&Timer->TimerListEntry);
325 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
326 KeReleaseSpinLock(&TimerListLock, oldlvl);
337 KeReadStateTimer (PKTIMER Timer)
339 return(Timer->Header.SignalState);
342 #endif /* LIBCAPTIVE */
348 KeInitializeTimer (PKTIMER Timer)
350 * FUNCTION: Initalizes a kernel timer object
352 * Timer = caller supplied storage for the timer
353 * NOTE: This function initializes a notification timer
356 KeInitializeTimerEx(Timer, NotificationTimer);
363 KeInitializeTimerEx (PKTIMER Timer,
366 * FUNCTION: Initializes a kernel timer object
368 * Timer = caller supplied storage for the timer
369 * Type = the type of timer (notification or synchronization)
370 * NOTE: When a notification type expires all waiting threads are released
371 * and the timer remains signalled until it is explicitly reset. When a
372 * syncrhonization timer expires its state is set to signalled until a
373 * single waiting thread is released and then the timer is reset.
378 if (Type == NotificationTimer)
380 IType = InternalNotificationTimer;
382 else if (Type == SynchronizationTimer)
384 IType = InternalSynchronizationTimer;
392 KeInitializeDispatcherHeader(&Timer->Header,
394 sizeof(KTIMER) / sizeof(ULONG),
396 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
405 KeQueryTickCount(PLARGE_INTEGER TickCount)
407 * FUNCTION: Returns the number of ticks since the system was booted
409 * TickCount (OUT) = Points to storage for the number of ticks
412 TickCount->QuadPart = KeTickCount;
416 HandleExpiredTimer(PKTIMER current)
418 DPRINT("HandleExpiredTime(current %x)\n",current);
419 if (current->Dpc != NULL)
421 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
422 current->Dpc, current->Dpc->DeferredRoutine);
423 KeInsertQueueDpc(current->Dpc,
426 DPRINT("Finished dpc routine\n");
428 KeAcquireDispatcherDatabaseLock(FALSE);
429 current->Header.SignalState = TRUE;
430 KeDispatcherObjectWake(¤t->Header);
431 KeReleaseDispatcherDatabaseLock(FALSE);
432 if (current->Period != 0)
434 current->DueTime.QuadPart +=
435 current->Period * SYSTEM_TIME_UNITS_PER_MSEC;
439 RemoveEntryList(¤t->TimerListEntry);
440 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
445 KeExpireTimers(PKDPC Dpc,
450 PLIST_ENTRY current_entry = NULL;
451 PKTIMER current = NULL;
452 ULONG Eip = (ULONG)Arg1;
454 LARGE_INTEGER SystemTime;
456 DPRINT("KeExpireTimers()\n");
458 KeRaiseIrql(PROFILE_LEVEL, &oldIrql);
459 KeAcquireSpinLockAtDpcLevel(&TimerValueLock);
461 SystemTime = system_time;
463 KeReleaseSpinLock(&TimerValueLock, oldIrql);
464 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
466 if (KeGetCurrentIrql() > DISPATCH_LEVEL)
468 DPRINT1("-----------------------------\n");
473 current_entry = TimerListHead.Flink;
475 while (current_entry != &TimerListHead)
477 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
479 current_entry = current_entry->Flink;
480 if ((ULONGLONG) SystemTime.QuadPart >= current->DueTime.QuadPart)
482 HandleExpiredTimer(current);
486 KiAddProfileEvent(ProfileTime, Eip);
488 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
493 KiUpdateSystemTime(KIRQL oldIrql,
496 * FUNCTION: Handles a timer interrupt
500 assert(KeGetCurrentIrql() == PROFILE_LEVEL);
504 if (TimerInitDone == FALSE)
509 * Increment the number of timers ticks
512 SharedUserData->TickCountLow++;
514 KeAcquireSpinLockAtDpcLevel(&TimerValueLock);
515 system_time.QuadPart += CLOCK_INCREMENT;
516 KeReleaseSpinLockFromDpcLevel(&TimerValueLock);
519 * Queue a DPC that will expire timers
521 KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)Eip, 0);
526 KeInitializeTimerImpl(VOID)
528 * FUNCTION: Initializes timer irq handling
529 * NOTE: This is only called once from main()
532 TIME_FIELDS TimeFields;
533 LARGE_INTEGER SystemBootTime;
535 DPRINT("KeInitializeTimerImpl()\n");
536 InitializeListHead(&TimerListHead);
537 KeInitializeSpinLock(&TimerListLock);
538 KeInitializeSpinLock(&TimerValueLock);
539 KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0);
540 TimerInitDone = TRUE;
542 * Calculate the starting time for the system clock
544 HalQueryRealTimeClock(&TimeFields);
545 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
546 boot_time=SystemBootTime;
547 system_time=boot_time;
549 SharedUserData->TickCountLow = 0;
550 SharedUserData->TickCountMultiplier = 167783691; // 2^24 * 1193182 / 119310
552 DPRINT("Finished KeInitializeTimerImpl()\n");
555 #endif /* LIBCAPTIVE */