fix kernel queues + misc fixes/cleanups
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /* $Id: timer.c,v 1.90 2004/11/21 18:33:54 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 <ntoskrnl.h>
21
22 #define NDEBUG
23 #include <internal/debug.h>
24
25
26 /* GLOBALS ****************************************************************/
27
28 /*
29 * Current time
30 */
31 #if defined(__GNUC__)
32 LARGE_INTEGER SystemBootTime = (LARGE_INTEGER)0LL;
33 #else
34 LARGE_INTEGER SystemBootTime = { 0 };
35 #endif
36
37 CHAR KiTimerSystemAuditing = 0;
38
39 /*
40 * Number of timer interrupts since initialisation
41 */
42 volatile ULONGLONG KeTickCount = 0;
43 volatile ULONG KiRawTicks = 0;
44
45 /*
46 * The increment in the system clock every timer tick (in system time units)
47 *
48 * = (1/18.2)*10^9
49 *
50 * RJJ was 54945055
51 */
52 #define CLOCK_INCREMENT (100000)
53
54 #ifdef __GNUC__
55 ULONG EXPORTED KeMaximumIncrement = 100000;
56 ULONG EXPORTED KeMinimumIncrement = 100000;
57 #else
58 /* Microsoft-style declarations */
59 EXPORTED ULONG KeMaximumIncrement = 100000;
60 EXPORTED ULONG KeMinimumIncrement = 100000;
61 #endif
62
63
64
65 /*
66 * PURPOSE: List of timers
67 */
68 static LIST_ENTRY AbsoluteTimerListHead;
69 static LIST_ENTRY RelativeTimerListHead;
70 static KSPIN_LOCK TimerListLock;
71 static KSPIN_LOCK TimerValueLock;
72 static KDPC ExpireTimerDpc;
73
74 /* must raise IRQL to PROFILE_LEVEL and grab spin lock there, to sync with ISR */
75
76 extern HANDLE PsIdleThreadHandle;
77
78 #define MICROSECONDS_PER_TICK (10000)
79 #define TICKS_TO_CALIBRATE (1)
80 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
81 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
82
83 static BOOLEAN TimerInitDone = FALSE;
84
85 /* FUNCTIONS **************************************************************/
86
87
88 NTSTATUS STDCALL
89 NtQueryTimerResolution(OUT PULONG MinimumResolution,
90 OUT PULONG MaximumResolution,
91 OUT PULONG ActualResolution)
92 {
93 UNIMPLEMENTED;
94 return STATUS_NOT_IMPLEMENTED;
95 }
96
97
98 NTSTATUS STDCALL
99 NtSetTimerResolution(IN ULONG DesiredResolution,
100 IN BOOLEAN SetResolution,
101 OUT PULONG CurrentResolution)
102 {
103 UNIMPLEMENTED;
104 return STATUS_NOT_IMPLEMENTED;
105 }
106
107
108 NTSTATUS STDCALL
109 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
110 IN PLARGE_INTEGER Frequency)
111 {
112 LARGE_INTEGER PerfCounter;
113 LARGE_INTEGER PerfFrequency;
114 NTSTATUS Status;
115
116 PerfCounter = KeQueryPerformanceCounter(&PerfFrequency);
117
118 if (Counter != NULL)
119 {
120 Status = MmCopyToCaller(&Counter->QuadPart, &PerfCounter.QuadPart, sizeof(PerfCounter.QuadPart));
121 if (!NT_SUCCESS(Status))
122 {
123 return(Status);
124 }
125 }
126
127 if (Frequency != NULL)
128 {
129 Status = MmCopyToCaller(&Frequency->QuadPart, &PerfFrequency.QuadPart, sizeof(PerfFrequency.QuadPart));
130 if (!NT_SUCCESS(Status))
131 {
132 return(Status);
133 }
134 }
135
136 return(STATUS_SUCCESS);
137 }
138
139
140 NTSTATUS STDCALL
141 NtDelayExecution(IN ULONG Alertable,
142 IN TIME* Interval)
143 {
144 NTSTATUS Status;
145 LARGE_INTEGER Timeout;
146
147 Status = MmCopyFromCaller(&Timeout, Interval, sizeof(Timeout));
148 if (!NT_SUCCESS(Status))
149 {
150 return(Status);
151 }
152
153 Timeout = *((PLARGE_INTEGER)Interval);
154 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
155 Alertable, Internal, Timeout);
156
157 DPRINT("Execution delay is %d/%d\n",
158 Timeout.u.HighPart, Timeout.u.LowPart);
159 Status = KeDelayExecutionThread(UserMode, (BOOLEAN)Alertable, &Timeout);
160 return(Status);
161 }
162
163
164 /*
165 * @implemented
166 */
167 NTSTATUS STDCALL
168 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
169 BOOLEAN Alertable,
170 PLARGE_INTEGER Interval)
171 /*
172 * FUNCTION: Puts the current thread into an alertable or nonalertable
173 * wait state for a given internal
174 * ARGUMENTS:
175 * WaitMode = Processor mode in which the caller is waiting
176 * Altertable = Specifies if the wait is alertable
177 * Interval = Specifies the interval to wait
178 * RETURNS: Status
179 */
180 {
181 PKTHREAD Thread = KeGetCurrentThread();
182
183 KeSetTimer(&Thread->Timer, *Interval, NULL);
184 return (KeWaitForSingleObject(&Thread->Timer,
185 (WaitMode == KernelMode) ? Executive : UserRequest, /* TMN: Was unconditionally Executive */
186 WaitMode, /* TMN: Was UserMode */
187 Alertable,
188 NULL));
189 }
190
191
192 /*
193 * @implemented
194 */
195 ULONG STDCALL
196 KeQueryTimeIncrement(VOID)
197 /*
198 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
199 * the system clock every time the clock interrupts
200 * RETURNS: The increment
201 */
202 {
203 return(CLOCK_INCREMENT);
204 }
205
206
207 /*
208 * FUNCTION: Gets the current system time
209 * ARGUMENTS:
210 * CurrentTime (OUT) = The routine stores the current time here
211 * NOTE: The time is the number of 100-nanosecond intervals since the
212 * 1st of January, 1601.
213 *
214 * @implemented
215 */
216 VOID STDCALL
217 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
218 {
219 do
220 {
221 CurrentTime->u.HighPart = SharedUserData->SystemTime.High1Time;
222 CurrentTime->u.LowPart = SharedUserData->SystemTime.LowPart;
223 }
224 while (CurrentTime->u.HighPart != SharedUserData->SystemTime.High2Time);
225 }
226
227 ULONGLONG STDCALL
228 KeQueryInterruptTime(VOID)
229 {
230 LARGE_INTEGER CurrentTime;
231
232 do
233 {
234 CurrentTime.u.HighPart = SharedUserData->InterruptTime.High1Time;
235 CurrentTime.u.LowPart = SharedUserData->InterruptTime.LowPart;
236 }
237 while (CurrentTime.u.HighPart != SharedUserData->InterruptTime.High2Time);
238
239 return CurrentTime.QuadPart;
240 }
241
242 /*
243 * @implemented
244 */
245 ULONG
246 STDCALL
247 NtGetTickCount(VOID)
248 {
249 LARGE_INTEGER TickCount;
250 KeQueryTickCount(&TickCount);
251 return TickCount.u.LowPart;
252 }
253
254 /*
255 * @implemented
256 */
257 BOOLEAN STDCALL
258 KeSetTimer (PKTIMER Timer,
259 LARGE_INTEGER DueTime,
260 PKDPC Dpc)
261 /*
262 * FUNCTION: Sets the absolute or relative interval at which a timer object
263 * is to be set to the signaled state and optionally supplies a
264 * CustomTimerDpc to be executed when the timer expires.
265 * ARGUMENTS:
266 * Timer = Points to a previously initialized timer object
267 * DueTimer = If positive then absolute time to expire at
268 * If negative then the relative time to expire at
269 * Dpc = If non-NULL then a dpc to be called when the timer expires
270 * RETURNS: True if the timer was already in the system timer queue
271 * False otherwise
272 */
273 {
274 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
275 }
276
277 /*
278 * @implemented
279 */
280 BOOLEAN STDCALL
281 KeSetTimerEx (PKTIMER Timer,
282 LARGE_INTEGER DueTime,
283 LONG Period,
284 PKDPC Dpc)
285 /*
286 * FUNCTION: Sets the absolute or relative interval at which a timer object
287 * is to be set to the signaled state and optionally supplies a
288 * CustomTimerDpc to be executed when the timer expires.
289 * ARGUMENTS:
290 * Timer = Points to a previously initialized timer object
291 * DueTimer = If positive then absolute time to expire at
292 * If negative then the relative time to expire at
293 * Dpc = If non-NULL then a dpc to be called when the timer expires
294 * RETURNS: True if the timer was already in the system timer queue
295 * False otherwise
296 */
297 {
298 KIRQL oldlvl;
299 LARGE_INTEGER Time;
300 BOOLEAN AlreadyInList;
301
302 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer);
303
304 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
305
306 KeAcquireSpinLock(&TimerListLock, &oldlvl);
307
308 Timer->Dpc = Dpc;
309 if (DueTime.QuadPart < 0)
310 {
311 Timer->Header.Absolute = 0;
312 Timer->DueTime.QuadPart = KeQueryInterruptTime() - DueTime.QuadPart;
313 }
314 else
315 {
316 KeQuerySystemTime(&Time);
317 Timer->Header.Absolute = 1;
318 if (DueTime.QuadPart >= Time.QuadPart)
319 {
320 Timer->DueTime.QuadPart = DueTime.QuadPart;
321 }
322 else
323 {
324 Timer->DueTime.QuadPart = Time.QuadPart;
325 }
326 }
327 Timer->Period = Period;
328 Timer->Header.SignalState = FALSE;
329 AlreadyInList = (Timer->TimerListEntry.Flink == NULL) ? FALSE : TRUE;
330 ASSERT((Timer->TimerListEntry.Flink == NULL && Timer->TimerListEntry.Blink == NULL) ||
331 (Timer->TimerListEntry.Flink != NULL && Timer->TimerListEntry.Blink != NULL));
332 if (AlreadyInList)
333 {
334 RemoveEntryList(&Timer->TimerListEntry);
335 }
336 if (Timer->Header.Absolute)
337 {
338 InsertAscendingList(&AbsoluteTimerListHead,
339 KTIMER,
340 TimerListEntry,
341 Timer,
342 DueTime.QuadPart);
343
344 }
345 else
346 {
347 InsertAscendingList(&RelativeTimerListHead,
348 KTIMER,
349 TimerListEntry,
350 Timer,
351 DueTime.QuadPart);
352
353 }
354
355 KeReleaseSpinLock(&TimerListLock, oldlvl);
356
357 return AlreadyInList;
358 }
359
360 /*
361 * @implemented
362 */
363 BOOLEAN STDCALL
364 KeCancelTimer (PKTIMER Timer)
365 /*
366 * FUNCTION: Removes a timer from the system timer list
367 * ARGUMENTS:
368 * Timer = timer to cancel
369 * RETURNS: True if the timer was running
370 * False otherwise
371 */
372 {
373 KIRQL oldlvl;
374
375 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
376
377 KeAcquireSpinLock(&TimerListLock, &oldlvl);
378
379 if (Timer->TimerListEntry.Flink == NULL)
380 {
381 KeReleaseSpinLock(&TimerListLock, oldlvl);
382 return(FALSE);
383 }
384 if (Timer->Header.Absolute)
385 {
386 ASSERT(&Timer->TimerListEntry != &AbsoluteTimerListHead);
387 }
388 else
389 {
390 ASSERT(&Timer->TimerListEntry != &RelativeTimerListHead);
391 }
392 ASSERT(Timer->TimerListEntry.Flink != &Timer->TimerListEntry);
393 RemoveEntryList(&Timer->TimerListEntry);
394 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
395 KeReleaseSpinLock(&TimerListLock, oldlvl);
396
397 return(TRUE);
398 }
399
400 /*
401 * @implemented
402 */
403 BOOLEAN STDCALL
404 KeReadStateTimer (PKTIMER Timer)
405 {
406 return (BOOLEAN)(Timer->Header.SignalState);
407 }
408
409 /*
410 * @implemented
411 */
412 VOID STDCALL
413 KeInitializeTimer (PKTIMER Timer)
414 /*
415 * FUNCTION: Initalizes a kernel timer object
416 * ARGUMENTS:
417 * Timer = caller supplied storage for the timer
418 * NOTE: This function initializes a notification timer
419 */
420 {
421 KeInitializeTimerEx(Timer, NotificationTimer);
422 }
423
424 /*
425 * @implemented
426 */
427 VOID STDCALL
428 KeInitializeTimerEx (PKTIMER Timer,
429 TIMER_TYPE Type)
430 /*
431 * FUNCTION: Initializes a kernel timer object
432 * ARGUMENTS:
433 * Timer = caller supplied storage for the timer
434 * Type = the type of timer (notification or synchronization)
435 * NOTE: When a notification type expires all waiting threads are released
436 * and the timer remains signalled until it is explicitly reset. When a
437 * syncrhonization timer expires its state is set to signalled until a
438 * single waiting thread is released and then the timer is reset.
439 */
440 {
441 ULONG IType;
442
443 if (Type == NotificationTimer)
444 {
445 IType = InternalNotificationTimer;
446 }
447 else if (Type == SynchronizationTimer)
448 {
449 IType = InternalSynchronizationTimer;
450 }
451 else
452 {
453 ASSERT(FALSE);
454 return;
455 }
456
457 KeInitializeDispatcherHeader(&Timer->Header,
458 IType,
459 sizeof(KTIMER) / sizeof(ULONG),
460 FALSE);
461 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
462 }
463
464 /*
465 * @implemented
466 */
467 VOID STDCALL
468 KeQueryTickCount(PLARGE_INTEGER TickCount)
469 /*
470 * FUNCTION: Returns the number of ticks since the system was booted
471 * ARGUMENTS:
472 * TickCount (OUT) = Points to storage for the number of ticks
473 */
474 {
475 TickCount->QuadPart = KeTickCount;
476 }
477
478 /*
479 * @implemented
480 */
481 ULONG
482 STDCALL
483 KeQueryRuntimeThread(
484 IN PKTHREAD Thread,
485 OUT PULONG UserTime
486 )
487 {
488 /* Return the User Time */
489 *UserTime = Thread->UserTime;
490
491 /* Return the Kernel Time */
492 return Thread->KernelTime;
493 }
494
495 /*
496 * @implemented
497 */
498 VOID
499 STDCALL
500 KeSetTimeIncrement(
501 IN ULONG MaxIncrement,
502 IN ULONG MinIncrement
503 )
504 {
505 /* Set some Internal Variables */
506 /* FIXME: We use a harcoded CLOCK_INCREMENT. That *must* be changed */
507 KeMaximumIncrement = MaxIncrement;
508 KeMinimumIncrement = MinIncrement;
509 }
510
511 /*
512 * We enter this function at IRQL DISPATCH_LEVEL, and with the
513 * TimerListLock held.
514 */
515 STATIC VOID
516 HandleExpiredTimer(PKTIMER Timer)
517 {
518 DPRINT("HandleExpiredTime(Timer %x)\n", Timer);
519 if (Timer->Dpc != NULL)
520 {
521 DPRINT("Timer->Dpc %x Timer->Dpc->DeferredRoutine %x\n",
522 Timer->Dpc, Timer->Dpc->DeferredRoutine);
523 KeInsertQueueDpc(Timer->Dpc,
524 NULL,
525 NULL);
526 DPRINT("Finished dpc routine\n");
527 }
528
529 ASSERT_IRQL_EQUAL(DISPATCH_LEVEL);
530
531 KeAcquireDispatcherDatabaseLockAtDpcLevel();
532 Timer->Header.SignalState = TRUE;
533 KiDispatcherObjectWake(&Timer->Header);
534 KeReleaseDispatcherDatabaseLockFromDpcLevel();
535
536 if (Timer->Period != 0)
537 {
538 Timer->DueTime.QuadPart +=
539 Timer->Period * SYSTEM_TIME_UNITS_PER_MSEC;
540 if (Timer->Header.Absolute)
541 {
542 InsertAscendingList(&AbsoluteTimerListHead,
543 KTIMER,
544 TimerListEntry,
545 Timer,
546 DueTime.QuadPart);
547 }
548 else
549 {
550 InsertAscendingList(&RelativeTimerListHead,
551 KTIMER,
552 TimerListEntry,
553 Timer,
554 DueTime.QuadPart);
555 }
556 }
557 }
558
559 VOID STDCALL
560 KeExpireTimers(PKDPC Dpc,
561 PVOID Context1,
562 PVOID Arg1,
563 PVOID Arg2)
564 {
565 PLIST_ENTRY current_entry = NULL;
566 PKTIMER current = NULL;
567 ULONG Eip = (ULONG)Arg1;
568 LARGE_INTEGER InterruptTime;
569 LARGE_INTEGER SystemTime;
570 LIST_ENTRY TimerList;
571
572 DPRINT("KeExpireTimers()\n");
573
574 ASSERT_IRQL_EQUAL(DISPATCH_LEVEL);
575
576 InitializeListHead(&TimerList);
577
578 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
579
580 InterruptTime.QuadPart = KeQueryInterruptTime();
581 KeQuerySystemTime(&SystemTime);
582
583 current_entry = RelativeTimerListHead.Flink;
584 ASSERT(current_entry);
585 while (current_entry != &RelativeTimerListHead)
586 {
587 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
588 ASSERT(current);
589 ASSERT(current_entry != &RelativeTimerListHead);
590 ASSERT(current_entry->Flink != current_entry);
591 if ((ULONGLONG)InterruptTime.QuadPart < current->DueTime.QuadPart)
592 {
593 break;
594 }
595 current_entry = current_entry->Flink;
596 RemoveEntryList(&current->TimerListEntry);
597 InsertTailList(&TimerList, &current->TimerListEntry);
598 }
599
600 current_entry = AbsoluteTimerListHead.Flink;
601 ASSERT(current_entry);
602 while (current_entry != &AbsoluteTimerListHead)
603 {
604 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
605 ASSERT(current);
606 ASSERT(current_entry != &AbsoluteTimerListHead);
607 ASSERT(current_entry->Flink != current_entry);
608 if ((ULONGLONG)SystemTime.QuadPart < current->DueTime.QuadPart)
609 {
610 break;
611 }
612 current_entry = current_entry->Flink;
613 RemoveEntryList(&current->TimerListEntry);
614 InsertTailList(&TimerList, &current->TimerListEntry);
615 }
616
617 while (!IsListEmpty(&TimerList))
618 {
619 current_entry = RemoveHeadList(&TimerList);
620 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
621 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
622 HandleExpiredTimer(current);
623 }
624
625 KiAddProfileEvent(ProfileTime, Eip);
626
627 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
628 }
629
630
631 VOID INIT_FUNCTION
632 KeInitializeTimerImpl(VOID)
633 /*
634 * FUNCTION: Initializes timer irq handling
635 * NOTE: This is only called once from main()
636 */
637 {
638 TIME_FIELDS TimeFields;
639
640 DPRINT("KeInitializeTimerImpl()\n");
641 InitializeListHead(&AbsoluteTimerListHead);
642 InitializeListHead(&RelativeTimerListHead);
643 KeInitializeSpinLock(&TimerListLock);
644 KeInitializeSpinLock(&TimerValueLock);
645 KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0);
646 /*
647 * Calculate the starting time for the system clock
648 */
649 HalQueryRealTimeClock(&TimeFields);
650 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
651
652 SharedUserData->TickCountLowDeprecated = 0;
653 SharedUserData->TickCountMultiplier = 167783691; // 2^24 * 1193182 / 119310
654 SharedUserData->InterruptTime.High2Time = 0;
655 SharedUserData->InterruptTime.LowPart = 0;
656 SharedUserData->InterruptTime.High1Time = 0;
657 SharedUserData->SystemTime.High2Time = SystemBootTime.u.HighPart;
658 SharedUserData->SystemTime.LowPart = SystemBootTime.u.LowPart;
659 SharedUserData->SystemTime.High1Time = SystemBootTime.u.HighPart;
660
661 TimerInitDone = TRUE;
662 DPRINT("Finished KeInitializeTimerImpl()\n");
663 }
664
665 /*
666 * @unimplemented
667 */
668 VOID
669 FASTCALL
670 KeSetTimeUpdateNotifyRoutine(
671 IN PTIME_UPDATE_NOTIFY_ROUTINE NotifyRoutine
672 )
673 {
674 UNIMPLEMENTED;
675 }
676
677
678 /*
679 * NOTE: On Windows this function takes exactly one parameter and EBP is
680 * guaranteed to point to KTRAP_FRAME. The function is used only
681 * by HAL, so there's no point in keeping that prototype.
682 *
683 * @implemented
684 */
685 VOID
686 STDCALL
687 KeUpdateRunTime(
688 IN PKTRAP_FRAME TrapFrame,
689 IN KIRQL Irql
690 )
691 {
692 PKPCR Pcr;
693 PKTHREAD CurrentThread;
694 PKPROCESS CurrentProcess;
695 #if 0
696 ULONG DpcLastCount;
697 #endif
698
699 Pcr = KeGetCurrentKPCR();
700
701 /* Make sure we don't go further if we're in early boot phase. */
702 if (Pcr == NULL || Pcr->PrcbData.CurrentThread == NULL)
703 return;
704
705 DPRINT("KernelTime %u, UserTime %u \n", Pcr->PrcbData.KernelTime, Pcr->PrcbData.UserTime);
706
707 CurrentThread = Pcr->PrcbData.CurrentThread;
708 CurrentProcess = CurrentThread->ApcState.Process;
709
710 /*
711 * Cs bit 0 is always set for user mode if we are in protected mode.
712 * V86 mode is counted as user time.
713 */
714 if (TrapFrame->Cs & 0x1 ||
715 TrapFrame->Eflags & X86_EFLAGS_VM)
716 {
717 InterlockedIncrement((PLONG)&CurrentThread->UserTime);
718 InterlockedIncrement((PLONG)&CurrentProcess->UserTime);
719 InterlockedIncrement((PLONG)&Pcr->PrcbData.UserTime);
720 }
721 else
722 {
723 if (Irql > DISPATCH_LEVEL)
724 {
725 Pcr->PrcbData.InterruptTime++;
726 }
727 else if (Irql == DISPATCH_LEVEL)
728 {
729 Pcr->PrcbData.DpcTime++;
730 }
731 else
732 {
733 InterlockedIncrement((PLONG)&CurrentThread->KernelTime);
734 InterlockedIncrement((PLONG)&CurrentProcess->KernelTime);
735 }
736 }
737
738 #if 0
739 DpcLastCount = Pcr->PrcbData.DpcLastCount;
740 Pcr->PrcbData.DpcLastCount = Pcr->PrcbData.DpcCount;
741 Pcr->PrcbData.DpcRequestRate = ((Pcr->PrcbData.DpcCount - DpcLastCount) +
742 Pcr->PrcbData.DpcRequestRate) / 2;
743 #endif
744
745 if (Pcr->PrcbData.DpcData[0].DpcQueueDepth > 0 &&
746 Pcr->PrcbData.DpcRoutineActive == FALSE &&
747 Pcr->PrcbData.DpcInterruptRequested == FALSE)
748 {
749 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
750 }
751
752 /* FIXME: Do DPC rate adjustments */
753
754 /*
755 * If we're at end of quantum request software interrupt. The rest
756 * is handled in KiDispatchInterrupt.
757 */
758 if ((CurrentThread->Quantum -= 3) <= 0)
759 {
760 Pcr->PrcbData.QuantumEnd = TRUE;
761 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
762 }
763 }
764
765
766 /*
767 * NOTE: On Windows this function takes exactly zero parameters and EBP is
768 * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL.
769 * The function is used only by HAL, so there's no point in keeping
770 * that prototype.
771 *
772 * @implemented
773 */
774 VOID
775 STDCALL
776 KeUpdateSystemTime(
777 IN PKTRAP_FRAME TrapFrame,
778 IN KIRQL Irql
779 )
780 /*
781 * FUNCTION: Handles a timer interrupt
782 */
783 {
784 LARGE_INTEGER Time;
785
786 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL);
787
788 KiRawTicks++;
789
790 if (TimerInitDone == FALSE)
791 {
792 return;
793 }
794 /*
795 * Increment the number of timers ticks
796 */
797 KeTickCount++;
798 SharedUserData->TickCountLowDeprecated++;
799 KiAcquireSpinLock(&TimerValueLock);
800
801 Time.u.LowPart = SharedUserData->InterruptTime.LowPart;
802 Time.u.HighPart = SharedUserData->InterruptTime.High1Time;
803 Time.QuadPart += CLOCK_INCREMENT;
804 SharedUserData->InterruptTime.High2Time = Time.u.HighPart;
805 SharedUserData->InterruptTime.LowPart = Time.u.LowPart;
806 SharedUserData->InterruptTime.High1Time = Time.u.HighPart;
807
808 Time.u.LowPart = SharedUserData->SystemTime.LowPart;
809 Time.u.HighPart = SharedUserData->SystemTime.High1Time;
810 Time.QuadPart += CLOCK_INCREMENT;
811 SharedUserData->SystemTime.High2Time = Time.u.HighPart;
812 SharedUserData->SystemTime.LowPart = Time.u.LowPart;
813 SharedUserData->SystemTime.High1Time = Time.u.HighPart;
814
815 /* FIXME: Here we should check for remote debugger break-ins */
816
817 KiReleaseSpinLock(&TimerValueLock);
818
819 /* Update process and thread times */
820 KeUpdateRunTime(TrapFrame, Irql);
821
822 /*
823 * Queue a DPC that will expire timers
824 */
825 KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)TrapFrame->Eip, 0);
826 }
827
828 /* EOF */