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