1 /* $Id: timer.c,v 1.48 2002/04/26 13:11:28 ekohl 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 ***************************************************************/
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
;
177 if ( UpTime
== NULL
)
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");