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);
141 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
143 PLARGE_INTEGER Interval)
145 * FUNCTION: Puts the current thread into an alertable or nonalertable
146 * wait state for a given internal
148 * WaitMode = Processor mode in which the caller is waiting
149 * Altertable = Specifies if the wait is alertable
150 * Interval = Specifies the interval to wait
154 PKTHREAD Thread = KeGetCurrentThread();
156 KeInitializeTimer(&Thread->Timer);
157 KeSetTimer(&Thread->Timer, *Interval, NULL);
158 return (KeWaitForSingleObject(&Thread->Timer,
167 KeQueryTimeIncrement(VOID)
169 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
170 * the system clock every time the clock interrupts
171 * RETURNS: The increment
174 return(CLOCK_INCREMENT);
179 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
181 * FUNCTION: Gets the current system time
183 * CurrentTime (OUT) = The routine stores the current time here
184 * NOTE: The time is the number of 100-nanosecond intervals since the
185 * 1st of January, 1601.
190 KeRaiseIrql(PROFILE_LEVEL, &oldIrql);
191 KeAcquireSpinLockAtDpcLevel(&TimerValueLock);
192 *CurrentTime = system_time;
193 KeReleaseSpinLock(&TimerValueLock, oldIrql);
198 NtGetTickCount (PULONG UpTime)
200 LARGE_INTEGER TickCount;
202 return(STATUS_INVALID_PARAMETER);
203 KeQueryTickCount(&TickCount);
204 *UpTime = TickCount.u.LowPart;
205 return (STATUS_SUCCESS);
210 KeSetTimer (PKTIMER Timer,
211 LARGE_INTEGER DueTime,
214 * FUNCTION: Sets the absolute or relative interval at which a timer object
215 * is to be set to the signaled state and optionally supplies a
216 * CustomTimerDpc to be executed when the timer expires.
218 * Timer = Points to a previously initialized timer object
219 * DueTimer = If positive then absolute time to expire at
220 * If negative then the relative time to expire at
221 * Dpc = If non-NULL then a dpc to be called when the timer expires
222 * RETURNS: True if the timer was already in the system timer queue
226 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
230 KeSetTimerEx (PKTIMER Timer,
231 LARGE_INTEGER DueTime,
235 * FUNCTION: Sets the absolute or relative interval at which a timer object
236 * is to be set to the signaled state and optionally supplies a
237 * CustomTimerDpc to be executed when the timer expires.
239 * Timer = Points to a previously initialized timer object
240 * DueTimer = If positive then absolute time to expire at
241 * If negative then the relative time to expire at
242 * Dpc = If non-NULL then a dpc to be called when the timer expires
243 * RETURNS: True if the timer was already in the system timer queue
248 LARGE_INTEGER SystemTime;
250 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer);
252 KeRaiseIrql(PROFILE_LEVEL, &oldlvl);
253 KeAcquireSpinLockAtDpcLevel(&TimerValueLock);
255 SystemTime = system_time;
257 KeReleaseSpinLock(&TimerValueLock, DISPATCH_LEVEL);
258 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
261 if (DueTime.QuadPart < 0)
264 Timer->DueTime.QuadPart = SystemTime.QuadPart - DueTime.QuadPart;
268 Timer->DueTime.QuadPart = DueTime.QuadPart;
270 Timer->Period = Period;
271 Timer->Header.SignalState = FALSE;
272 if (Timer->TimerListEntry.Flink != NULL)
274 KeReleaseSpinLock(&TimerListLock, oldlvl);
277 InsertTailList(&TimerListHead,&Timer->TimerListEntry);
278 KeReleaseSpinLock(&TimerListLock, oldlvl);
283 #endif /* LIBCAPTIVE */
286 KeCancelTimer (PKTIMER Timer)
288 * FUNCTION: Removes a timer from the system timer list
290 * Timer = timer to cancel
291 * RETURNS: True if the timer was running
297 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
299 KeAcquireSpinLock(&TimerListLock, &oldlvl);
301 if (Timer->TimerListEntry.Flink == NULL)
303 KeReleaseSpinLock(&TimerListLock, oldlvl);
306 RemoveEntryList(&Timer->TimerListEntry);
307 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
308 KeReleaseSpinLock(&TimerListLock, oldlvl);
316 KeReadStateTimer (PKTIMER Timer)
318 return(Timer->Header.SignalState);
321 #endif /* LIBCAPTIVE */
324 KeInitializeTimer (PKTIMER Timer)
326 * FUNCTION: Initalizes a kernel timer object
328 * Timer = caller supplied storage for the timer
329 * NOTE: This function initializes a notification timer
332 KeInitializeTimerEx(Timer, NotificationTimer);
336 KeInitializeTimerEx (PKTIMER Timer,
339 * FUNCTION: Initializes a kernel timer object
341 * Timer = caller supplied storage for the timer
342 * Type = the type of timer (notification or synchronization)
343 * NOTE: When a notification type expires all waiting threads are released
344 * and the timer remains signalled until it is explicitly reset. When a
345 * syncrhonization timer expires its state is set to signalled until a
346 * single waiting thread is released and then the timer is reset.
351 if (Type == NotificationTimer)
353 IType = InternalNotificationTimer;
355 else if (Type == SynchronizationTimer)
357 IType = InternalSynchronizationTimer;
365 KeInitializeDispatcherHeader(&Timer->Header,
367 sizeof(KTIMER) / sizeof(ULONG),
369 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
375 KeQueryTickCount(PLARGE_INTEGER TickCount)
377 * FUNCTION: Returns the number of ticks since the system was booted
379 * TickCount (OUT) = Points to storage for the number of ticks
382 TickCount->QuadPart = KeTickCount;
386 HandleExpiredTimer(PKTIMER current)
388 DPRINT("HandleExpiredTime(current %x)\n",current);
389 if (current->Dpc != NULL)
391 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
392 current->Dpc, current->Dpc->DeferredRoutine);
393 KeInsertQueueDpc(current->Dpc,
396 DPRINT("Finished dpc routine\n");
398 KeAcquireDispatcherDatabaseLock(FALSE);
399 current->Header.SignalState = TRUE;
400 KeDispatcherObjectWake(¤t->Header);
401 KeReleaseDispatcherDatabaseLock(FALSE);
402 if (current->Period != 0)
404 current->DueTime.QuadPart +=
405 current->Period * SYSTEM_TIME_UNITS_PER_MSEC;
409 RemoveEntryList(¤t->TimerListEntry);
410 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
415 KeExpireTimers(PKDPC Dpc,
420 PLIST_ENTRY current_entry = NULL;
421 PKTIMER current = NULL;
422 ULONG Eip = (ULONG)Arg1;
424 LARGE_INTEGER SystemTime;
426 DPRINT("KeExpireTimers()\n");
428 KeRaiseIrql(PROFILE_LEVEL, &oldIrql);
429 KeAcquireSpinLockAtDpcLevel(&TimerValueLock);
431 SystemTime = system_time;
433 KeReleaseSpinLock(&TimerValueLock, oldIrql);
434 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
436 if (KeGetCurrentIrql() > DISPATCH_LEVEL)
438 DPRINT1("-----------------------------\n");
443 current_entry = TimerListHead.Flink;
445 while (current_entry != &TimerListHead)
447 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
449 current_entry = current_entry->Flink;
450 if ((ULONGLONG) SystemTime.QuadPart >= current->DueTime.QuadPart)
452 HandleExpiredTimer(current);
456 KiAddProfileEvent(ProfileTime, Eip);
458 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
463 KiUpdateSystemTime(KIRQL oldIrql,
466 * FUNCTION: Handles a timer interrupt
470 assert(KeGetCurrentIrql() == PROFILE_LEVEL);
474 if (TimerInitDone == FALSE)
479 * Increment the number of timers ticks
482 SharedUserData->TickCountLow++;
484 KeAcquireSpinLockAtDpcLevel(&TimerValueLock);
485 system_time.QuadPart += CLOCK_INCREMENT;
486 KeReleaseSpinLockFromDpcLevel(&TimerValueLock);
489 * Queue a DPC that will expire timers
491 KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)Eip, 0);
496 KeInitializeTimerImpl(VOID)
498 * FUNCTION: Initializes timer irq handling
499 * NOTE: This is only called once from main()
502 TIME_FIELDS TimeFields;
503 LARGE_INTEGER SystemBootTime;
505 DPRINT("KeInitializeTimerImpl()\n");
506 InitializeListHead(&TimerListHead);
507 KeInitializeSpinLock(&TimerListLock);
508 KeInitializeSpinLock(&TimerValueLock);
509 KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0);
510 TimerInitDone = TRUE;
512 * Calculate the starting time for the system clock
514 HalQueryRealTimeClock(&TimeFields);
515 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
516 boot_time=SystemBootTime;
517 system_time=boot_time;
519 DPRINT("Finished KeInitializeTimerImpl()\n");
522 #endif /* LIBCAPTIVE */