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