1 /* $Id: timer.c,v 1.52 2002/09/07 15:12:57 chorns 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 ***************************************************************/
23 #include <internal/debug.h>
26 /* TYPES *****************************************************************/
30 /* GLOBALS ****************************************************************/
35 static unsigned long long boot_time
= 0;
36 static unsigned long long system_time
= 0;
39 * Number of timer interrupts since initialisation
41 volatile ULONGLONG KeTickCount
= 0;
42 volatile ULONG KiRawTicks
= 0;
45 * The increment in the system clock every timer tick (in system time units)
51 #define CLOCK_INCREMENT (100000)
54 * PURPOSE: List of timers
56 static LIST_ENTRY TimerListHead
;
57 static KSPIN_LOCK TimerListLock
;
58 static KDPC ExpireTimerDpc
;
60 /* must raise IRQL to HIGH_LEVEL and grab spin lock there, to sync with ISR */
62 extern ULONG PiNrRunnableThreads
;
64 #define MICROSECONDS_PER_TICK (10000)
65 #define TICKS_TO_CALIBRATE (1)
66 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
67 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
69 static BOOLEAN TimerInitDone
= FALSE
;
71 /* FUNCTIONS **************************************************************/
75 NtQueryTimerResolution(OUT PULONG MinimumResolution
,
76 OUT PULONG MaximumResolution
,
77 OUT PULONG ActualResolution
)
84 NtSetTimerResolution(IN ULONG RequestedResolution
,
86 OUT PULONG ActualResolution
)
93 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter
,
94 IN PLARGE_INTEGER Frequency
)
101 NtDelayExecution(IN ULONG Alertable
,
105 LARGE_INTEGER Timeout
;
107 Timeout
= *((PLARGE_INTEGER
)Interval
);
108 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
109 Alertable
, Internal
, Timeout
);
111 DPRINT("Execution delay is %d/%d\n",
112 Timeout
.u
.HighPart
, Timeout
.u
.LowPart
);
113 Status
= KeDelayExecutionThread(UserMode
, Alertable
, &Timeout
);
119 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode
,
121 PLARGE_INTEGER Interval
)
123 * FUNCTION: Puts the current thread into an alertable or nonalertable
124 * wait state for a given internal
126 * WaitMode = Processor mode in which the caller is waiting
127 * Altertable = Specifies if the wait is alertable
128 * Interval = Specifies the interval to wait
132 PKTHREAD Thread
= KeGetCurrentThread();
134 KeInitializeTimer(&Thread
->Timer
);
135 KeSetTimer(&Thread
->Timer
, *Interval
, NULL
);
136 return (KeWaitForSingleObject(&Thread
->Timer
,
145 KeQueryTimeIncrement(VOID
)
147 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
148 * the system clock every time the clock interrupts
149 * RETURNS: The increment
152 return(CLOCK_INCREMENT
);
157 KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
159 * FUNCTION: Gets the current system time
161 * CurrentTime (OUT) = The routine stores the current time here
162 * NOTE: The time is the number of 100-nanosecond intervals since the
163 * 1st of January, 1601.
166 CurrentTime
->QuadPart
= system_time
;
171 NtGetTickCount (PULONG UpTime
)
173 LARGE_INTEGER TickCount
;
175 return(STATUS_INVALID_PARAMETER
);
176 KeQueryTickCount(&TickCount
);
177 *UpTime
= TickCount
.u
.LowPart
;
178 return (STATUS_SUCCESS
);
183 KeSetTimer (PKTIMER Timer
,
184 LARGE_INTEGER DueTime
,
187 * FUNCTION: Sets the absolute or relative interval at which a timer object
188 * is to be set to the signaled state and optionally supplies a
189 * CustomTimerDpc to be executed when the timer expires.
191 * Timer = Points to a previously initialized timer object
192 * DueTimer = If positive then absolute time to expire at
193 * If negative then the relative time to expire at
194 * Dpc = If non-NULL then a dpc to be called when the timer expires
195 * RETURNS: True if the timer was already in the system timer queue
199 return(KeSetTimerEx(Timer
, DueTime
, 0, Dpc
));
203 KeSetTimerEx (PKTIMER Timer
,
204 LARGE_INTEGER DueTime
,
208 * FUNCTION: Sets the absolute or relative interval at which a timer object
209 * is to be set to the signaled state and optionally supplies a
210 * CustomTimerDpc to be executed when the timer expires.
212 * Timer = Points to a previously initialized timer object
213 * DueTimer = If positive then absolute time to expire at
214 * If negative then the relative time to expire at
215 * Dpc = If non-NULL then a dpc to be called when the timer expires
216 * RETURNS: True if the timer was already in the system timer queue
222 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer
);
223 KeAcquireSpinLock( &TimerListLock
, &oldlvl
);
226 if (DueTime
.QuadPart
< 0)
228 Timer
->DueTime
.QuadPart
= system_time
- DueTime
.QuadPart
;
232 Timer
->DueTime
.QuadPart
= DueTime
.QuadPart
;
234 Timer
->Period
= Period
;
235 Timer
->Header
.SignalState
= FALSE
;
236 if (Timer
->TimerListEntry
.Flink
!= NULL
)
238 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
241 InsertTailList(&TimerListHead
,&Timer
->TimerListEntry
);
242 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
248 KeCancelTimer (PKTIMER Timer
)
250 * FUNCTION: Removes a timer from the system timer list
252 * Timer = timer to cancel
253 * RETURNS: True if the timer was running
259 DPRINT("KeCancelTimer(Timer %x)\n",Timer
);
261 KeRaiseIrql(HIGH_LEVEL
, &oldlvl
);
262 KeAcquireSpinLockAtDpcLevel( &TimerListLock
);
264 if (Timer
->TimerListEntry
.Flink
== NULL
)
266 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
269 RemoveEntryList(&Timer
->TimerListEntry
);
270 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
271 KeReleaseSpinLock(&TimerListLock
, oldlvl
);
277 KeReadStateTimer (PKTIMER Timer
)
279 return(Timer
->Header
.SignalState
);
283 KeInitializeTimer (PKTIMER Timer
)
285 * FUNCTION: Initalizes a kernel timer object
287 * Timer = caller supplied storage for the timer
288 * NOTE: This function initializes a notification timer
291 KeInitializeTimerEx(Timer
, NotificationTimer
);
295 KeInitializeTimerEx (PKTIMER Timer
,
298 * FUNCTION: Initializes a kernel timer object
300 * Timer = caller supplied storage for the timer
301 * Type = the type of timer (notification or synchronization)
302 * NOTE: When a notification type expires all waiting threads are released
303 * and the timer remains signalled until it is explicitly reset. When a
304 * syncrhonization timer expires its state is set to signalled until a
305 * single waiting thread is released and then the timer is reset.
310 if (Type
== NotificationTimer
)
312 IType
= InternalNotificationTimer
;
314 else if (Type
== SynchronizationTimer
)
316 IType
= InternalSynchronizationTimer
;
324 KeInitializeDispatcherHeader(&Timer
->Header
,
326 sizeof(KTIMER
) / sizeof(ULONG
),
328 Timer
->TimerListEntry
.Flink
= Timer
->TimerListEntry
.Blink
= NULL
;
332 KeQueryTickCount(PLARGE_INTEGER TickCount
)
334 * FUNCTION: Returns the number of ticks since the system was booted
336 * TickCount (OUT) = Points to storage for the number of ticks
339 TickCount
->QuadPart
= KeTickCount
;
343 HandleExpiredTimer(PKTIMER current
)
345 DPRINT("HandleExpiredTime(current %x)\n",current
);
346 if (current
->Dpc
!= NULL
)
348 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
349 current
->Dpc
, current
->Dpc
->DeferredRoutine
);
350 KeInsertQueueDpc(current
->Dpc
,
353 DPRINT("Finished dpc routine\n");
355 KeAcquireDispatcherDatabaseLock(FALSE
);
356 current
->Header
.SignalState
= TRUE
;
357 KeDispatcherObjectWake(¤t
->Header
);
358 KeReleaseDispatcherDatabaseLock(FALSE
);
359 if (current
->Period
!= 0)
361 current
->DueTime
.QuadPart
+=
362 current
->Period
* SYSTEM_TIME_UNITS_PER_MSEC
;
366 RemoveEntryList(¤t
->TimerListEntry
);
367 current
->TimerListEntry
.Flink
= current
->TimerListEntry
.Blink
= NULL
;
372 KeExpireTimers(PKDPC Dpc
,
377 PLIST_ENTRY current_entry
= NULL
;
378 PKTIMER current
= NULL
;
379 ULONG Eip
= (ULONG
)Arg1
;
381 DPRINT("KeExpireTimers()\n");
383 current_entry
= TimerListHead
.Flink
;
385 KeAcquireSpinLockAtDpcLevel(&TimerListLock
);
387 while (current_entry
!= &TimerListHead
)
389 current
= CONTAINING_RECORD(current_entry
, KTIMER
, TimerListEntry
);
391 current_entry
= current_entry
->Flink
;
393 if (system_time
>= current
->DueTime
.QuadPart
)
395 HandleExpiredTimer(current
);
399 KiAddProfileEvent(ProfileTime
, Eip
);
401 KeReleaseSpinLockFromDpcLevel(&TimerListLock
);
406 KiUpdateSystemTime(KIRQL oldIrql
,
409 * FUNCTION: Handles a timer interrupt
414 if (TimerInitDone
== FALSE
)
419 * Increment the number of timers ticks
422 SharedUserData
->TickCountLow
++;
423 system_time
= system_time
+ CLOCK_INCREMENT
;
426 * Queue a DPC that will expire timers
428 KeInsertQueueDpc(&ExpireTimerDpc
, (PVOID
)Eip
, 0);
433 KeInitializeTimerImpl(VOID
)
435 * FUNCTION: Initializes timer irq handling
436 * NOTE: This is only called once from main()
439 TIME_FIELDS TimeFields
;
440 LARGE_INTEGER SystemBootTime
;
442 DPRINT("KeInitializeTimerImpl()\n");
443 InitializeListHead(&TimerListHead
);
444 KeInitializeSpinLock(&TimerListLock
);
445 KeInitializeDpc(&ExpireTimerDpc
, KeExpireTimers
, 0);
446 TimerInitDone
= TRUE
;
448 * Calculate the starting time for the system clock
450 HalQueryRealTimeClock(&TimeFields
);
451 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
452 boot_time
=SystemBootTime
.QuadPart
;
453 system_time
=boot_time
;
455 DPRINT("Finished KeInitializeTimerImpl()\n");