Added working set functions
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /* $Id: timer.c,v 1.31 2000/07/04 08:52:40 dwelch Exp $
2 *
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)
8 * UPDATE HISTORY:
9 * 28/05/98: Created
10 * 12/3/99: Phillip Susi: enabled the timers, fixed spin lock
11 */
12
13 /* NOTES ******************************************************************/
14 /*
15 * System time units are 100-nanosecond intervals
16 */
17
18 /* INCLUDES ***************************************************************/
19
20 #include <limits.h>
21 #include <ddk/ntddk.h>
22 #include <string.h>
23 #include <internal/string.h>
24 #include <stdio.h>
25 #include <internal/ke.h>
26 #include <internal/id.h>
27 #include <internal/ps.h>
28
29 #define NDEBUG
30 #include <internal/debug.h>
31
32 /* TYPES *****************************************************************/
33
34 #define TIMER_IRQ 0
35
36 /* GLOBALS ****************************************************************/
37
38 #define IDMAP_BASE (0xd0000000)
39
40 /*
41 * Return a linear address which can be used to access the physical memory
42 * starting at x
43 */
44 extern inline unsigned int physical_to_linear(unsigned int x)
45 {
46 return(x+IDMAP_BASE);
47 }
48
49 extern inline unsigned int linear_to_physical(unsigned int x)
50 {
51 return(x-IDMAP_BASE);
52 }
53
54 /*
55 * Current time
56 */
57 static unsigned long long boot_time = 0;
58 static unsigned long long system_time = 0;
59
60 /*
61 * Number of timer interrupts since initialisation
62 */
63 volatile ULONGLONG KiTimerTicks;
64
65 /*
66 * The increment in the system clock every timer tick (in system time units)
67 *
68 * = (1/18.2)*10^9
69 *
70 * RJJ was 54945055
71 */
72 #define CLOCK_INCREMENT (549450)
73
74 /*
75 * PURPOSE: List of timers
76 */
77 static LIST_ENTRY TimerListHead;
78 static KSPIN_LOCK TimerListLock;
79
80 /* must raise IRQL to HIGH_LEVEL and grab spin lock there, to sync with ISR */
81
82 extern ULONG PiNrRunnableThreads;
83
84 #define MICROSECONDS_PER_TICK (54945)
85 #define TICKS_TO_CALIBRATE (1)
86 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
87 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
88
89 static BOOLEAN TimerInitDone = FALSE;
90
91 /* FUNCTIONS **************************************************************/
92
93
94 NTSTATUS STDCALL NtQueryTimerResolution(OUT PULONG MinimumResolution,
95 OUT PULONG MaximumResolution,
96 OUT PULONG ActualResolution)
97 {
98 UNIMPLEMENTED;
99 }
100
101
102 NTSTATUS STDCALL NtSetTimerResolution(IN ULONG RequestedResolution,
103 IN BOOL SetOrUnset,
104 OUT PULONG ActualResolution)
105 {
106 UNIMPLEMENTED;
107 }
108
109
110 NTSTATUS STDCALL NtQueryPerformanceCounter (IN PLARGE_INTEGER Counter,
111 IN PLARGE_INTEGER Frequency)
112 {
113 UNIMPLEMENTED;
114 }
115
116
117 NTSTATUS KeAddThreadTimeout(PKTHREAD Thread, PLARGE_INTEGER Interval)
118 {
119 assert(Thread != NULL);
120 assert(Interval != NULL);
121
122 DPRINT("KeAddThreadTimeout(Thread %x, Interval %x)\n",Thread,Interval);
123
124 KeInitializeTimer(&(Thread->Timer));
125 KeSetTimer( &(Thread->Timer), *Interval, &Thread->TimerDpc );
126
127 DPRINT("Thread->Timer.entry.Flink %x\n",
128 Thread->Timer.TimerListEntry.Flink);
129
130 return STATUS_SUCCESS;
131 }
132
133
134 NTSTATUS STDCALL NtDelayExecution(IN ULONG Alertable,
135 IN TIME* Interval)
136 {
137 NTSTATUS Status;
138 PLARGE_INTEGER IntervalP;
139
140 IntervalP = (PLARGE_INTEGER)Interval;
141
142 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
143 Alertable, Internal, IntervalP);
144
145 Status = KeDelayExecutionThread(UserMode, Alertable, IntervalP);
146 return(Status);
147 }
148
149
150 NTSTATUS
151 STDCALL
152 KeDelayExecutionThread (
153 KPROCESSOR_MODE WaitMode,
154 BOOLEAN Alertable,
155 PLARGE_INTEGER Interval
156 )
157 /*
158 * FUNCTION: Puts the current thread into an alertable or nonalertable
159 * wait state for a given internal
160 * ARGUMENTS:
161 * WaitMode = Processor mode in which the caller is waiting
162 * Altertable = Specifies if the wait is alertable
163 * Interval = Specifies the interval to wait
164 * RETURNS: Status
165 */
166 {
167 PKTHREAD CurrentThread = KeGetCurrentThread();
168 KeAddThreadTimeout(CurrentThread, Interval);
169 return (KeWaitForSingleObject(&(CurrentThread->Timer),
170 Executive,
171 KernelMode,
172 Alertable,
173 NULL));
174 }
175
176 ULONG
177 STDCALL
178 KeQueryTimeIncrement (
179 VOID
180 )
181 /*
182 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
183 * the system clock every time the clock interrupts
184 * RETURNS: The increment
185 */
186 {
187 return(CLOCK_INCREMENT);
188 }
189
190 VOID
191 STDCALL
192 KeQuerySystemTime (
193 PLARGE_INTEGER CurrentTime
194 )
195 /*
196 * FUNCTION: Gets the current system time
197 * ARGUMENTS:
198 * CurrentTime (OUT) = The routine stores the current time here
199 * NOTE: The time is the number of 100-nanosecond intervals since the
200 * 1st of January, 1601.
201 */
202 {
203 CurrentTime->QuadPart = system_time;
204 }
205
206
207 NTSTATUS
208 STDCALL
209 NtGetTickCount (
210 PULONG UpTime
211 )
212 {
213 LARGE_INTEGER TickCount;
214 if ( UpTime == NULL )
215 return(STATUS_INVALID_PARAMETER);
216 KeQueryTickCount(&TickCount);
217 *UpTime = TickCount.u.LowPart;
218 return (STATUS_SUCCESS);
219 }
220
221
222 BOOLEAN
223 STDCALL
224 KeSetTimer (
225 PKTIMER Timer,
226 LARGE_INTEGER DueTime,
227 PKDPC Dpc
228 )
229 /*
230 * FUNCTION: Sets the absolute or relative interval at which a timer object
231 * is to be set to the signaled state and optionally supplies a
232 * CustomTimerDpc to be executed when the timer expires.
233 * ARGUMENTS:
234 * Timer = Points to a previously initialized timer object
235 * DueTimer = If positive then absolute time to expire at
236 * If negative then the relative time to expire at
237 * Dpc = If non-NULL then a dpc to be called when the timer expires
238 * RETURNS: True if the timer was already in the system timer queue
239 * False otherwise
240 */
241 {
242 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
243 }
244
245 BOOLEAN
246 STDCALL
247 KeSetTimerEx (
248 PKTIMER Timer,
249 LARGE_INTEGER DueTime,
250 LONG Period,
251 PKDPC Dpc
252 )
253 /*
254 * FUNCTION: Sets the absolute or relative interval at which a timer object
255 * is to be set to the signaled state and optionally supplies a
256 * CustomTimerDpc to be executed when the timer expires.
257 * ARGUMENTS:
258 * Timer = Points to a previously initialized timer object
259 * DueTimer = If positive then absolute time to expire at
260 * If negative then the relative time to expire at
261 * Dpc = If non-NULL then a dpc to be called when the timer expires
262 * RETURNS: True if the timer was already in the system timer queue
263 * False otherwise
264 */
265 {
266 KIRQL oldlvl;
267
268 DPRINT("KeSetTimerEx(Timer %x)\n",Timer);
269
270 KeRaiseIrql( HIGH_LEVEL, &oldlvl );
271 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
272
273 Timer->Dpc = Dpc;
274 if (DueTime.QuadPart < 0)
275 {
276 Timer->DueTime.QuadPart = system_time + (-(DueTime.QuadPart));
277 }
278 else
279 {
280 Timer->DueTime.QuadPart = DueTime.QuadPart;
281 }
282 Timer->Period = Period;
283 Timer->Header.SignalState = FALSE;
284 if (Timer->TimerListEntry.Flink != NULL)
285 {
286 KeReleaseSpinLock(&TimerListLock, oldlvl);
287 return(TRUE);
288 }
289 InsertTailList(&TimerListHead,&Timer->TimerListEntry);
290 KeReleaseSpinLock(&TimerListLock, oldlvl);
291
292 return FALSE;
293 }
294
295 BOOLEAN
296 STDCALL
297 KeCancelTimer (
298 PKTIMER Timer
299 )
300 /*
301 * FUNCTION: Removes a timer from the system timer list
302 * ARGUMENTS:
303 * Timer = timer to cancel
304 * RETURNS: True if the timer was running
305 * False otherwise
306 */
307 {
308 KIRQL oldlvl;
309
310 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
311
312 KeRaiseIrql( HIGH_LEVEL, &oldlvl );
313 KeAcquireSpinLockAtDpcLevel( &TimerListLock );
314
315 if (Timer->TimerListEntry.Flink == NULL)
316 {
317 KeReleaseSpinLock(&TimerListLock, oldlvl);
318 return(FALSE);
319 }
320 RemoveEntryList(&Timer->TimerListEntry);
321 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
322 KeReleaseSpinLock(&TimerListLock, oldlvl);
323
324 return(TRUE);
325 }
326
327 BOOLEAN
328 STDCALL
329 KeReadStateTimer (
330 PKTIMER Timer
331 )
332 {
333 return(Timer->Header.SignalState);
334 }
335
336 VOID
337 STDCALL
338 KeInitializeTimer (
339 PKTIMER Timer
340 )
341 /*
342 * FUNCTION: Initalizes a kernel timer object
343 * ARGUMENTS:
344 * Timer = caller supplied storage for the timer
345 * NOTE: This function initializes a notification timer
346 */
347 {
348 KeInitializeTimerEx(Timer,NotificationTimer);
349 }
350
351 VOID
352 STDCALL
353 KeInitializeTimerEx (
354 PKTIMER Timer,
355 TIMER_TYPE Type
356 )
357 /*
358 * FUNCTION: Initializes a kernel timer object
359 * ARGUMENTS:
360 * Timer = caller supplied storage for the timer
361 * Type = the type of timer (notification or synchronization)
362 * NOTE: When a notification type expires all waiting threads are released
363 * and the timer remains signalled until it is explicitly reset. When a
364 * syncrhonization timer expires its state is set to signalled until a
365 * single waiting thread is released and then the timer is reset.
366 */
367 {
368 ULONG IType;
369
370 if (Type == NotificationTimer)
371 {
372 IType = InternalNotificationTimer;
373 }
374 else if (Type == SynchronizationTimer)
375 {
376 IType = InternalSynchronizationTimer;
377 }
378 else
379 {
380 assert(FALSE);
381 return;
382 }
383
384 KeInitializeDispatcherHeader(&Timer->Header,
385 IType,
386 sizeof(KTIMER) / sizeof(ULONG),
387 FALSE);
388 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
389 }
390
391 VOID
392 STDCALL
393 KeQueryTickCount (
394 PLARGE_INTEGER TickCount
395 )
396 /*
397 * FUNCTION: Returns the number of ticks since the system was booted
398 * ARGUMENTS:
399 * TickCount (OUT) = Points to storage for the number of ticks
400 */
401 {
402 TickCount->QuadPart = KiTimerTicks;
403 }
404
405 static void HandleExpiredTimer(PKTIMER current)
406 {
407 DPRINT("HandleExpiredTime(current %x)\n",current);
408 if (current->Dpc != NULL)
409 {
410 DPRINT("current->Dpc %x current->Dpc->DeferredRoutine %x\n",
411 current->Dpc, current->Dpc->DeferredRoutine);
412 KeInsertQueueDpc(current->Dpc,
413 NULL,
414 NULL);
415 DPRINT("Finished dpc routine\n");
416 }
417 current->Header.SignalState = TRUE;
418 if (current->Period != 0)
419 {
420 current->DueTime.QuadPart +=
421 current->Period * SYSTEM_TIME_UNITS_PER_MSEC;
422 }
423 else
424 {
425 RemoveEntryList(&current->TimerListEntry);
426 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
427 }
428 }
429
430 VOID KeExpireTimers(VOID)
431 {
432 PLIST_ENTRY current_entry = NULL;
433 PKTIMER current = NULL;
434 KIRQL oldlvl;
435
436 DPRINT("KeExpireTimers()\n");
437
438 current_entry = TimerListHead.Flink;
439
440 // DPRINT("&TimerListHead %x\n",&TimerListHead);
441 // DPRINT("current_entry %x\n",current_entry);
442 // DPRINT("current_entry->Flink %x\n",current_entry->Flink);
443 // DPRINT("current_entry->Flink->Flink %x\n",current_entry->Flink->Flink);
444
445 KeRaiseIrql(HIGH_LEVEL, &oldlvl);
446 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
447
448 while (current_entry!=(&TimerListHead))
449 {
450 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
451
452 current_entry = current_entry->Flink;
453
454 if (system_time >= current->DueTime.QuadPart)
455 {
456 HandleExpiredTimer(current);
457 }
458 }
459
460 KeReleaseSpinLock(&TimerListLock, oldlvl);
461 // DPRINT("Finished KeExpireTimers()\n");
462 }
463
464
465 VOID KiUpdateSystemTime (VOID)
466 /*
467 * FUNCTION: Handles a timer interrupt
468 */
469 {
470 char str[36];
471 char* vidmem=(char *)physical_to_linear(0xb8000 + 160 - 36);
472 int i;
473 int x,y;
474 // extern ULONG EiNrUsedBlocks;
475 extern unsigned int EiFreeNonPagedPool;
476 extern unsigned int EiUsedNonPagedPool;
477 extern ULONG PiNrThreads;
478 // extern ULONG MiNrFreePages;
479
480 if (TimerInitDone == FALSE)
481 {
482 return;
483 }
484 /*
485 * Increment the number of timers ticks
486 */
487 KiTimerTicks++;
488 system_time = system_time + CLOCK_INCREMENT;
489
490 /*
491 * Display the tick count in the top left of the screen as a debugging
492 * aid
493 */
494 // sprintf(str,"%.8u %.8u",nr_used_blocks,ticks);
495 if ((EiFreeNonPagedPool + EiUsedNonPagedPool) == 0)
496 {
497 x = y = 0;
498 }
499 else
500 {
501 x = (EiFreeNonPagedPool * 100) /
502 (EiFreeNonPagedPool + EiUsedNonPagedPool);
503 y = (EiUsedNonPagedPool * 100) /
504 (EiFreeNonPagedPool + EiUsedNonPagedPool);
505 }
506 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,ticks);
507 memset(str, 0, sizeof(str));
508 // sprintf(str,"%.8u %.8u",(unsigned int)EiNrUsedBlocks,
509 // (unsigned int)EiFreeNonPagedPool);
510 // sprintf(str,"%.8u %.8u",EiFreeNonPagedPool,EiUsedNonPagedPool);
511 sprintf(str,"%.8u %.8u",(unsigned int)PiNrRunnableThreads,
512 (unsigned int)PiNrThreads);
513 // sprintf(str,"%.8u %.8u", (unsigned int)PiNrRunnableThreads,
514 // (unsigned int)MiNrFreePages);
515 for (i=0;i<17;i++)
516 {
517 *vidmem=str[i];
518 vidmem++;
519 *vidmem=0x7;
520 vidmem++;
521 }
522 KeExpireTimers();
523 }
524
525
526 VOID KeInitializeTimerImpl(VOID)
527 /*
528 * FUNCTION: Initializes timer irq handling
529 * NOTE: This is only called once from main()
530 */
531 {
532 TIME_FIELDS TimeFields;
533 LARGE_INTEGER SystemBootTime;
534
535 DPRINT("KeInitializeTimerImpl()\n");
536
537 InitializeListHead(&TimerListHead);
538 KeInitializeSpinLock(&TimerListLock);
539
540 TimerInitDone = TRUE;
541
542 /*
543 * Calculate the starting time for the system clock
544 */
545 HalQueryRealTimeClock(&TimeFields);
546 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
547 boot_time=SystemBootTime.QuadPart;
548 system_time=boot_time;
549
550 DPRINT("Finished KeInitializeTimerImpl()\n");
551 }