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 unsigned long long boot_time = 0;
39 static unsigned long long system_time = 0;
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 KDPC ExpireTimerDpc;
63 /* must raise IRQL to HIGH_LEVEL and grab spin lock there, to sync with ISR */
65 extern ULONG PiNrRunnableThreads;
67 #define MICROSECONDS_PER_TICK (10000)
68 #define TICKS_TO_CALIBRATE (1)
69 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
70 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
72 static BOOLEAN TimerInitDone = FALSE;
74 /* FUNCTIONS **************************************************************/
78 NtQueryTimerResolution(OUT PULONG MinimumResolution,
79 OUT PULONG MaximumResolution,
80 OUT PULONG ActualResolution)
87 NtSetTimerResolution(IN ULONG RequestedResolution,
89 OUT PULONG ActualResolution)
96 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
97 IN PLARGE_INTEGER Frequency)
104 NtDelayExecution(IN ULONG Alertable,
108 LARGE_INTEGER Timeout;
110 Timeout = *((PLARGE_INTEGER)Interval);
111 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
112 Alertable, Internal, Timeout);
114 DPRINT("Execution delay is %d/%d\n",
115 Timeout.u.HighPart, Timeout.u.LowPart);
116 Status = KeDelayExecutionThread(UserMode, Alertable, &Timeout);
122 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
124 PLARGE_INTEGER Interval)
126 * FUNCTION: Puts the current thread into an alertable or nonalertable
127 * wait state for a given internal
129 * WaitMode = Processor mode in which the caller is waiting
130 * Altertable = Specifies if the wait is alertable
131 * Interval = Specifies the interval to wait
135 PKTHREAD Thread = KeGetCurrentThread();
137 KeInitializeTimer(&Thread->Timer);
138 KeSetTimer(&Thread->Timer, *Interval, NULL);
139 return (KeWaitForSingleObject(&Thread->Timer,
148 KeQueryTimeIncrement(VOID)
150 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
151 * the system clock every time the clock interrupts
152 * RETURNS: The increment
155 return(CLOCK_INCREMENT);
160 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
162 * FUNCTION: Gets the current system time
164 * CurrentTime (OUT) = The routine stores the current time here
165 * NOTE: The time is the number of 100-nanosecond intervals since the
166 * 1st of January, 1601.
169 CurrentTime->QuadPart = system_time;
174 NtGetTickCount (PULONG UpTime)
176 LARGE_INTEGER TickCount;
178 return(STATUS_INVALID_PARAMETER);
179 KeQueryTickCount(&TickCount);
180 *UpTime = TickCount.u.LowPart;
181 return (STATUS_SUCCESS);
186 KeSetTimer (PKTIMER Timer,
187 LARGE_INTEGER DueTime,
190 * FUNCTION: Sets the absolute or relative interval at which a timer object
191 * is to be set to the signaled state and optionally supplies a
192 * CustomTimerDpc to be executed when the timer expires.
194 * Timer = Points to a previously initialized timer object
195 * DueTimer = If positive then absolute time to expire at
196 * If negative then the relative time to expire at
197 * Dpc = If non-NULL then a dpc to be called when the timer expires
198 * RETURNS: True if the timer was already in the system timer queue
202 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
206 KeSetTimerEx (PKTIMER Timer,
207 LARGE_INTEGER DueTime,
211 * FUNCTION: Sets the absolute or relative interval at which a timer object
212 * is to be set to the signaled state and optionally supplies a
213 * CustomTimerDpc to be executed when the timer expires.
215 * Timer = Points to a previously initialized timer object
216 * DueTimer = If positive then absolute time to expire at
217 * If negative then the relative time to expire at
218 * Dpc = If non-NULL then a dpc to be called when the timer expires
219 * RETURNS: True if the timer was already in the system timer queue
225 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer);
226 KeAcquireSpinLock( &TimerListLock, &oldlvl );
229 if (DueTime.QuadPart < 0)
231 Timer->DueTime.QuadPart = system_time - DueTime.QuadPart;
235 Timer->DueTime.QuadPart = DueTime.QuadPart;
237 Timer->Period = Period;
238 Timer->Header.SignalState = FALSE;
239 if (Timer->TimerListEntry.Flink != NULL)
241 KeReleaseSpinLock(&TimerListLock, oldlvl);
244 InsertTailList(&TimerListHead,&Timer->TimerListEntry);
245 KeReleaseSpinLock(&TimerListLock, oldlvl);
251 KeCancelTimer (PKTIMER Timer)
253 * FUNCTION: Removes a timer from the system timer list
255 * Timer = timer to cancel
256 * RETURNS: True if the timer was running
262 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
264 KeRaiseIrql(HIGH_LEVEL, &oldlvl);
265 KeAcquireSpinLockAtDpcLevel( &TimerListLock );
267 if (Timer->TimerListEntry.Flink == NULL)
269 KeReleaseSpinLock(&TimerListLock, oldlvl);
272 RemoveEntryList(&Timer->TimerListEntry);
273 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
274 KeReleaseSpinLock(&TimerListLock, oldlvl);
280 KeReadStateTimer (PKTIMER Timer)
282 return(Timer->Header.SignalState);
286 KeInitializeTimer (PKTIMER Timer)
288 * FUNCTION: Initalizes a kernel timer object
290 * Timer = caller supplied storage for the timer
291 * NOTE: This function initializes a notification timer
294 KeInitializeTimerEx(Timer, NotificationTimer);
298 KeInitializeTimerEx (PKTIMER Timer,
301 * FUNCTION: Initializes a kernel timer object
303 * Timer = caller supplied storage for the timer
304 * Type = the type of timer (notification or synchronization)
305 * NOTE: When a notification type expires all waiting threads are released
306 * and the timer remains signalled until it is explicitly reset. When a
307 * syncrhonization timer expires its state is set to signalled until a
308 * single waiting thread is released and then the timer is reset.
313 if (Type == NotificationTimer)
315 IType = InternalNotificationTimer;
317 else if (Type == SynchronizationTimer)
319 IType = InternalSynchronizationTimer;
327 KeInitializeDispatcherHeader(&Timer->Header,
329 sizeof(KTIMER) / sizeof(ULONG),
331 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
335 KeQueryTickCount(PLARGE_INTEGER TickCount)
337 * FUNCTION: Returns the number of ticks since the system was booted
339 * TickCount (OUT) = Points to storage for the number of ticks
342 TickCount->QuadPart = KeTickCount;
346 HandleExpiredTimer(PKTIMER current)
348 DPRINT("HandleExpiredTime(current %x)\n",current);
349 if (current->Dpc != NULL)
351 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
352 current->Dpc, current->Dpc->DeferredRoutine);
353 KeInsertQueueDpc(current->Dpc,
356 DPRINT("Finished dpc routine\n");
358 KeAcquireDispatcherDatabaseLock(FALSE);
359 current->Header.SignalState = TRUE;
360 KeDispatcherObjectWake(¤t->Header);
361 KeReleaseDispatcherDatabaseLock(FALSE);
362 if (current->Period != 0)
364 current->DueTime.QuadPart +=
365 current->Period * SYSTEM_TIME_UNITS_PER_MSEC;
369 RemoveEntryList(¤t->TimerListEntry);
370 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
375 KeExpireTimers(PKDPC Dpc,
380 PLIST_ENTRY current_entry = NULL;
381 PKTIMER current = NULL;
382 ULONG Eip = (ULONG)Arg1;
384 DPRINT("KeExpireTimers()\n");
386 current_entry = TimerListHead.Flink;
388 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
390 while (current_entry != &TimerListHead)
392 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
394 current_entry = current_entry->Flink;
396 if (system_time >= current->DueTime.QuadPart)
398 HandleExpiredTimer(current);
402 KiAddProfileEvent(ProfileTime, Eip);
404 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
409 KiUpdateSystemTime(KIRQL oldIrql,
412 * FUNCTION: Handles a timer interrupt
417 if (TimerInitDone == FALSE)
422 * Increment the number of timers ticks
425 SharedUserData->TickCountLow++;
426 system_time = system_time + CLOCK_INCREMENT;
429 * Queue a DPC that will expire timers
431 KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)Eip, 0);
436 KeInitializeTimerImpl(VOID)
438 * FUNCTION: Initializes timer irq handling
439 * NOTE: This is only called once from main()
442 TIME_FIELDS TimeFields;
443 LARGE_INTEGER SystemBootTime;
445 DPRINT("KeInitializeTimerImpl()\n");
446 InitializeListHead(&TimerListHead);
447 KeInitializeSpinLock(&TimerListLock);
448 KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0);
449 TimerInitDone = TRUE;
451 * Calculate the starting time for the system clock
453 HalQueryRealTimeClock(&TimeFields);
454 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
455 boot_time=SystemBootTime.QuadPart;
456 system_time=boot_time;
458 DPRINT("Finished KeInitializeTimerImpl()\n");