- Fix compilation with GCC 4.0-20041219.
[reactos.git] / reactos / ntoskrnl / ke / timer.c
1 /* $Id: timer.c,v 1.93 2004/12/24 17:06:58 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
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 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 * FUNCTION: Gets the current system time
208 * ARGUMENTS:
209 * CurrentTime (OUT) = The routine stores the current time here
210 * NOTE: The time is the number of 100-nanosecond intervals since the
211 * 1st of January, 1601.
212 *
213 * @implemented
214 */
215 VOID STDCALL
216 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
217 {
218 do
219 {
220 CurrentTime->u.HighPart = SharedUserData->SystemTime.High1Time;
221 CurrentTime->u.LowPart = SharedUserData->SystemTime.LowPart;
222 }
223 while (CurrentTime->u.HighPart != SharedUserData->SystemTime.High2Time);
224 }
225
226 ULONGLONG STDCALL
227 KeQueryInterruptTime(VOID)
228 {
229 LARGE_INTEGER CurrentTime;
230
231 do
232 {
233 CurrentTime.u.HighPart = SharedUserData->InterruptTime.High1Time;
234 CurrentTime.u.LowPart = SharedUserData->InterruptTime.LowPart;
235 }
236 while (CurrentTime.u.HighPart != SharedUserData->InterruptTime.High2Time);
237
238 return CurrentTime.QuadPart;
239 }
240
241 /*
242 * @implemented
243 */
244 ULONG
245 STDCALL
246 NtGetTickCount(VOID)
247 {
248 LARGE_INTEGER TickCount;
249 KeQueryTickCount(&TickCount);
250 return TickCount.u.LowPart;
251 }
252
253 /*
254 * @implemented
255 */
256 BOOLEAN STDCALL
257 KeSetTimer (PKTIMER Timer,
258 LARGE_INTEGER DueTime,
259 PKDPC Dpc)
260 /*
261 * FUNCTION: Sets the absolute or relative interval at which a timer object
262 * is to be set to the signaled state and optionally supplies a
263 * CustomTimerDpc to be executed when the timer expires.
264 * ARGUMENTS:
265 * Timer = Points to a previously initialized timer object
266 * DueTimer = If positive then absolute time to expire at
267 * If negative then the relative time to expire at
268 * Dpc = If non-NULL then a dpc to be called when the timer expires
269 * RETURNS: True if the timer was already in the system timer queue
270 * False otherwise
271 */
272 {
273 return(KeSetTimerEx(Timer, DueTime, 0, Dpc));
274 }
275
276 /*
277 * @implemented
278 */
279 BOOLEAN STDCALL
280 KeSetTimerEx (PKTIMER Timer,
281 LARGE_INTEGER DueTime,
282 LONG Period,
283 PKDPC Dpc)
284 /*
285 * FUNCTION: Sets the absolute or relative interval at which a timer object
286 * is to be set to the signaled state and optionally supplies a
287 * CustomTimerDpc to be executed when the timer expires.
288 * ARGUMENTS:
289 * Timer = Points to a previously initialized timer object
290 * DueTimer = If positive then absolute time to expire at
291 * If negative then the relative time to expire at
292 * Dpc = If non-NULL then a dpc to be called when the timer expires
293 * RETURNS: True if the timer was already in the system timer queue
294 * False otherwise
295 */
296 {
297 KIRQL oldlvl;
298 LARGE_INTEGER Time;
299 BOOLEAN AlreadyInList;
300
301 DPRINT("KeSetTimerEx(Timer %x), DueTime: \n",Timer);
302
303 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
304
305 KeAcquireSpinLock(&TimerListLock, &oldlvl);
306
307 Timer->Dpc = Dpc;
308 if (DueTime.QuadPart < 0)
309 {
310 Timer->Header.Absolute = 0;
311 Timer->DueTime.QuadPart = KeQueryInterruptTime() - DueTime.QuadPart;
312 }
313 else
314 {
315 KeQuerySystemTime(&Time);
316 Timer->Header.Absolute = 1;
317 if (DueTime.QuadPart >= Time.QuadPart)
318 {
319 Timer->DueTime.QuadPart = DueTime.QuadPart;
320 }
321 else
322 {
323 Timer->DueTime.QuadPart = Time.QuadPart;
324 }
325 }
326 Timer->Period = Period;
327 Timer->Header.SignalState = FALSE;
328 AlreadyInList = (Timer->TimerListEntry.Flink == NULL) ? FALSE : TRUE;
329 ASSERT((Timer->TimerListEntry.Flink == NULL && Timer->TimerListEntry.Blink == NULL) ||
330 (Timer->TimerListEntry.Flink != NULL && Timer->TimerListEntry.Blink != NULL));
331 if (AlreadyInList)
332 {
333 RemoveEntryList(&Timer->TimerListEntry);
334 }
335 if (Timer->Header.Absolute)
336 {
337 InsertAscendingList(&AbsoluteTimerListHead,
338 KTIMER,
339 TimerListEntry,
340 Timer,
341 DueTime.QuadPart);
342
343 }
344 else
345 {
346 InsertAscendingList(&RelativeTimerListHead,
347 KTIMER,
348 TimerListEntry,
349 Timer,
350 DueTime.QuadPart);
351
352 }
353
354 KeReleaseSpinLock(&TimerListLock, oldlvl);
355
356 return AlreadyInList;
357 }
358
359 /*
360 * @implemented
361 */
362 BOOLEAN STDCALL
363 KeCancelTimer (PKTIMER Timer)
364 /*
365 * FUNCTION: Removes a timer from the system timer list
366 * ARGUMENTS:
367 * Timer = timer to cancel
368 * RETURNS: True if the timer was running
369 * False otherwise
370 */
371 {
372 KIRQL oldlvl;
373
374 DPRINT("KeCancelTimer(Timer %x)\n",Timer);
375
376 KeAcquireSpinLock(&TimerListLock, &oldlvl);
377
378 if (Timer->TimerListEntry.Flink == NULL)
379 {
380 KeReleaseSpinLock(&TimerListLock, oldlvl);
381 return(FALSE);
382 }
383 if (Timer->Header.Absolute)
384 {
385 ASSERT(&Timer->TimerListEntry != &AbsoluteTimerListHead);
386 }
387 else
388 {
389 ASSERT(&Timer->TimerListEntry != &RelativeTimerListHead);
390 }
391 ASSERT(Timer->TimerListEntry.Flink != &Timer->TimerListEntry);
392 RemoveEntryList(&Timer->TimerListEntry);
393 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
394 KeReleaseSpinLock(&TimerListLock, oldlvl);
395
396 return(TRUE);
397 }
398
399 /*
400 * @implemented
401 */
402 BOOLEAN STDCALL
403 KeReadStateTimer (PKTIMER Timer)
404 {
405 return (BOOLEAN)(Timer->Header.SignalState);
406 }
407
408 /*
409 * @implemented
410 */
411 VOID STDCALL
412 KeInitializeTimer (PKTIMER Timer)
413 /*
414 * FUNCTION: Initalizes a kernel timer object
415 * ARGUMENTS:
416 * Timer = caller supplied storage for the timer
417 * NOTE: This function initializes a notification timer
418 */
419 {
420 KeInitializeTimerEx(Timer, NotificationTimer);
421 }
422
423 /*
424 * @implemented
425 */
426 VOID STDCALL
427 KeInitializeTimerEx (PKTIMER Timer,
428 TIMER_TYPE Type)
429 /*
430 * FUNCTION: Initializes a kernel timer object
431 * ARGUMENTS:
432 * Timer = caller supplied storage for the timer
433 * Type = the type of timer (notification or synchronization)
434 * NOTE: When a notification type expires all waiting threads are released
435 * and the timer remains signalled until it is explicitly reset. When a
436 * syncrhonization timer expires its state is set to signalled until a
437 * single waiting thread is released and then the timer is reset.
438 */
439 {
440 ULONG IType;
441
442 if (Type == NotificationTimer)
443 {
444 IType = InternalNotificationTimer;
445 }
446 else if (Type == SynchronizationTimer)
447 {
448 IType = InternalSynchronizationTimer;
449 }
450 else
451 {
452 ASSERT(FALSE);
453 return;
454 }
455
456 KeInitializeDispatcherHeader(&Timer->Header,
457 IType,
458 sizeof(KTIMER) / sizeof(ULONG),
459 FALSE);
460 Timer->TimerListEntry.Flink = Timer->TimerListEntry.Blink = NULL;
461 }
462
463 /*
464 * @implemented
465 */
466 VOID STDCALL
467 KeQueryTickCount(PLARGE_INTEGER TickCount)
468 /*
469 * FUNCTION: Returns the number of ticks since the system was booted
470 * ARGUMENTS:
471 * TickCount (OUT) = Points to storage for the number of ticks
472 */
473 {
474 TickCount->QuadPart = KeTickCount;
475 }
476
477 /*
478 * @implemented
479 */
480 ULONG
481 STDCALL
482 KeQueryRuntimeThread(
483 IN PKTHREAD Thread,
484 OUT PULONG UserTime
485 )
486 {
487 /* Return the User Time */
488 *UserTime = Thread->UserTime;
489
490 /* Return the Kernel Time */
491 return Thread->KernelTime;
492 }
493
494 /*
495 * @implemented
496 */
497 VOID
498 STDCALL
499 KeSetTimeIncrement(
500 IN ULONG MaxIncrement,
501 IN ULONG MinIncrement
502 )
503 {
504 /* Set some Internal Variables */
505 /* FIXME: We use a harcoded CLOCK_INCREMENT. That *must* be changed */
506 KeMaximumIncrement = MaxIncrement;
507 KeMinimumIncrement = MinIncrement;
508 }
509
510 /*
511 * We enter this function at IRQL DISPATCH_LEVEL, and with the
512 * TimerListLock held.
513 */
514 STATIC VOID
515 HandleExpiredTimer(PKTIMER Timer)
516 {
517 DPRINT("HandleExpiredTime(Timer %x)\n", Timer);
518 if (Timer->Dpc != NULL)
519 {
520 DPRINT("Timer->Dpc %x Timer->Dpc->DeferredRoutine %x\n",
521 Timer->Dpc, Timer->Dpc->DeferredRoutine);
522 KeInsertQueueDpc(Timer->Dpc,
523 NULL,
524 NULL);
525 DPRINT("Finished dpc routine\n");
526 }
527
528 ASSERT_IRQL_EQUAL(DISPATCH_LEVEL);
529
530 KeAcquireDispatcherDatabaseLockAtDpcLevel();
531 Timer->Header.SignalState = TRUE;
532 KiDispatcherObjectWake(&Timer->Header);
533 KeReleaseDispatcherDatabaseLockFromDpcLevel();
534
535 if (Timer->Period != 0)
536 {
537 Timer->DueTime.QuadPart +=
538 Timer->Period * SYSTEM_TIME_UNITS_PER_MSEC;
539 if (Timer->Header.Absolute)
540 {
541 InsertAscendingList(&AbsoluteTimerListHead,
542 KTIMER,
543 TimerListEntry,
544 Timer,
545 DueTime.QuadPart);
546 }
547 else
548 {
549 InsertAscendingList(&RelativeTimerListHead,
550 KTIMER,
551 TimerListEntry,
552 Timer,
553 DueTime.QuadPart);
554 }
555 }
556 }
557
558 VOID STDCALL
559 KeExpireTimers(PKDPC Dpc,
560 PVOID Context1,
561 PVOID Arg1,
562 PVOID Arg2)
563 {
564 PLIST_ENTRY current_entry = NULL;
565 PKTIMER current = NULL;
566 ULONG Eip = (ULONG)Arg1;
567 LARGE_INTEGER InterruptTime;
568 LARGE_INTEGER SystemTime;
569 LIST_ENTRY TimerList;
570
571 DPRINT("KeExpireTimers()\n");
572
573 ASSERT_IRQL_EQUAL(DISPATCH_LEVEL);
574
575 InitializeListHead(&TimerList);
576
577 KeAcquireSpinLockAtDpcLevel(&TimerListLock);
578
579 InterruptTime.QuadPart = KeQueryInterruptTime();
580 KeQuerySystemTime(&SystemTime);
581
582 current_entry = RelativeTimerListHead.Flink;
583 ASSERT(current_entry);
584 while (current_entry != &RelativeTimerListHead)
585 {
586 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
587 ASSERT(current);
588 ASSERT(current_entry != &RelativeTimerListHead);
589 ASSERT(current_entry->Flink != current_entry);
590 if ((ULONGLONG)InterruptTime.QuadPart < current->DueTime.QuadPart)
591 {
592 break;
593 }
594 current_entry = current_entry->Flink;
595 RemoveEntryList(&current->TimerListEntry);
596 InsertTailList(&TimerList, &current->TimerListEntry);
597 }
598
599 current_entry = AbsoluteTimerListHead.Flink;
600 ASSERT(current_entry);
601 while (current_entry != &AbsoluteTimerListHead)
602 {
603 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
604 ASSERT(current);
605 ASSERT(current_entry != &AbsoluteTimerListHead);
606 ASSERT(current_entry->Flink != current_entry);
607 if ((ULONGLONG)SystemTime.QuadPart < current->DueTime.QuadPart)
608 {
609 break;
610 }
611 current_entry = current_entry->Flink;
612 RemoveEntryList(&current->TimerListEntry);
613 InsertTailList(&TimerList, &current->TimerListEntry);
614 }
615
616 while (!IsListEmpty(&TimerList))
617 {
618 current_entry = RemoveHeadList(&TimerList);
619 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
620 current->TimerListEntry.Flink = current->TimerListEntry.Blink = NULL;
621 HandleExpiredTimer(current);
622 }
623
624 KiAddProfileEvent(ProfileTime, Eip);
625
626 KeReleaseSpinLockFromDpcLevel(&TimerListLock);
627 }
628
629
630 VOID INIT_FUNCTION
631 KeInitializeTimerImpl(VOID)
632 /*
633 * FUNCTION: Initializes timer irq handling
634 * NOTE: This is only called once from main()
635 */
636 {
637 TIME_FIELDS TimeFields;
638
639 DPRINT("KeInitializeTimerImpl()\n");
640 InitializeListHead(&AbsoluteTimerListHead);
641 InitializeListHead(&RelativeTimerListHead);
642 KeInitializeSpinLock(&TimerListLock);
643 KeInitializeDpc(&ExpireTimerDpc, KeExpireTimers, 0);
644 /*
645 * Calculate the starting time for the system clock
646 */
647 HalQueryRealTimeClock(&TimeFields);
648 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
649
650 SharedUserData->TickCountLowDeprecated = 0;
651 SharedUserData->TickCountMultiplier = 167783691; // 2^24 * 1193182 / 119310
652 SharedUserData->InterruptTime.High2Time = 0;
653 SharedUserData->InterruptTime.LowPart = 0;
654 SharedUserData->InterruptTime.High1Time = 0;
655 SharedUserData->SystemTime.High2Time = SystemBootTime.u.HighPart;
656 SharedUserData->SystemTime.LowPart = SystemBootTime.u.LowPart;
657 SharedUserData->SystemTime.High1Time = SystemBootTime.u.HighPart;
658
659 TimerInitDone = TRUE;
660 DPRINT("Finished KeInitializeTimerImpl()\n");
661 }
662
663 /*
664 * @unimplemented
665 */
666 VOID
667 FASTCALL
668 KeSetTimeUpdateNotifyRoutine(
669 IN PTIME_UPDATE_NOTIFY_ROUTINE NotifyRoutine
670 )
671 {
672 UNIMPLEMENTED;
673 }
674
675
676 /*
677 * NOTE: On Windows this function takes exactly one parameter and EBP is
678 * guaranteed to point to KTRAP_FRAME. The function is used only
679 * by HAL, so there's no point in keeping that prototype.
680 *
681 * @implemented
682 */
683 VOID
684 STDCALL
685 KeUpdateRunTime(
686 IN PKTRAP_FRAME TrapFrame,
687 IN KIRQL Irql
688 )
689 {
690 PKPCR Pcr;
691 PKTHREAD CurrentThread;
692 PKPROCESS CurrentProcess;
693 #if 0
694 ULONG DpcLastCount;
695 #endif
696
697 Pcr = KeGetCurrentKPCR();
698
699 /* Make sure we don't go further if we're in early boot phase. */
700 if (Pcr == NULL || Pcr->PrcbData.CurrentThread == NULL)
701 return;
702
703 DPRINT("KernelTime %u, UserTime %u \n", Pcr->PrcbData.KernelTime, Pcr->PrcbData.UserTime);
704
705 CurrentThread = Pcr->PrcbData.CurrentThread;
706 CurrentProcess = CurrentThread->ApcState.Process;
707
708 /*
709 * Cs bit 0 is always set for user mode if we are in protected mode.
710 * V86 mode is counted as user time.
711 */
712 if (TrapFrame->Cs & 0x1 ||
713 TrapFrame->Eflags & X86_EFLAGS_VM)
714 {
715 InterlockedIncrementUL(&CurrentThread->UserTime);
716 InterlockedIncrementUL(&CurrentProcess->UserTime);
717 Pcr->PrcbData.UserTime++;
718 }
719 else
720 {
721 if (Irql > DISPATCH_LEVEL)
722 {
723 Pcr->PrcbData.InterruptTime++;
724 }
725 else if (Irql == DISPATCH_LEVEL)
726 {
727 Pcr->PrcbData.DpcTime++;
728 }
729 else
730 {
731 InterlockedIncrementUL(&CurrentThread->KernelTime);
732 InterlockedIncrementUL(&CurrentProcess->KernelTime);
733 Pcr->PrcbData.KernelTime++;
734 }
735 }
736
737 #if 0
738 DpcLastCount = Pcr->PrcbData.DpcLastCount;
739 Pcr->PrcbData.DpcLastCount = Pcr->PrcbData.DpcCount;
740 Pcr->PrcbData.DpcRequestRate = ((Pcr->PrcbData.DpcCount - DpcLastCount) +
741 Pcr->PrcbData.DpcRequestRate) / 2;
742 #endif
743
744 if (Pcr->PrcbData.DpcData[0].DpcQueueDepth > 0 &&
745 Pcr->PrcbData.DpcRoutineActive == FALSE &&
746 Pcr->PrcbData.DpcInterruptRequested == FALSE)
747 {
748 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
749 }
750
751 /* FIXME: Do DPC rate adjustments */
752
753 /*
754 * If we're at end of quantum request software interrupt. The rest
755 * is handled in KiDispatchInterrupt.
756 */
757 if ((CurrentThread->Quantum -= 3) <= 0)
758 {
759 Pcr->PrcbData.QuantumEnd = TRUE;
760 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
761 }
762 }
763
764
765 /*
766 * NOTE: On Windows this function takes exactly zero parameters and EBP is
767 * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL.
768 * The function is used only by HAL, so there's no point in keeping
769 * that prototype.
770 *
771 * @implemented
772 */
773 VOID
774 STDCALL
775 KeUpdateSystemTime(
776 IN PKTRAP_FRAME TrapFrame,
777 IN KIRQL Irql
778 )
779 /*
780 * FUNCTION: Handles a timer interrupt
781 */
782 {
783 LARGE_INTEGER Time;
784
785 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL);
786
787 KiRawTicks++;
788
789 if (TimerInitDone == FALSE)
790 {
791 return;
792 }
793 /*
794 * Increment the number of timers ticks
795 */
796 KeTickCount++;
797 SharedUserData->TickCountLowDeprecated++;
798
799 Time.u.LowPart = SharedUserData->InterruptTime.LowPart;
800 Time.u.HighPart = SharedUserData->InterruptTime.High1Time;
801 Time.QuadPart += CLOCK_INCREMENT;
802 SharedUserData->InterruptTime.High2Time = Time.u.HighPart;
803 SharedUserData->InterruptTime.LowPart = Time.u.LowPart;
804 SharedUserData->InterruptTime.High1Time = Time.u.HighPart;
805
806 Time.u.LowPart = SharedUserData->SystemTime.LowPart;
807 Time.u.HighPart = SharedUserData->SystemTime.High1Time;
808 Time.QuadPart += CLOCK_INCREMENT;
809 SharedUserData->SystemTime.High2Time = Time.u.HighPart;
810 SharedUserData->SystemTime.LowPart = Time.u.LowPart;
811 SharedUserData->SystemTime.High1Time = Time.u.HighPart;
812
813 /* FIXME: Here we should check for remote debugger break-ins */
814
815 /* Update process and thread times */
816 KeUpdateRunTime(TrapFrame, Irql);
817
818 /*
819 * Queue a DPC that will expire timers
820 */
821 KeInsertQueueDpc(&ExpireTimerDpc, (PVOID)TrapFrame->Eip, 0);
822 }
823
824
825 VOID
826 KiSetSystemTime(PLARGE_INTEGER NewSystemTime)
827 {
828 LARGE_INTEGER OldSystemTime;
829 LARGE_INTEGER DeltaTime;
830 KIRQL OldIrql;
831 PLIST_ENTRY current_entry = NULL;
832 PKTIMER current = NULL;
833
834 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
835
836 OldIrql = KeAcquireDispatcherDatabaseLock();
837
838 do
839 {
840 OldSystemTime.u.HighPart = SharedUserData->SystemTime.High1Time;
841 OldSystemTime.u.LowPart = SharedUserData->SystemTime.LowPart;
842 }
843 while (OldSystemTime.u.HighPart != SharedUserData->SystemTime.High2Time);
844
845 /* Set the new system time */
846 SharedUserData->SystemTime.LowPart = NewSystemTime->u.LowPart;
847 SharedUserData->SystemTime.High1Time = NewSystemTime->u.HighPart;
848 SharedUserData->SystemTime.High2Time = NewSystemTime->u.HighPart;
849
850 /* Calculate the difference between the new and the old time */
851 DeltaTime.QuadPart = NewSystemTime->QuadPart - OldSystemTime.QuadPart;
852
853 /* Update system boot time */
854 SystemBootTime.QuadPart += DeltaTime.QuadPart;
855
856 /* Update all relative timers */
857 current_entry = RelativeTimerListHead.Flink;
858 ASSERT(current_entry);
859 while (current_entry != &RelativeTimerListHead)
860 {
861 current = CONTAINING_RECORD(current_entry, KTIMER, TimerListEntry);
862 ASSERT(current);
863 ASSERT(current_entry != &RelativeTimerListHead);
864 ASSERT(current_entry->Flink != current_entry);
865
866 current->DueTime.QuadPart += DeltaTime.QuadPart;
867
868 current_entry = current_entry->Flink;
869 }
870
871 KeReleaseDispatcherDatabaseLock(OldIrql);
872
873 /*
874 * NOTE: Expired timers will be processed at the next clock tick!
875 */
876 }
877
878 /* EOF */