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