InsertAscendingList adaption
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /* $Id: timer.c,v 1.67 2004/01/18 22:32:47 gdalsnes 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 <internal/ke.h>
23 #include <internal/id.h>
24 #include <internal/ps.h>
25 #include <internal/safe.h>
26
27 #define NDEBUG
28 #include <internal/debug.h>
29
30 /* GLOBALS ****************************************************************/
31
32 /*
33 * Current time
34 */
35 #if defined(__GNUC__)
36 static LARGE_INTEGER SystemBootTime = (LARGE_INTEGER)0LL;
37 #else
38 static LARGE_INTEGER SystemBootTime = { 0 };
39 #endif
40
41 /*
42 * Number of timer interrupts since initialisation
43 */
44 volatile ULONGLONG KeTickCount = 0;
45 volatile ULONG KiRawTicks = 0;
46
47 /*
48 * The increment in the system clock every timer tick (in system time units)
49 *
50 * = (1/18.2)*10^9
51 *
52 * RJJ was 54945055
53 */
54 #define CLOCK_INCREMENT (100000)
55
56 /*
57 * PURPOSE: List of timers
58 */
59 static LIST_ENTRY AbsoluteTimerListHead;
60 static LIST_ENTRY RelativeTimerListHead;
61 static KSPIN_LOCK TimerListLock;
62 static KSPIN_LOCK TimerValueLock;
63 static KDPC ExpireTimerDpc;
64
65 /* must raise IRQL to PROFILE_LEVEL and grab spin lock there, to sync with ISR */
66
67 extern ULONG PiNrRunnableThreads;
68
69 #define MICROSECONDS_PER_TICK (10000)
70 #define TICKS_TO_CALIBRATE (1)
71 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
72 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
73
74 static BOOLEAN TimerInitDone = FALSE;
75
76 /* FUNCTIONS **************************************************************/
77
78
79 NTSTATUS STDCALL
80 NtQueryTimerResolution(OUT PULONG MinimumResolution,
81 OUT PULONG MaximumResolution,
82 OUT PULONG ActualResolution)
83 {
84 UNIMPLEMENTED;
85 return STATUS_NOT_IMPLEMENTED;
86 }
87
88
89 NTSTATUS STDCALL
90 NtSetTimerResolution(IN ULONG RequestedResolution,
91 IN BOOL SetOrUnset,
92 OUT PULONG ActualResolution)
93 {
94 UNIMPLEMENTED;
95 return STATUS_NOT_IMPLEMENTED;
96 }
97
98
99 NTSTATUS STDCALL
100 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
101 IN PLARGE_INTEGER Frequency)
102 {
103 LARGE_INTEGER PerfCounter;
104 LARGE_INTEGER PerfFrequency;
105 NTSTATUS Status;
106
107 PerfCounter = KeQueryPerformanceCounter(&PerfFrequency);
108
109 if (Counter != NULL)
110 {
111 Status = MmCopyToCaller(&Counter->QuadPart, &PerfCounter.QuadPart, sizeof(PerfCounter.QuadPart));
112 if (!NT_SUCCESS(Status))
113 {
114 return(Status);
115 }
116 }
117
118 if (Frequency != NULL)
119 {
120 Status = MmCopyToCaller(&Frequency->QuadPart, &PerfFrequency.QuadPart, sizeof(PerfFrequency.QuadPart));
121 if (!NT_SUCCESS(Status))
122 {
123 return(Status);
124 }
125 }
126
127 return(STATUS_SUCCESS);
128 }
129
130
131 NTSTATUS STDCALL
132 NtDelayExecution(IN ULONG Alertable,
133 IN TIME* Interval)
134 {
135 NTSTATUS Status;
136 LARGE_INTEGER Timeout;
137
138 Status = MmCopyFromCaller(&Timeout, Interval, sizeof(Timeout));
139 if (!NT_SUCCESS(Status))
140 {
141 return(Status);
142 }
143
144 Timeout = *((PLARGE_INTEGER)Interval);
145 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
146 Alertable, Internal, Timeout);
147
148 DPRINT("Execution delay is %d/%d\n",
149 Timeout.u.HighPart, Timeout.u.LowPart);
150 Status = KeDelayExecutionThread(UserMode, (BOOLEAN)Alertable, &Timeout);
151 return(Status);
152 }
153
154
155 /*
156 * @implemented
157 */
158 NTSTATUS STDCALL
159 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
160 BOOLEAN Alertable,
161 PLARGE_INTEGER Interval)
162 /*
163 * FUNCTION: Puts the current thread into an alertable or nonalertable
164 * wait state for a given internal
165 * ARGUMENTS:
166 * WaitMode = Processor mode in which the caller is waiting
167 * Altertable = Specifies if the wait is alertable
168 * Interval = Specifies the interval to wait
169 * RETURNS: Status
170 */
171 {
172 PKTHREAD Thread = KeGetCurrentThread();
173
174 KeSetTimer(&Thread->Timer, *Interval, NULL);
175 return (KeWaitForSingleObject(&Thread->Timer,
176 (WaitMode == KernelMode) ? Executive : UserRequest, /* TMN: Was unconditionally Executive */
177 WaitMode, /* TMN: Was UserMode */
178 Alertable,
179 NULL));
180 }
181
182
183 /*
184 * @implemented
185 */
186 ULONG STDCALL
187 KeQueryTimeIncrement(VOID)
188 /*
189 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
190 * the system clock every time the clock interrupts
191 * RETURNS: The increment
192 */
193 {
194 return(CLOCK_INCREMENT);
195 }
196
197
198 /*
199 * @implemented
200 */
201 VOID STDCALL
202 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
203 /*
204 * FUNCTION: Gets the current system time
205 * ARGUMENTS:
206 * CurrentTime (OUT) = The routine stores the current time here
207 * NOTE: The time is the number of 100-nanosecond intervals since the
208 * 1st of January, 1601.
209 */
210 {
211 do
212 {
213 CurrentTime->u.HighPart = SharedUserData->SystemTime.High1Part;
214 CurrentTime->u.LowPart = SharedUserData->SystemTime.LowPart;
215 }
216 while (CurrentTime->u.HighPart != SharedUserData->SystemTime.High2Part);
217 }
218
219 VOID STDCALL
220 KeQueryInterruptTime(PLARGE_INTEGER CurrentTime)
221 {
222 do
223 {
224 CurrentTime->u.HighPart = SharedUserData->InterruptTime.High1Part;
225 CurrentTime->u.LowPart = SharedUserData->InterruptTime.LowPart;
226 }
227 while (CurrentTime->u.HighPart != SharedUserData->InterruptTime.High2Part);
228
229 }
230
231
232 NTSTATUS STDCALL
233 NtGetTickCount (PULONG UpTime)
234 {
235 LARGE_INTEGER TickCount;
236 if (UpTime == NULL)
237 {
238 return(STATUS_INVALID_PARAMETER);
239 }
240
241 KeQueryTickCount(&TickCount);
242 return(MmCopyToCaller(UpTime, &TickCount.u.LowPart, sizeof(*UpTime)));
243 }
244
245
246 /*
247 * @implemented
248 */
249 BOOLEAN STDCALL
250 KeSetTimer (PKTIMER Timer,
251 LARGE_INTEGER DueTime,
252 PKDPC Dpc)
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 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
267 }
268
269 /*
270 * @implemented
271 */
272 BOOLEAN STDCALL
273 KeSetTimerEx (PKTIMER Timer,
274 LARGE_INTEGER DueTime,
275 LONG Period,
276 PKDPC Dpc)
277 /*
278 * FUNCTION: Sets the absolute or relative interval at which a timer object
279 * is to be set to the signaled state and optionally supplies a
280 * CustomTimerDpc to be executed when the timer expires.
281 * ARGUMENTS:
282 * Timer = Points to a previously initialized timer object
283 * DueTimer = If positive then absolute time to expire at
284 * If negative then the relative time to expire at
285 * Dpc = If non-NULL then a dpc to be called when the timer expires
286 * RETURNS: True if the timer was already in the system timer queue
287 * False otherwise
288 */
289 {
290 KIRQL oldlvl;
291 LARGE_INTEGER Time;
292 BOOLEAN AlreadyInList;
293
294 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer);
295
296 assert(KeGetCurrentIrql() <= DISPATCH_LEVEL);
297
298 KeAcquireSpinLock(&TimerListLock, &oldlvl);
299
300 Timer->Dpc = Dpc;
301 if (DueTime.QuadPart < 0)
302 {
303 KeQueryInterruptTime(&Time);
304 Timer->Header.Absolute = 0;
305 Timer->DueTime.QuadPart = Time.QuadPart - DueTime.QuadPart;
306 }
307 else
308 {
309 KeQuerySystemTime(&Time);
310 Timer->Header.Absolute = 1;
311 if (DueTime.QuadPart >= Time.QuadPart)
312 {
313 Timer->DueTime.QuadPart = DueTime.QuadPart;
314 }
315 else
316 {
317 Timer->DueTime.QuadPart = Time.QuadPart;
318 }
319 }
320 Timer->Period = Period;
321 Timer->Header.SignalState = FALSE;
322 AlreadyInList = (Timer->TimerListEntry.Flink == NULL) ? FALSE : TRUE;
323 assert((Timer->TimerListEntry.Flink == NULL && Timer->TimerListEntry.Blink == NULL) ||
324 (Timer->TimerListEntry.Flink != NULL && Timer->TimerListEntry.Blink != NULL));
325 if (AlreadyInList)
326 {
327 RemoveEntryList(&Timer->TimerListEntry);
328 }
329 if (Timer->Header.Absolute)
330 {
331 InsertAscendingList(&AbsoluteTimerListHead,
332 KTIMER,
333 TimerListEntry,
334 Timer,
335 DueTime.QuadPart);
336
337 }
338 else
339 {
340 InsertAscendingList(&RelativeTimerListHead,
341 KTIMER,
342 TimerListEntry,
343 Timer,
344 DueTime.QuadPart);
345
346 }
347
348 KeReleaseSpinLock(&TimerListLock, oldlvl);
349
350 return AlreadyInList;
351 }
352
353 /*
354 * @implemented
355 */
356 BOOLEAN STDCALL
357 KeCancelTimer (PKTIMER Timer)
358 /*
359 * FUNCTION: Removes a timer from the system timer list
360 * ARGUMENTS:
361 * Timer = timer to cancel
362 * RETURNS: True if the timer was running
363 * False otherwise
364 */
365 {
366 KIRQL oldlvl;
367
368 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
369
370 KeAcquireSpinLock(&TimerListLock, &oldlvl);
371
372 if (Timer->TimerListEntry.Flink == NULL)
373 {
374 KeReleaseSpinLock(&TimerListLock, oldlvl);
375 return(FALSE);
376 }
377 if (Timer->Header.Absolute)
378 {
379 assert(&Timer->TimerListEntry != &AbsoluteTimerListHead);
380 }
381 else
382 {
383 assert(&Timer->TimerListEntry != &RelativeTimerListHead);
384 }
385 assert(Timer->TimerListEntry.Flink != &Timer->TimerListEntry);
386 RemoveEntryList(&Timer->TimerListEntry);
387 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
388 KeReleaseSpinLock(&TimerListLock, oldlvl);
389
390 return(TRUE);
391 }
392
393 /*
394 * @implemented
395 */
396 BOOLEAN STDCALL
397 KeReadStateTimer (PKTIMER Timer)
398 {
399 return (BOOLEAN)(Timer->Header.SignalState);
400 }
401
402 /*
403 * @implemented
404 */
405 VOID STDCALL
406 KeInitializeTimer (PKTIMER Timer)
407 /*
408 * FUNCTION: Initalizes a kernel timer object
409 * ARGUMENTS:
410 * Timer = caller supplied storage for the timer
411 * NOTE: This function initializes a notification timer
412 */
413 {
414 KeInitializeTimerEx(Timer, NotificationTimer);
415 }
416
417 /*
418 * @implemented
419 */
420 VOID STDCALL
421 KeInitializeTimerEx (PKTIMER Timer,
422 TIMER_TYPE Type)
423 /*
424 * FUNCTION: Initializes a kernel timer object
425 * ARGUMENTS:
426 * Timer = caller supplied storage for the timer
427 * Type = the type of timer (notification or synchronization)
428 * NOTE: When a notification type expires all waiting threads are released
429 * and the timer remains signalled until it is explicitly reset. When a
430 * syncrhonization timer expires its state is set to signalled until a
431 * single waiting thread is released and then the timer is reset.
432 */
433 {
434 ULONG IType;
435
436 if (Type == NotificationTimer)
437 {
438 IType = InternalNotificationTimer;
439 }
440 else if (Type == SynchronizationTimer)
441 {
442 IType = InternalSynchronizationTimer;
443 }
444 else
445 {
446 assert(FALSE);
447 return;
448 }
449
450 KeInitializeDispatcherHeader(&Timer->Header,
451 IType,
452 sizeof(KTIMER) / sizeof(ULONG),
453 FALSE);
454 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
455 }
456
457 /*
458 * @implemented
459 */
460 VOID STDCALL
461 KeQueryTickCount(PLARGE_INTEGER TickCount)
462 /*
463 * FUNCTION: Returns the number of ticks since the system was booted
464 * ARGUMENTS:
465 * TickCount (OUT) = Points to storage for the number of ticks
466 */
467 {
468 TickCount->QuadPart = KeTickCount;
469 }
470
471 /*
472 * We enter this function at IRQL DISPATCH_LEVEL, and with the
473 * TimerListLock held.
474 */
475 STATIC VOID
476 HandleExpiredTimer(PKTIMER Timer)
477 {
478 DPRINT("HandleExpiredTime(Timer %x)\n", Timer);
479 if (Timer->Dpc != NULL)
480 {
481 DPRINT("Timer->Dpc %x Timer->Dpc->DeferredRoutine %x\n",
482 Timer->Dpc, Timer->Dpc->DeferredRoutine);
483 KeInsertQueueDpc(Timer->Dpc,
484 NULL,
485 NULL);
486 DPRINT("Finished dpc routine\n");
487 }
488
489 assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
490
491 KeAcquireDispatcherDatabaseLockAtDpcLevel();
492 Timer->Header.SignalState = TRUE;
493 KeDispatcherObjectWake(&Timer->Header);
494 KeReleaseDispatcherDatabaseLockFromDpcLevel();
495
496 if (Timer->Period != 0)
497 {
498 Timer->DueTime.QuadPart +=
499 Timer->Period * SYSTEM_TIME_UNITS_PER_MSEC;
500 if (Timer->Header.Absolute)
501 {
502 InsertAscendingList(&AbsoluteTimerListHead,
503 KTIMER,
504 TimerListEntry,
505 Timer,
506 DueTime.QuadPart);
507 }
508 else
509 {
510 InsertAscendingList(&RelativeTimerListHead,
511 KTIMER,
512 TimerListEntry,
513 Timer,
514 DueTime.QuadPart);
515 }
516 }
517 }
518
519 VOID STDCALL
520 KeExpireTimers(PKDPC Dpc,
521 PVOID Context1,
522 PVOID Arg1,
523 PVOID Arg2)
524 {
525 PLIST_ENTRY current_entry = NULL;
526 PKTIMER current = NULL;
527 ULONG Eip = (ULONG)Arg1;
528 LARGE_INTEGER InterruptTime;
529 LARGE_INTEGER SystemTime;
530 LIST_ENTRY TimerList;
531
532 DPRINT("KeExpireTimers()\n");
533
534 assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
535
536 InitializeListHead(&TimerList);
537
538 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
539
540 KeQueryInterruptTime(&InterruptTime);
541 KeQuerySystemTime(&SystemTime);
542
543 current_entry = RelativeTimerListHead.Flink;
544 assert(current_entry);
545 while (current_entry != &RelativeTimerListHead)
546 {
547 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
548 assert(current);
549 assert(current_entry != &RelativeTimerListHead);
550 assert(current_entry->Flink != current_entry);
551 if ((ULONGLONG)InterruptTime.QuadPart < current->DueTime.QuadPart)
552 {
553 break;
554 }
555 current_entry = current_entry->Flink;
556 RemoveEntryList(&current->TimerListEntry);
557 InsertTailList(&TimerList, &current->TimerListEntry);
558 }
559
560 current_entry = AbsoluteTimerListHead.Flink;
561 assert(current_entry);
562 while (current_entry != &AbsoluteTimerListHead)
563 {
564 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
565 assert(current);
566 assert(current_entry != &AbsoluteTimerListHead);
567 assert(current_entry->Flink != current_entry);
568 if ((ULONGLONG)SystemTime.QuadPart < current->DueTime.QuadPart)
569 {
570 break;
571 }
572 current_entry = current_entry->Flink;
573 RemoveEntryList(&current->TimerListEntry);
574 InsertTailList(&TimerList, &current->TimerListEntry);
575 }
576
577 while (!IsListEmpty(&TimerList))
578 {
579 current_entry = RemoveHeadList(&TimerList);
580 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
581 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
582 HandleExpiredTimer(current);
583 }
584
585 KiAddProfileEvent(ProfileTime, Eip);
586
587 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
588 }
589
590
591 VOID
592 KiUpdateSystemTime(KIRQL oldIrql,
593 ULONG Eip)
594 /*
595 * FUNCTION: Handles a timer interrupt
596 */
597 {
598 LARGE_INTEGER Time;
599 assert(KeGetCurrentIrql() == PROFILE_LEVEL);
600
601 KiRawTicks++;
602
603 if (TimerInitDone == FALSE)
604 {
605 return;
606 }
607 /*
608 * Increment the number of timers ticks
609 */
610 KeTickCount++;
611 SharedUserData->TickCountLow++;
612
613 KiAcquireSpinLock(&TimerValueLock);
614
615 Time.u.LowPart = SharedUserData->InterruptTime.LowPart;
616 Time.u.HighPart = SharedUserData->InterruptTime.High1Part;
617 Time.QuadPart += CLOCK_INCREMENT;
618 SharedUserData->InterruptTime.High2Part = Time.u.HighPart;
619 SharedUserData->InterruptTime.LowPart = Time.u.LowPart;
620 SharedUserData->InterruptTime.High1Part = Time.u.HighPart;
621
622 Time.u.LowPart = SharedUserData->SystemTime.LowPart;
623 Time.u.HighPart = SharedUserData->SystemTime.High1Part;
624 Time.QuadPart += CLOCK_INCREMENT;
625 SharedUserData->SystemTime.High2Part = Time.u.HighPart;
626 SharedUserData->SystemTime.LowPart = Time.u.LowPart;
627 SharedUserData->SystemTime.High1Part = Time.u.HighPart;
628
629 KiReleaseSpinLock(&TimerValueLock);
630
631 /*
632 * Queue a DPC that will expire timers
633 */
634 KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)Eip, 0);
635 }
636
637
638 VOID INIT_FUNCTION
639 KeInitializeTimerImpl(VOID)
640 /*
641 * FUNCTION: Initializes timer irq handling
642 * NOTE: This is only called once from main()
643 */
644 {
645 TIME_FIELDS TimeFields;
646
647 DPRINT("KeInitializeTimerImpl()\n");
648 InitializeListHead(&AbsoluteTimerListHead);
649 InitializeListHead(&RelativeTimerListHead);
650 KeInitializeSpinLock(&TimerListLock);
651 KeInitializeSpinLock(&TimerValueLock);
652 KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0);
653 /*
654 * Calculate the starting time for the system clock
655 */
656 HalQueryRealTimeClock(&TimeFields);
657 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
658
659 SharedUserData->TickCountLow = 0;
660 SharedUserData->TickCountMultiplier = 167783691; // 2^24 * 1193182 / 119310
661 SharedUserData->InterruptTime.High2Part = 0;
662 SharedUserData->InterruptTime.LowPart = 0;
663 SharedUserData->InterruptTime.High1Part = 0;
664 SharedUserData->SystemTime.High2Part = SystemBootTime.u.HighPart;
665 SharedUserData->SystemTime.LowPart = SystemBootTime.u.LowPart;
666 SharedUserData->SystemTime.High1Part = SystemBootTime.u.HighPart;
667
668 TimerInitDone = TRUE;
669 DPRINT("Finished KeInitializeTimerImpl()\n");
670 }