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>
23 #include <internal/debug.h>
25 /* TYPES *****************************************************************/
30 /* GLOBALS ****************************************************************/
35 static unsigned long long system_time
= 0;
38 * Number of timer interrupts since initialisation
40 volatile ULONGLONG KiTimerTicks
;
43 * The increment in the system clock every timer tick (in system time units)
49 #define CLOCK_INCREMENT (549450)
52 * PURPOSE: List of timers
54 static LIST_ENTRY timer_list_head
= {NULL
,NULL
};
55 static KSPIN_LOCK timer_list_lock
= {0,};
59 #define MICROSECONDS_PER_TICK (54945)
60 #define TICKS_TO_CALIBRATE (1)
61 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
62 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
64 static unsigned int loops_per_microsecond
= 100;
66 /* FUNCTIONS **************************************************************/
68 VOID
KeCalibrateTimerLoop(VOID
)
70 unsigned int start_tick
;
71 // unsigned int end_tick;
72 // unsigned int nr_ticks;
74 unsigned int microseconds
;
79 start_tick
= KiTimerTicks
;
81 while (start_tick
== KiTimerTicks
);
82 while (KiTimerTicks
== (start_tick
+TICKS_TO_CALIBRATE
))
84 KeStallExecutionProcessor(1);
88 // DbgPrint("microseconds %d\n",microseconds);
90 if (microseconds
> (CALIBRATE_PERIOD
+1000))
92 loops_per_microsecond
= loops_per_microsecond
+ 1;
94 if (microseconds
< (CALIBRATE_PERIOD
-1000))
96 loops_per_microsecond
= loops_per_microsecond
- 1;
98 // DbgPrint("loops_per_microsecond %d\n",loops_per_microsecond);
104 NTSTATUS STDCALL
NtQueryTimerResolution (OUT PULONG MinimumResolution
,
105 OUT PULONG MaximumResolution
,
106 OUT PULONG ActualResolution
)
108 return(ZwQueryTimerResolution(MinimumResolution
,MaximumResolution
,
112 NTSTATUS STDCALL
ZwQueryTimerResolution (OUT PULONG MinimumResolution
,
113 OUT PULONG MaximumResolution
,
114 OUT PULONG ActualResolution
)
119 NTSTATUS STDCALL
NtSetTimerResolution(IN ULONG RequestedResolution
,
121 OUT PULONG ActualResolution
)
123 return(ZwSetTimerResolution(RequestedResolution
,
128 NTSTATUS STDCALL
ZwSetTimerResolution(IN ULONG RequestedResolution
,
130 OUT PULONG ActualResolution
)
135 NTSTATUS STDCALL
NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter
,
136 IN PLARGE_INTEGER Frequency
)
138 return(ZwQueryPerformanceCounter(Counter
,
142 NTSTATUS STDCALL
ZwQueryPerformanceCounter(IN PLARGE_INTEGER Counter
,
143 IN PLARGE_INTEGER Frequency
)
149 NTSTATUS
KeAddThreadTimeout(PKTHREAD Thread
, PLARGE_INTEGER Interval
)
151 assert(Thread
!= NULL
);
152 assert(Interval
!= NULL
);
154 DPRINT("KeAddThreadTimeout(Thread %x, Interval %x)\n",Thread
,Interval
);
156 KeInitializeTimer(&(Thread
->TimerBlock
));
157 KeSetTimer(&(Thread
->TimerBlock
),*Interval
,NULL
);
159 DPRINT("Thread->TimerBlock.entry.Flink %x\n",
160 Thread
->TimerBlock
.entry
.Flink
);
162 return STATUS_SUCCESS
;
166 NTSTATUS STDCALL
NtDelayExecution(IN BOOLEAN Alertable
,
169 return(ZwDelayExecution(Alertable
,Interval
));
172 NTSTATUS STDCALL
ZwDelayExecution(IN BOOLEAN Alertable
,
178 NTSTATUS
KeDelayExecutionThread(KPROCESSOR_MODE WaitMode
,
180 PLARGE_INTEGER Interval
)
182 * FUNCTION: Puts the current thread into an alertable or nonalertable
183 * wait state for a given internal
185 * WaitMode = Processor mode in which the caller is waiting
186 * Altertable = Specifies if the wait is alertable
187 * Interval = Specifies the interval to wait
191 PKTHREAD CurrentThread
= KeGetCurrentThread();
192 KeAddThreadTimeout(CurrentThread
,Interval
);
193 return(KeWaitForSingleObject(&(CurrentThread
->TimerBlock
),Executive
,
194 KernelMode
,Alertable
,NULL
));
197 VOID
KeStallExecutionProcessor(ULONG MicroSeconds
)
200 for (i
=0; i
<(loops_per_microsecond
*MicroSeconds
) ;i
++)
207 static inline void ULLToLargeInteger(unsigned long long src
,
210 dest
->LowPart
= src
& 0xffffffff;
211 dest
->HighPart
= (src
>>32);
214 static inline void SLLToLargeInteger(signed long long src
,
219 dest
->LowPart
= src
& 0xffffffff;
220 dest
->HighPart
= (src
>>32);
225 dest
->LowPart
= src
& 0xffffffff;
226 dest
->HighPart
= -(src
>>32);
230 static inline signed long long LargeIntegerToSLL(PLARGE_INTEGER src
)
235 if (src
->HighPart
>= 0)
237 r
= r
| (((unsigned long long)src
->HighPart
)<<32);
241 r
= r
| (((unsigned long long)(-(src
->HighPart
)))<<32);
250 LARGE_INTEGER
KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFreq
)
252 * FUNCTION: Queries the finest grained running count avaiable in the system
254 * PerformanceFreq (OUT) = The routine stores the number of
255 * performance counters tick per second here
256 * RETURNS: The performance counter value in HERTZ
257 * NOTE: Returns the system tick count or the time-stamp on the pentium
260 if (PerformanceFreq
!= NULL
)
262 LARGE_INTEGER_QUAD_PART(*PerformanceFreq
) = 0;
265 return *PerformanceFreq
;
268 ULONG
KeQueryTimeIncrement(VOID
)
270 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
271 * the system clock every time the clock interrupts
272 * RETURNS: The increment
275 return(CLOCK_INCREMENT
);
278 VOID
KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
280 * FUNCTION: Gets the current system time
282 * CurrentTime (OUT) = The routine stores the current time here
283 * NOTE: The time is the number of 100-nanosecond intervals since the
284 * 1st of January, 1601.
287 LARGE_INTEGER_QUAD_PART(*CurrentTime
) = system_time
;
290 NTSTATUS STDCALL
NtGetTickCount(PULONG UpTime
)
292 return(ZwGetTickCount(UpTime
));
295 NTSTATUS STDCALL
ZwGetTickCount(PULONG UpTime
)
300 BOOLEAN
KeSetTimer(PKTIMER Timer
, LARGE_INTEGER DueTime
, PKDPC Dpc
)
302 * FUNCTION: Sets the absolute or relative interval at which a timer object
303 * is to be set to the signaled state and optionally supplies a
304 * CustomTimerDpc to be executed when the timer expires.
306 * Timer = Points to a previously initialized timer object
307 * DueTimer = If positive then absolute time to expire at
308 * If negative then the relative time to expire at
309 * Dpc = If non-NULL then a dpc to be called when the timer expires
310 * RETURNS: True if the timer was already in the system timer queue
314 return(KeSetTimerEx(Timer
,DueTime
,0,Dpc
));
317 BOOLEAN
KeSetTimerEx(PKTIMER Timer
, LARGE_INTEGER DueTime
, LONG Period
,
320 * FUNCTION: Sets the absolute or relative interval at which a timer object
321 * is to be set to the signaled state and optionally supplies a
322 * CustomTimerDpc to be executed when the timer expires.
324 * Timer = Points to a previously initialized timer object
325 * DueTimer = If positive then absolute time to expire at
326 * If negative then the relative time to expire at
327 * Dpc = If non-NULL then a dpc to be called when the timer expires
328 * RETURNS: True if the timer was already in the system timer queue
334 DPRINT("KeSetTimerEx(Timer %x)\n",Timer
);
336 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
339 Timer
->period
=Period
;
340 Timer
->expire_time
= (*(long long int *)&DueTime
);
341 if (Timer
->expire_time
< 0)
343 Timer
->expire_time
= system_time
+ (-Timer
->expire_time
);
345 DPRINT("System:%ld:%ld Expire:%d:%d Period:%d\n",
346 (unsigned long) (system_time
& 0xffffffff),
347 (unsigned long) ((system_time
>> 32) & 0xffffffff),
348 (unsigned long) (Timer
->expire_time
& 0xffffffff),
349 (unsigned long) ((Timer
->expire_time
>> 32) & 0xffffffff),
351 Timer
->signaled
= FALSE
;
354 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
357 DPRINT("Inserting %x in list\n",&Timer
->entry
);
358 DPRINT("Timer->entry.Flink %x\n",Timer
->entry
.Flink
);
360 InsertTailList(&timer_list_head
,&Timer
->entry
);
361 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
366 BOOLEAN
KeCancelTimer(PKTIMER Timer
)
368 * FUNCTION: Removes a timer from the system timer list
370 * Timer = timer to cancel
371 * RETURNS: True if the timer was running
377 DPRINT("KeCancelTimer(Timer %x)\n",Timer
);
379 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
383 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
386 RemoveEntryList(&Timer
->entry
);
387 Timer
->running
= FALSE
;
388 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
393 BOOLEAN
KeReadStateTimer(PKTIMER Timer
)
395 return Timer
->signaled
;
398 VOID
KeInitializeTimer(PKTIMER Timer
)
400 * FUNCTION: Initalizes a kernel timer object
402 * Timer = caller supplied storage for the timer
403 * NOTE: This function initializes a notification timer
406 KeInitializeTimerEx(Timer
,NotificationTimer
);
409 VOID
KeInitializeTimerEx(PKTIMER Timer
, TIMER_TYPE Type
)
411 * FUNCTION: Initializes a kernel timer object
413 * Timer = caller supplied storage for the timer
414 * Type = the type of timer (notification or synchronization)
415 * NOTE: When a notification type expires all waiting threads are released
416 * and the timer remains signalled until it is explicitly reset. When a
417 * syncrhonization timer expires its state is set to signalled until a
418 * single waiting thread is released and then the timer is reset.
421 Timer
->running
=FALSE
;
423 Timer
->signaled
=FALSE
;
426 VOID
KeQueryTickCount(PLARGE_INTEGER TickCount
)
428 * FUNCTION: Returns the number of ticks since the system was booted
430 * TickCount (OUT) = Points to storage for the number of ticks
433 LARGE_INTEGER_QUAD_PART(*TickCount
) = KiTimerTicks
;
436 static void HandleExpiredTimer(PKTIMER current
)
438 DPRINT("HandleExpiredTime(current %x)\n",current
);
439 if (current
->dpc
!=NULL
)
441 DPRINT("current->dpc->DeferredRoutine %x\n",
442 current
->dpc
->DeferredRoutine
);
443 current
->dpc
->DeferredRoutine(current
->dpc
,
444 current
->dpc
->DeferredContext
,
445 current
->dpc
->SystemArgument1
,
446 current
->dpc
->SystemArgument2
);
447 DPRINT("Finished dpc routine\n");
449 current
->signaled
=TRUE
;
450 if (current
->period
!= 0)
452 DPRINT("System:%ld:%ld Expire:%d:%d Period:%d\n",
453 (unsigned long) (system_time
& 0xffffffff),
454 (unsigned long) ((system_time
>> 32) & 0xffffffff),
455 (unsigned long) (current
->expire_time
& 0xffffffff),
456 (unsigned long) ((current
->expire_time
>> 32) & 0xffffffff),
458 current
->expire_time
+= current
->period
* SYSTEM_TIME_UNITS_PER_MSEC
;
462 RemoveEntryList(¤t
->entry
);
463 current
->running
=FALSE
;
467 void KeExpireTimers(void)
469 PLIST_ENTRY current_entry
= timer_list_head
.Flink
;
470 PKTIMER current
= CONTAINING_RECORD(current_entry
,KTIMER
,entry
);
473 DPRINT("KeExpireTimers()\n");
474 DPRINT("&timer_list_head %x\n",&timer_list_head
);
475 DPRINT("current_entry %x\n",current_entry
);
476 DPRINT("current_entry->Flink %x\n",current_entry
->Flink
);
477 DPRINT("current_entry->Flink->Flink %x\n",current_entry
->Flink
->Flink
);
479 KeAcquireSpinLock(&timer_list_lock
,&oldlvl
);
481 while (current_entry
!=(&timer_list_head
))
483 if (system_time
>= current
->expire_time
)
485 HandleExpiredTimer(current
);
488 current_entry
= current_entry
->Flink
;
489 current
= CONTAINING_RECORD(current_entry
,KTIMER
,entry
);
491 KeReleaseSpinLock(&timer_list_lock
,oldlvl
);
492 DPRINT("Finished KeExpireTimers()\n");
495 extern unsigned int nr_used_blocks
;
496 extern unsigned int EiFreeNonPagedPool
;
497 extern unsigned int EiUsedNonPagedPool
;
499 BOOLEAN
KiTimerInterrupt(VOID
)
501 * FUNCTION: Handles a timer interrupt
505 char* vidmem
=(char *)physical_to_linear(0xb8000 + 160 - 36);
508 extern ULONG PiNrThreads
;
509 extern ULONG EiNrUsedBlocks
;
512 * Increment the number of timers ticks
515 system_time
= system_time
+ CLOCK_INCREMENT
;
518 * Display the tick count in the top left of the screen as a debugging
521 // sprintf(str,"%.8u %.8u",nr_used_blocks,ticks);
522 if ((EiFreeNonPagedPool
+ EiUsedNonPagedPool
) == 0)
528 x
= (EiFreeNonPagedPool
* 100) /
529 (EiFreeNonPagedPool
+ EiUsedNonPagedPool
);
530 y
= (EiUsedNonPagedPool
* 100) /
531 (EiFreeNonPagedPool
+ EiUsedNonPagedPool
);
533 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,ticks);
534 memset(str
, 0, sizeof(str
));
535 // sprintf(str,"%.8u %.8u",EiNrUsedBlocks,KiTimerTicks);
536 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,EiUsedNonPagedPool);
537 sprintf(str
,"%.8u %.8u",PiNrThreads
,KiTimerTicks
);
550 void InitializeTimer(void)
552 * FUNCTION: Initializes timer irq handling
553 * NOTE: This is only called once from main()
556 InitializeListHead(&timer_list_head
);
557 KeInitializeSpinLock(&timer_list_lock
);
560 * Calculate the starting time for the system clock