2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/timer.c
5 * PURPOSE: Handle timers
6 * PROGRAMMER: David Welch (welch@mcmail.com)
11 /* NOTES ******************************************************************/
13 * System time units are 100-nanosecond intervals
16 /* INCLUDES ***************************************************************/
19 #include <ddk/ntddk.h>
22 #include <internal/debug.h>
24 /* TYPES *****************************************************************/
29 /* GLOBALS ****************************************************************/
34 static unsigned long long system_time
= 0;
37 * Number of timer interrupts since initialisation
39 static volatile unsigned long long ticks
=0;
42 * The increment in the system clock every timer tick (in system time units)
47 #define CLOCK_INCREMENT (54945055)
50 * PURPOSE: List of timers
52 static LIST_ENTRY timer_list_head
= {NULL
,NULL
};
53 static KSPIN_LOCK timer_list_lock
= {0,};
57 #define MICROSECONDS_PER_TICK (54945)
58 #define TICKS_TO_CALIBRATE (1)
59 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
61 static unsigned int loops_per_microsecond
= 100;
63 /* FUNCTIONS **************************************************************/
65 void KeCalibrateTimerLoop()
67 unsigned int start_tick
;
68 unsigned int end_tick
;
69 unsigned int nr_ticks
;
71 unsigned int microseconds
;
78 while (start_tick
== ticks
);
79 while (ticks
== (start_tick
+TICKS_TO_CALIBRATE
))
81 KeStallExecutionProcessor(1);
85 // DbgPrint("microseconds %d\n",microseconds);
87 if (microseconds
> (CALIBRATE_PERIOD
+1000))
89 loops_per_microsecond
= loops_per_microsecond
+ 1;
91 if (microseconds
< (CALIBRATE_PERIOD
-1000))
93 loops_per_microsecond
= loops_per_microsecond
- 1;
95 // DbgPrint("loops_per_microsecond %d\n",loops_per_microsecond);
101 NTSTATUS STDCALL
NtQueryTimerResolution (OUT PULONG MinimumResolution
,
102 OUT PULONG MaximumResolution
,
103 OUT PULONG ActualResolution
)
105 return(ZwQueryTimerResolution(MinimumResolution
,MaximumResolution
,
109 NTSTATUS STDCALL
ZwQueryTimerResolution (OUT PULONG MinimumResolution
,
110 OUT PULONG MaximumResolution
,
111 OUT PULONG ActualResolution
)
116 NTSTATUS STDCALL
NtSetTimerResolution(IN ULONG RequestedResolution
,
118 OUT PULONG ActualResolution
)
120 return(ZwSetTimerResolution(RequestedResolution
,
125 NTSTATUS STDCALL
ZwSetTimerResolution(IN ULONG RequestedResolution
,
127 OUT PULONG ActualResolution
)
132 NTSTATUS STDCALL
NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter
,
133 IN PLARGE_INTEGER Frequency
)
135 return(ZwQueryPerformanceCounter(Counter
,
139 NTSTATUS STDCALL
ZwQueryPerformanceCounter(IN PLARGE_INTEGER Counter
,
140 IN PLARGE_INTEGER Frequency
)
146 NTSTATUS
KeAddThreadTimeout(PKTHREAD Thread
, PLARGE_INTEGER Interval
)
148 KeInitializeTimer(&(Thread
->TimerBlock
));
149 KeSetTimer(&(Thread
->TimerBlock
),*Interval
,NULL
);
153 NTSTATUS STDCALL
NtDelayExecution(IN BOOLEAN Alertable
,
156 return(ZwDelayExecution(Alertable
,Interval
));
159 NTSTATUS STDCALL
ZwDelayExecution(IN BOOLEAN Alertable
,
165 NTSTATUS
KeDelayExecutionThread(KPROCESSOR_MODE WaitMode
,
167 PLARGE_INTEGER Interval
)
169 * FUNCTION: Puts the current thread into an alertable or nonalertable
170 * wait state for a given internal
172 * WaitMode = Processor mode in which the caller is waiting
173 * Altertable = Specifies if the wait is alertable
174 * Interval = Specifies the interval to wait
178 PKTHREAD CurrentThread
= KeGetCurrentThread();
179 KeAddThreadTimeout(CurrentThread
,Interval
);
180 return(KeWaitForSingleObject(&(CurrentThread
->TimerBlock
),Executive
,
181 KernelMode
,Alertable
,NULL
));
184 VOID
KeStallExecutionProcessor(ULONG MicroSeconds
)
187 for (i
=0; i
<(loops_per_microsecond
*MicroSeconds
) ;i
++)
193 static inline void ULLToLargeInteger(unsigned long long src
,
196 dest
->LowPart
= src
& 0xffffffff;
197 dest
->HighPart
= (src
>>32);
200 static inline void SLLToLargeInteger(signed long long src
,
205 dest
->LowPart
= src
& 0xffffffff;
206 dest
->HighPart
= (src
>>32);
211 dest
->LowPart
= src
& 0xffffffff;
212 dest
->HighPart
= -(src
>>32);
216 static inline signed long long LargeIntegerToSLL(PLARGE_INTEGER src
)
221 if (src
->HighPart
>= 0)
223 r
= r
| (((unsigned long long)src
->HighPart
)<<32);
227 r
= r
| (((unsigned long long)(-(src
->HighPart
)))<<32);
234 LARGE_INTEGER
KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq
)
236 * FUNCTION: Queries the finest grained running count avaiable in the system
238 * PerformanceFreq (OUT) = The routine stores the number of
239 * performance counters tick per second here
240 * RETURNS: The performance counter value in HERTZ
241 * NOTE: Returns the system tick count or the time-stamp on the pentium
244 PerformanceFreq
->HighPart
=0;
245 PerformanceFreq
->LowPart
=0;
248 ULONG
KeQueryTimeIncrement(VOID
)
250 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
251 * the system clock every time the clock interrupts
252 * RETURNS: The increment
255 return(CLOCK_INCREMENT
);
258 VOID
KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
260 * FUNCTION: Gets the current system time
262 * CurrentTime (OUT) = The routine stores the current time here
263 * NOTE: The time is the number of 100-nanosecond intervals since the
264 * 1st of January, 1601.
267 ULLToLargeInteger(system_time
,CurrentTime
);
270 NTSTATUS STDCALL
NtGetTickCount(PULONG UpTime
)
272 return(ZwGetTickCount(UpTime
));
275 NTSTATUS STDCALL
ZwGetTickCount(PULONG UpTime
)
280 BOOLEAN
KeSetTimer(PKTIMER Timer
, LARGE_INTEGER DueTime
, PKDPC Dpc
)
282 * FUNCTION: Sets the absolute or relative interval at which a timer object
283 * is to be set to the signaled state and optionally supplies a
284 * CustomTimerDpc to be executed when the timer expires.
286 * Timer = Points to a previously initialized timer object
287 * DueTimer = If positive then absolute time to expire at
288 * If negative then the relative time to expire at
289 * Dpc = If non-NULL then a dpc to be called when the timer expires
290 * RETURNS: True if the timer was already in the system timer queue
294 return(KeSetTimerEx(Timer
,DueTime
,0,Dpc
));
297 BOOLEAN
KeSetTimerEx(PKTIMER Timer
, LARGE_INTEGER DueTime
, LONG Period
,
300 * FUNCTION: Sets the absolute or relative interval at which a timer object
301 * is to be set to the signaled state and optionally supplies a
302 * CustomTimerDpc to be executed when the timer expires.
304 * Timer = Points to a previously initialized timer object
305 * DueTimer = If positive then absolute time to expire at
306 * If negative then the relative time to expire at
307 * Dpc = If non-NULL then a dpc to be called when the timer expires
308 * RETURNS: True if the timer was already in the system timer queue
314 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
317 Timer
->period
=Period
;
318 Timer
->expire_time
= LargeIntegerToSLL(&DueTime
);
319 if (Timer
->expire_time
< 0)
321 Timer
->expire_time
= system_time
- Timer
->expire_time
;
323 Timer
->signaled
= FALSE
;
326 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
329 InsertTailList(&timer_list_head
,&Timer
->entry
);
330 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
334 BOOLEAN
KeCancelTimer(PKTIMER Timer
)
336 * FUNCTION: Removes a timer from the system timer list
338 * Timer = timer to cancel
339 * RETURNS: True if the timer was running
345 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
351 RemoveEntryList(&Timer
->entry
);
352 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
356 BOOLEAN
KeReadStateTimer(PKTIMER Timer
)
358 return(Timer
->signaled
);
361 VOID
KeInitializeTimer(PKTIMER Timer
)
363 * FUNCTION: Initalizes a kernel timer object
365 * Timer = caller supplied storage for the timer
366 * NOTE: This function initializes a notification timer
369 KeInitializeTimerEx(Timer
,NotificationTimer
);
372 VOID
KeInitializeTimerEx(PKTIMER Timer
, TIMER_TYPE Type
)
374 * FUNCTION: Initializes a kernel timer object
376 * Timer = caller supplied storage for the timer
377 * Type = the type of timer (notification or synchronization)
378 * NOTE: When a notification type expires all waiting threads are released
379 * and the timer remains signalled until it is explicitly reset. When a
380 * syncrhonization timer expires its state is set to signalled until a
381 * single waiting thread is released and then the timer is reset.
384 Timer
->running
=FALSE
;
386 Timer
->signaled
=FALSE
;
389 VOID
KeQueryTickCount(PLARGE_INTEGER TickCount
)
391 * FUNCTION: Returns the number of ticks since the system was booted
393 * TickCount (OUT) = Points to storage for the number of ticks
396 ULLToLargeInteger(ticks
,TickCount
);
399 static void HandleExpiredTimer(PKTIMER current
)
401 if (current
->dpc
!=NULL
)
403 current
->dpc
->DeferredRoutine(current
->dpc
,
404 current
->dpc
->DeferredContext
,
405 current
->dpc
->SystemArgument1
,
406 current
->dpc
->SystemArgument2
);
408 current
->signaled
=TRUE
;
409 if (current
->period
!=0)
411 current
->expire_time
= current
->expire_time
+ current
->period
;
415 RemoveEntryList(¤t
->entry
);
416 current
->running
=FALSE
;
420 void KeExpireTimers(void)
422 PLIST_ENTRY current_entry
= timer_list_head
.Flink
;
423 PKTIMER current
= CONTAINING_RECORD(current_entry
,KTIMER
,entry
);
426 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
428 while (current_entry
!=(&timer_list_head
))
430 if (system_time
== current
->expire_time
)
432 HandleExpiredTimer(current
);
435 current_entry
= current_entry
->Flink
;
436 current
= CONTAINING_RECORD(current_entry
,KTIMER
,entry
);
438 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
441 VOID
KiTimerInterrupt(VOID
)
443 * FUNCTION: Handles a timer interrupt
447 char* vidmem
=(char *)physical_to_linear(0xb8000 + 160 - 16);
451 * Increment the number of timers ticks
454 system_time
= system_time
+ CLOCK_INCREMENT
;
457 * Display the tick count in the top left of the screen as a debugging
460 sprintf(str
,"%.8u",ticks
);
473 void InitializeTimer(void)
475 * FUNCTION: Initializes timer irq handling
476 * NOTE: This is only called once from main()
479 InitializeListHead(&timer_list_head
);
480 KeInitializeSpinLock(&timer_list_lock
);
483 * Calculate the starting time for the system clock