d80073b8b7693a1f66614cd71dbfed62c3387d93
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /* $Id: timer.c,v 1.87 2004/10/31 21:22:06 navaraf 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 #define NDEBUG
22 #include <internal/debug.h>
23
24
25 /* GLOBALS ****************************************************************/
26
27 /*
28 * Current time
29 */
30 #if defined(__GNUC__)
31 LARGE_INTEGER SystemBootTime = (LARGE_INTEGER)0LL;
32 #else
33 LARGE_INTEGER SystemBootTime = { 0 };
34 #endif
35
36 CHAR KiTimerSystemAuditing = 0;
37
38 /*
39 * Number of timer interrupts since initialisation
40 */
41 volatile ULONGLONG KeTickCount = 0;
42 volatile ULONG KiRawTicks = 0;
43
44 /*
45 * The increment in the system clock every timer tick (in system time units)
46 *
47 * = (1/18.2)*10^9
48 *
49 * RJJ was 54945055
50 */
51 #define CLOCK_INCREMENT (100000)
52
53 #ifdef __GNUC__
54 ULONG EXPORTED KeMaximumIncrement = 100000;
55 ULONG EXPORTED KeMinimumIncrement = 100000;
56 #else
57 /* Microsoft-style declarations */
58 EXPORTED ULONG KeMaximumIncrement = 100000;
59 EXPORTED ULONG KeMinimumIncrement = 100000;
60 #endif
61
62
63
64 /*
65 * PURPOSE: List of timers
66 */
67 static LIST_ENTRY AbsoluteTimerListHead;
68 static LIST_ENTRY RelativeTimerListHead;
69 static KSPIN_LOCK TimerListLock;
70 static KSPIN_LOCK TimerValueLock;
71 static KDPC ExpireTimerDpc;
72
73 /* must raise IRQL to PROFILE_LEVEL and grab spin lock there, to sync with ISR */
74
75 extern HANDLE PsIdleThreadHandle;
76
77 #define MICROSECONDS_PER_TICK (10000)
78 #define TICKS_TO_CALIBRATE (1)
79 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
80 #define SYSTEM_TIME_UNITS_PER_MSEC (10000)
81
82 static BOOLEAN TimerInitDone = FALSE;
83
84 /* FUNCTIONS **************************************************************/
85
86
87 NTSTATUS STDCALL
88 NtQueryTimerResolution(OUT PULONG MinimumResolution,
89 OUT PULONG MaximumResolution,
90 OUT PULONG ActualResolution)
91 {
92 UNIMPLEMENTED;
93 return STATUS_NOT_IMPLEMENTED;
94 }
95
96
97 NTSTATUS STDCALL
98 NtSetTimerResolution(IN ULONG DesiredResolution,
99 IN BOOLEAN SetResolution,
100 OUT PULONG CurrentResolution)
101 {
102 UNIMPLEMENTED;
103 return STATUS_NOT_IMPLEMENTED;
104 }
105
106
107 NTSTATUS STDCALL
108 NtQueryPerformanceCounter(IN PLARGE_INTEGER Counter,
109 IN PLARGE_INTEGER Frequency)
110 {
111 LARGE_INTEGER PerfCounter;
112 LARGE_INTEGER PerfFrequency;
113 NTSTATUS Status;
114
115 PerfCounter = KeQueryPerformanceCounter(&PerfFrequency);
116
117 if (Counter != NULL)
118 {
119 Status = MmCopyToCaller(&Counter->QuadPart, &PerfCounter.QuadPart, sizeof(PerfCounter.QuadPart));
120 if (!NT_SUCCESS(Status))
121 {
122 return(Status);
123 }
124 }
125
126 if (Frequency != NULL)
127 {
128 Status = MmCopyToCaller(&Frequency->QuadPart, &PerfFrequency.QuadPart, sizeof(PerfFrequency.QuadPart));
129 if (!NT_SUCCESS(Status))
130 {
131 return(Status);
132 }
133 }
134
135 return(STATUS_SUCCESS);
136 }
137
138
139 NTSTATUS STDCALL
140 NtDelayExecution(IN ULONG Alertable,
141 IN TIME* Interval)
142 {
143 NTSTATUS Status;
144 LARGE_INTEGER Timeout;
145
146 Status = MmCopyFromCaller(&Timeout, Interval, sizeof(Timeout));
147 if (!NT_SUCCESS(Status))
148 {
149 return(Status);
150 }
151
152 Timeout = *((PLARGE_INTEGER)Interval);
153 DPRINT("NtDelayExecution(Alertable %d, Internal %x) IntervalP %x\n",
154 Alertable, Internal, Timeout);
155
156 DPRINT("Execution delay is %d/%d\n",
157 Timeout.u.HighPart, Timeout.u.LowPart);
158 Status = KeDelayExecutionThread(UserMode, (BOOLEAN)Alertable, &Timeout);
159 return(Status);
160 }
161
162
163 /*
164 * @implemented
165 */
166 NTSTATUS STDCALL
167 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
168 BOOLEAN Alertable,
169 PLARGE_INTEGER Interval)
170 /*
171 * FUNCTION: Puts the current thread into an alertable or nonalertable
172 * wait state for a given internal
173 * ARGUMENTS:
174 * WaitMode = Processor mode in which the caller is waiting
175 * Altertable = Specifies if the wait is alertable
176 * Interval = Specifies the interval to wait
177 * RETURNS: Status
178 */
179 {
180 PKTHREAD Thread = KeGetCurrentThread();
181
182 KeSetTimer(&Thread->Timer, *Interval, NULL);
183 return (KeWaitForSingleObject(&Thread->Timer,
184 (WaitMode == KernelMode) ? Executive : UserRequest, /* TMN: Was unconditionally Executive */
185 WaitMode, /* TMN: Was UserMode */
186 Alertable,
187 NULL));
188 }
189
190
191 /*
192 * @implemented
193 */
194 ULONG STDCALL
195 KeQueryTimeIncrement(VOID)
196 /*
197 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
198 * the system clock every time the clock interrupts
199 * RETURNS: The increment
200 */
201 {
202 return(CLOCK_INCREMENT);
203 }
204
205
206 /*
207 * @implemented
208 */
209 VOID STDCALL
210 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
211 /*
212 * FUNCTION: Gets the current system time
213 * ARGUMENTS:
214 * CurrentTime (OUT) = The routine stores the current time here
215 * NOTE: The time is the number of 100-nanosecond intervals since the
216 * 1st of January, 1601.
217 */
218 {
219 do
220 {
221 CurrentTime->u.HighPart = SharedUserData->SystemTime.High1Part;
222 CurrentTime->u.LowPart = SharedUserData->SystemTime.LowPart;
223 }
224 while (CurrentTime->u.HighPart != SharedUserData->SystemTime.High2Part);
225 }
226
227 ULONGLONG STDCALL
228 KeQueryInterruptTime(VOID)
229 {
230 LARGE_INTEGER CurrentTime;
231
232 do
233 {
234 CurrentTime.u.HighPart = SharedUserData->InterruptTime.High1Part;
235 CurrentTime.u.LowPart = SharedUserData->InterruptTime.LowPart;
236 }
237 while (CurrentTime.u.HighPart != SharedUserData->InterruptTime.High2Part);
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(KeGetCurrentIrql() == DISPATCH_LEVEL);
530
531 KeAcquireDispatcherDatabaseLockAtDpcLevel();
532 Timer->Header.SignalState = TRUE;
533 KeDispatcherObjectWake(&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(KeGetCurrentIrql() == 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->TickCountLow = 0;
653 SharedUserData->TickCountMultiplier = 167783691; // 2^24 * 1193182 / 119310
654 SharedUserData->InterruptTime.High2Part = 0;
655 SharedUserData->InterruptTime.LowPart = 0;
656 SharedUserData->InterruptTime.High1Part = 0;
657 SharedUserData->SystemTime.High2Part = SystemBootTime.u.HighPart;
658 SharedUserData->SystemTime.LowPart = SystemBootTime.u.LowPart;
659 SharedUserData->SystemTime.High1Part = 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->TickCountLow++;
799 KiAcquireSpinLock(&TimerValueLock);
800
801 Time.u.LowPart = SharedUserData->InterruptTime.LowPart;
802 Time.u.HighPart = SharedUserData->InterruptTime.High1Part;
803 Time.QuadPart += CLOCK_INCREMENT;
804 SharedUserData->InterruptTime.High2Part = Time.u.HighPart;
805 SharedUserData->InterruptTime.LowPart = Time.u.LowPart;
806 SharedUserData->InterruptTime.High1Part = Time.u.HighPart;
807
808 Time.u.LowPart = SharedUserData->SystemTime.LowPart;
809 Time.u.HighPart = SharedUserData->SystemTime.High1Part;
810 Time.QuadPart += CLOCK_INCREMENT;
811 SharedUserData->SystemTime.High2Part = Time.u.HighPart;
812 SharedUserData->SystemTime.LowPart = Time.u.LowPart;
813 SharedUserData->SystemTime.High1Part = 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 */